1
1
import type { ReferenceElement } from "@floating-ui/dom" ;
2
2
import type {
3
3
ContextData ,
4
- FloatingEvents ,
4
+ OnOpenChange ,
5
5
OpenChangeReason ,
6
6
ReferenceType ,
7
7
} from "../types.js" ;
@@ -11,7 +11,6 @@ import { useFloatingParentNodeId } from "../components/floating-tree/hooks.svelt
11
11
import { DEV } from "esm-env" ;
12
12
import { isElement } from "@floating-ui/utils/dom" ;
13
13
import { error } from "../internal/log.js" ;
14
- import { noop } from "../internal/noop.js" ;
15
14
16
15
interface UseFloatingRootContextOptions {
17
16
open ?: boolean ;
@@ -26,123 +25,93 @@ interface UseFloatingRootContextOptions {
26
25
} ;
27
26
}
28
27
29
- interface FloatingRootContext < RT extends ReferenceType = ReferenceType > {
30
- data : ContextData ;
31
- open : boolean ;
32
- onOpenChange : (
33
- open : boolean ,
34
- event ?: Event ,
35
- reason ?: OpenChangeReason ,
36
- ) => void ;
37
- elements : {
38
- domReference : Element | null ;
39
- reference : RT | null ;
40
- floating : HTMLElement | null ;
41
- } ;
42
- events : FloatingEvents ;
43
- floatingId : string | undefined ;
44
- refs : {
45
- setPositionReference ( node : ReferenceType | null ) : void ;
46
- } ;
47
- }
28
+ class FloatingRootContext < RT extends ReferenceType = ReferenceType > {
29
+ floatingId = useId ( ) ;
30
+ data : ContextData < RT > = $state ( { } ) ;
31
+ events = createPubSub ( ) ;
32
+ open = $derived . by ( ( ) => this . options . open ?? false ) ;
48
33
49
- /**
50
- * Creates a floating root context to manage the state of a floating element.
51
- */
52
- function useFloatingRootContext (
53
- options : UseFloatingRootContextOptions ,
54
- ) : FloatingRootContext {
55
- const elementsProp : {
56
- reference : ReferenceType | null ;
57
- floating : HTMLElement | null ;
58
- } = $state ( options . elements ) ;
34
+ /** Whether the floating element is nested inside another floating element. */
35
+ #nested: boolean ;
36
+ /** Enables the user to specify a position reference after initialization. */
37
+ #positionReference = $state < ReferenceElement | null > ( null ) ;
38
+ #referenceElement = $state < Element | null > ( null ) ;
39
+ #floatingElement = $state < HTMLElement | null > ( null ) ;
59
40
60
- $effect . pre ( ( ) => {
61
- if ( ! options . elements || ! options . elements . reference ) {
62
- return ;
63
- }
64
- elementsProp . reference = options . elements . reference ;
65
- } ) ;
41
+ #elements = $derived . by ( ( ) => ( {
42
+ reference : ( this . #positionReference ||
43
+ this . #referenceElement ||
44
+ null ) as RT | null ,
45
+ floating : this . #floatingElement || null ,
46
+ domReference : this . #referenceElement as Element | null ,
47
+ } ) ) ;
66
48
67
- $effect . pre ( ( ) => {
68
- if ( ! options . elements || ! options . elements . floating ) {
69
- return ;
70
- }
71
- elementsProp . floating = options . elements . floating ;
72
- } ) ;
49
+ constructor ( private readonly options : UseFloatingRootContextOptions ) {
50
+ this . #nested = useFloatingParentNodeId ( ) != null ;
73
51
74
- const { open = false , onOpenChange : onOpenChangeProp = noop } = options ;
52
+ this . #referenceElement = this . options . elements . reference ;
53
+ this . #floatingElement = this . options . elements . floating ;
75
54
76
- const floatingId = useId ( ) ;
77
- const data = $state < ContextData > ( { } ) ;
78
- const events = createPubSub ( ) ;
79
- const nested = useFloatingParentNodeId ( ) != null ;
55
+ $effect . pre ( ( ) => {
56
+ this . #referenceElement = this . options . elements . reference ;
57
+ } ) ;
80
58
81
- if ( DEV ) {
82
- const optionDomReference = elementsProp . reference ;
83
- if ( optionDomReference && ! isElement ( optionDomReference ) ) {
84
- error (
85
- "Cannot pass a virtual element to the `elements.reference` option," ,
86
- "as it must be a real DOM element. Use `refs.setPositionReference()`" ,
87
- "instead." ,
88
- ) ;
59
+ $effect . pre ( ( ) => {
60
+ this . #floatingElement = this . options . elements . floating ;
61
+ } ) ;
62
+
63
+ if ( DEV ) {
64
+ if (
65
+ options . elements . reference &&
66
+ ! isElement ( options . elements . reference )
67
+ ) {
68
+ error (
69
+ "Cannot pass a virtual element to the `elements.reference` option," ,
70
+ "as it must be a real DOM element. Use `floating.setPositionReference()`" ,
71
+ "instead." ,
72
+ ) ;
73
+ }
89
74
}
75
+ this . #positionReference = options . elements . reference ;
90
76
}
91
77
92
- // Enable the user to set the position reference later to something other than
93
- // what it was initialized with
94
- let positionReference = $state < ReferenceElement | null > (
95
- elementsProp . reference ,
96
- ) ;
97
-
98
- const onOpenChange = (
99
- open : boolean ,
100
- event ?: Event ,
101
- reason ?: OpenChangeReason ,
102
- ) => {
103
- data . openEvent = open ? event : undefined ;
104
- events . emit ( "openchange" , { open, event, reason, nested } ) ;
105
- onOpenChangeProp ( open , event , reason ) ;
78
+ onOpenChange : OnOpenChange = ( open , event , reason ) => {
79
+ this . data . openEvent = open ? event : undefined ;
80
+ this . events . emit ( "openchange" , {
81
+ open,
82
+ event,
83
+ reason,
84
+ nested : this . #nested,
85
+ } ) ;
86
+ this . options . onOpenChange ?.( open , event , reason ) ;
106
87
} ;
107
88
108
- const elements = $derived ( {
109
- reference : positionReference || elementsProp . reference || null ,
110
- floating : elementsProp . floating || null ,
111
- domReference : elementsProp . reference as Element | null ,
112
- } ) ;
89
+ setPositionReference = ( node : ReferenceElement | null ) => {
90
+ this . #positionReference = node ;
91
+ } ;
113
92
114
- return {
115
- data,
116
- get open ( ) {
117
- return open ;
118
- } ,
119
- onOpenChange,
120
- elements : {
93
+ get elements ( ) {
94
+ const _this = this ;
95
+ return {
121
96
get reference ( ) {
122
- return elements . reference ;
123
- } ,
124
- set reference ( v : ReferenceType | null ) {
125
- elementsProp . reference = v ;
97
+ return _this . #elements. reference ;
126
98
} ,
127
99
get floating ( ) {
128
- return elements . floating ;
100
+ return _this . # elements. floating ;
129
101
} ,
130
- set floating ( v : HTMLElement | null ) {
131
- elementsProp . floating = v ;
102
+ set floating ( node : HTMLElement | null ) {
103
+ _this . #floatingElement = node ;
132
104
} ,
133
105
get domReference ( ) {
134
- return elements . domReference ;
135
- } ,
136
- } ,
137
- events,
138
- floatingId,
139
- refs : {
140
- setPositionReference ( node : ReferenceElement | null ) {
141
- positionReference = node ;
106
+ return _this . #referenceElement;
142
107
} ,
143
- } ,
144
- } ;
108
+ } ;
109
+ }
110
+ }
111
+
112
+ export function useFloatingRootContext ( options : UseFloatingRootContextOptions ) {
113
+ return new FloatingRootContext ( options ) ;
145
114
}
146
115
147
- export { useFloatingRootContext } ;
148
- export type { FloatingRootContext , UseFloatingRootContextOptions } ;
116
+ export type { UseFloatingRootContextOptions } ;
117
+ export { FloatingRootContext } ;
0 commit comments