11import type { ReferenceElement } from "@floating-ui/dom" ;
2- import type { ContextData , OpenChangeReason , ReferenceType } from "../types.js" ;
2+ import type {
3+ ContextData ,
4+ MaybeGetter ,
5+ OpenChangeReason ,
6+ ReferenceType ,
7+ } from "../types.js" ;
38import { useId } from "./use-id.js" ;
49import { createPubSub } from "../internal/create-pub-sub.js" ;
510import { useFloatingParentNodeId } from "../components/floating-tree/hooks.svelte.js" ;
611import { DEV } from "esm-env" ;
712import { isElement } from "@floating-ui/utils/dom" ;
813import { error } from "../internal/log.js" ;
14+ import {
15+ box ,
16+ type ReadableBox ,
17+ type WritableBox ,
18+ } from "../internal/box.svelte.js" ;
19+ import { extract } from "../internal/extract.js" ;
20+ import { noop } from "../internal/noop.js" ;
921
1022interface UseFloatingRootContextOptions {
11- open ?: boolean ;
23+ open ?: MaybeGetter < boolean > ;
1224 onOpenChange ?: (
1325 open : boolean ,
1426 event ?: Event ,
1527 reason ?: OpenChangeReason ,
1628 ) => void ;
17- elements : {
18- reference : Element | null ;
19- floating : HTMLElement | null ;
20- } ;
29+ reference : MaybeGetter < Element | null > ;
30+ floating : MaybeGetter < HTMLElement | null > ;
31+ onReferenceChange ?: ( node : Element | null ) => void ;
32+ onFloatingChange ?: ( node : HTMLElement | null ) => void ;
33+ }
34+
35+ class FloatingRootContextOptions {
36+ open : ReadableBox < boolean > ;
37+ onOpenChange : (
38+ open : boolean ,
39+ event ?: Event ,
40+ reason ?: OpenChangeReason ,
41+ ) => void ;
42+ onReferenceChange : ( node : Element | null ) => void ;
43+ onFloatingChange : ( node : HTMLElement | null ) => void ;
44+ #stableReference = $state < Element | null > ( null ) ;
45+ #stableFloating = $state < HTMLElement | null > ( null ) ;
46+ reference : WritableBox < Element | null > ;
47+ floating : WritableBox < HTMLElement | null > ;
48+
49+ constructor ( options : UseFloatingRootContextOptions ) {
50+ const floatingProp = $derived . by ( ( ) => extract ( options . floating , null ) ) ;
51+ const referenceProp = $derived . by ( ( ) => extract ( options . reference , null ) ) ;
52+ this . open = box . with ( ( ) => extract ( options . open , false ) ) ;
53+ this . onOpenChange = options . onOpenChange ?? noop ;
54+ this . onReferenceChange = options . onReferenceChange ?? noop ;
55+ this . onFloatingChange = options . onFloatingChange ?? noop ;
56+ this . reference = box . with (
57+ ( ) => this . #stableReference,
58+ ( node ) => {
59+ this . #stableReference = node ;
60+ this . onReferenceChange ( node as Element | null ) ;
61+ } ,
62+ ) ;
63+ this . floating = box . with (
64+ ( ) => this . #stableFloating,
65+ ( node ) => {
66+ this . #stableFloating = node ;
67+ this . onFloatingChange ( node ) ;
68+ } ,
69+ ) ;
70+
71+ this . reference . current = referenceProp ;
72+ this . floating . current = floatingProp ;
73+
74+ $effect . pre ( ( ) => {
75+ this . reference . current = referenceProp ;
76+ } ) ;
77+
78+ $effect . pre ( ( ) => {
79+ this . floating . current = floatingProp ;
80+ } ) ;
81+ }
2182}
2283
2384class FloatingRootContext < RT extends ReferenceType = ReferenceType > {
2485 floatingId = useId ( ) ;
2586 data : ContextData < RT > = $state ( { } ) ;
2687 events = createPubSub ( ) ;
27- open = $derived . by ( ( ) => this . options . open ?? false ) ;
28-
88+ open = $derived . by ( ( ) => this . options . open . current ) ;
2989 /** Whether the floating element is nested inside another floating element. */
3090 #nested: boolean ;
3191 /** Enables the user to specify a position reference after initialization. */
3292 #positionReference = $state < ReferenceElement | null > ( null ) ;
33- #referenceElement = $state < Element | null > ( null ) ;
34- #floatingElement = $state < HTMLElement | null > ( null ) ;
35-
36- #elements = $derived . by ( ( ) => ( {
37- reference : ( this . #positionReference ||
38- this . #referenceElement ||
39- null ) as RT | null ,
40- floating : this . #floatingElement || null ,
41- domReference : this . #referenceElement as Element | null ,
42- } ) ) ;
93+ reference = $derived . by (
94+ ( ) =>
95+ ( this . #positionReference ||
96+ this . options . reference . current ||
97+ null ) as RT | null ,
98+ ) ;
99+ floating = $derived . by ( ( ) => this . options . floating . current ) ;
100+ domReference = $derived . by ( ( ) => this . options . reference . current ) ;
43101
44- constructor ( private readonly options : UseFloatingRootContextOptions ) {
102+ constructor ( private readonly options : FloatingRootContextOptions ) {
45103 this . #nested = useFloatingParentNodeId ( ) != null ;
46104
47- this . #referenceElement = this . options . elements . reference ;
48- this . #floatingElement = this . options . elements . floating ;
49-
50- $effect . pre ( ( ) => {
51- this . #referenceElement = this . options . elements . reference ;
52- } ) ;
53-
54- $effect . pre ( ( ) => {
55- this . #floatingElement = this . options . elements . floating ;
56- } ) ;
57-
58105 if ( DEV ) {
59- if (
60- options . elements . reference &&
61- ! isElement ( options . elements . reference )
62- ) {
106+ if ( options . reference . current && ! isElement ( options . reference . current ) ) {
63107 error (
64108 "Cannot pass a virtual element to the `elements.reference` option," ,
65109 "as it must be a real DOM element. Use `floating.setPositionReference()`" ,
66110 "instead." ,
67111 ) ;
68112 }
69113 }
70- this . #positionReference = options . elements . reference ;
114+ this . #positionReference = this . options . reference . current ;
71115 this . onOpenChange = this . onOpenChange . bind ( this ) ;
72- this . setPositionReference = this . setPositionReference . bind ( this ) ;
73116 }
74117
75118 onOpenChange ( open : boolean , event ?: Event , reason ?: OpenChangeReason ) {
@@ -86,32 +129,11 @@ class FloatingRootContext<RT extends ReferenceType = ReferenceType> {
86129 setPositionReference ( node : ReferenceElement | null ) {
87130 this . #positionReference = node ;
88131 }
89-
90- setFloatingElement ( node : HTMLElement | null ) {
91- this . #floatingElement = node ;
92- }
93-
94- get elements ( ) {
95- const _this = this ;
96- return {
97- get reference ( ) {
98- return _this . #elements. reference ;
99- } ,
100- get floating ( ) {
101- return _this . #elements. floating ;
102- } ,
103- set floating ( node : HTMLElement | null ) {
104- _this . setFloatingElement ( node ) ;
105- } ,
106- get domReference ( ) {
107- return _this . #referenceElement;
108- } ,
109- } ;
110- }
111132}
112133
113134export function useFloatingRootContext ( options : UseFloatingRootContextOptions ) {
114- return new FloatingRootContext ( options ) ;
135+ const optionsState = new FloatingRootContextOptions ( options ) ;
136+ return new FloatingRootContext ( optionsState ) ;
115137}
116138
117139export type { UseFloatingRootContextOptions } ;
0 commit comments