@octanejs/floating-ui 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/Composite.ts +118 -20
- package/src/FloatingArrow.ts +61 -4
- package/src/FloatingFocusManager.ts +127 -26
- package/src/FloatingList.ts +58 -13
- package/src/FloatingOverlay.ts +26 -3
- package/src/FloatingPortal.ts +100 -23
- package/src/context.ts +43 -15
- package/src/delayGroup.ts +66 -8
- package/src/index.ts +108 -0
- package/src/pubsub.ts +3 -1
- package/src/safePolygon.ts +13 -4
- package/src/transitions.ts +76 -10
- package/src/tree.ts +53 -11
- package/src/types.ts +337 -0
- package/src/useClick.ts +63 -13
- package/src/useClientPoint.ts +50 -10
- package/src/useDismiss.ts +86 -12
- package/src/useFloating.ts +46 -14
- package/src/useFocus.ts +33 -7
- package/src/useHover.ts +80 -19
- package/src/useId.ts +1 -1
- package/src/useInteractions.ts +12 -3
- package/src/useListNavigation.ts +162 -8
- package/src/useMergeRefs.ts +6 -1
- package/src/useRole.ts +41 -12
- package/src/useTypeahead.ts +75 -10
- package/src/utils/index.ts +159 -62
package/src/index.ts
CHANGED
|
@@ -23,6 +23,86 @@ export {
|
|
|
23
23
|
size,
|
|
24
24
|
} from '@floating-ui/dom';
|
|
25
25
|
|
|
26
|
+
// Positioning types, re-exported from @floating-ui/dom (mirrors upstream
|
|
27
|
+
// @floating-ui/react's type re-export surface).
|
|
28
|
+
export type {
|
|
29
|
+
AlignedPlacement,
|
|
30
|
+
Alignment,
|
|
31
|
+
AutoPlacementOptions,
|
|
32
|
+
AutoUpdateOptions,
|
|
33
|
+
Axis,
|
|
34
|
+
Boundary,
|
|
35
|
+
ClientRectObject,
|
|
36
|
+
ComputePositionConfig,
|
|
37
|
+
ComputePositionReturn,
|
|
38
|
+
Coords,
|
|
39
|
+
Derivable,
|
|
40
|
+
DetectOverflowOptions,
|
|
41
|
+
Dimensions,
|
|
42
|
+
ElementContext,
|
|
43
|
+
ElementRects,
|
|
44
|
+
Elements,
|
|
45
|
+
FlipOptions,
|
|
46
|
+
FloatingElement,
|
|
47
|
+
HideOptions,
|
|
48
|
+
InlineOptions,
|
|
49
|
+
Length,
|
|
50
|
+
Middleware,
|
|
51
|
+
MiddlewareArguments,
|
|
52
|
+
MiddlewareData,
|
|
53
|
+
MiddlewareReturn,
|
|
54
|
+
MiddlewareState,
|
|
55
|
+
NodeScroll,
|
|
56
|
+
OffsetOptions,
|
|
57
|
+
Padding,
|
|
58
|
+
Placement,
|
|
59
|
+
Platform,
|
|
60
|
+
Rect,
|
|
61
|
+
ReferenceElement,
|
|
62
|
+
RootBoundary,
|
|
63
|
+
ShiftOptions,
|
|
64
|
+
Side,
|
|
65
|
+
SideObject,
|
|
66
|
+
SizeOptions,
|
|
67
|
+
Strategy,
|
|
68
|
+
VirtualElement,
|
|
69
|
+
} from '@floating-ui/dom';
|
|
70
|
+
|
|
71
|
+
// The shared octane-adapted type surface (mirrors @floating-ui/react's exported
|
|
72
|
+
// type names; see ./types for the octane adaptations).
|
|
73
|
+
export type {
|
|
74
|
+
ArrowOptions,
|
|
75
|
+
ContextData,
|
|
76
|
+
Delay,
|
|
77
|
+
ElementProps,
|
|
78
|
+
ExtendedElements,
|
|
79
|
+
ExtendedRefs,
|
|
80
|
+
ExtendedUserProps,
|
|
81
|
+
FloatingContext,
|
|
82
|
+
FloatingEvents,
|
|
83
|
+
FloatingNodeType,
|
|
84
|
+
FloatingRootContext,
|
|
85
|
+
FloatingStyles,
|
|
86
|
+
FloatingTreeType,
|
|
87
|
+
HandleClose,
|
|
88
|
+
HandleCloseContext,
|
|
89
|
+
HTMLProps,
|
|
90
|
+
MutableRefObject,
|
|
91
|
+
NarrowedElement,
|
|
92
|
+
OpenChangeReason,
|
|
93
|
+
RefCallback,
|
|
94
|
+
ReferenceType,
|
|
95
|
+
SafePolygonOptions,
|
|
96
|
+
UseFloatingData,
|
|
97
|
+
UseFloatingOptions,
|
|
98
|
+
UseFloatingReturn,
|
|
99
|
+
UseFloatingRootContextOptions,
|
|
100
|
+
UseInteractionsReturn,
|
|
101
|
+
UsePositionFloatingData,
|
|
102
|
+
UsePositionFloatingOptions,
|
|
103
|
+
UsePositionFloatingReturn,
|
|
104
|
+
} from './types';
|
|
105
|
+
|
|
26
106
|
// Positioning + context. The public `useFloating` (from ./context) wraps the
|
|
27
107
|
// positioning core and returns the interaction `context`.
|
|
28
108
|
export { useFloating, useFloatingRootContext, createPubSub } from './context';
|
|
@@ -54,6 +134,15 @@ export { useListNavigation } from './useListNavigation';
|
|
|
54
134
|
export { useTypeahead } from './useTypeahead';
|
|
55
135
|
export { useHover } from './useHover';
|
|
56
136
|
export { safePolygon } from './safePolygon';
|
|
137
|
+
// Interaction-hook prop types (mirror upstream @floating-ui/react's names).
|
|
138
|
+
export type { UseRoleProps } from './useRole';
|
|
139
|
+
export type { UseClickProps } from './useClick';
|
|
140
|
+
export type { UseFocusProps } from './useFocus';
|
|
141
|
+
export type { UseDismissProps } from './useDismiss';
|
|
142
|
+
export type { UseClientPointProps } from './useClientPoint';
|
|
143
|
+
export type { UseListNavigationProps } from './useListNavigation';
|
|
144
|
+
export type { UseTypeaheadProps } from './useTypeahead';
|
|
145
|
+
export type { UseHoverProps } from './useHover';
|
|
57
146
|
|
|
58
147
|
// Components (phase 3 — in progress).
|
|
59
148
|
export { FloatingOverlay } from './FloatingOverlay';
|
|
@@ -77,3 +166,22 @@ export {
|
|
|
77
166
|
useDelayGroupContext,
|
|
78
167
|
FloatingDelayGroupContext,
|
|
79
168
|
} from './delayGroup';
|
|
169
|
+
|
|
170
|
+
// Component / transition prop types (mirror upstream's exported names; the
|
|
171
|
+
// octane adaptations — OctaneNode children, ref-as-prop, native events — are
|
|
172
|
+
// documented on each type). FloatingListProps / UseListItemProps are exported
|
|
173
|
+
// here even though upstream keeps them module-private, since octane bindings
|
|
174
|
+
// compose them.
|
|
175
|
+
export type { CompositeProps, CompositeItemProps } from './Composite';
|
|
176
|
+
export type { FloatingArrowProps } from './FloatingArrow';
|
|
177
|
+
export type { FloatingFocusManagerProps } from './FloatingFocusManager';
|
|
178
|
+
export type { FloatingListProps, UseListItemProps } from './FloatingList';
|
|
179
|
+
export type { FloatingOverlayProps } from './FloatingOverlay';
|
|
180
|
+
export type { FloatingPortalProps, UseFloatingPortalNodeProps } from './FloatingPortal';
|
|
181
|
+
export type { FloatingNodeProps, FloatingTreeProps } from './tree';
|
|
182
|
+
export type {
|
|
183
|
+
TransitionStatus,
|
|
184
|
+
UseTransitionStatusProps,
|
|
185
|
+
UseTransitionStylesProps,
|
|
186
|
+
} from './transitions';
|
|
187
|
+
export type { FloatingDelayGroupProps } from './delayGroup';
|
package/src/pubsub.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// The tiny event emitter shared by useFloatingRootContext and FloatingTree. Kept
|
|
2
2
|
// in its own module so context.ts and tree.ts don't form an import cycle.
|
|
3
|
-
|
|
3
|
+
import type { FloatingEvents } from './types';
|
|
4
|
+
|
|
5
|
+
export function createPubSub(): FloatingEvents {
|
|
4
6
|
const map = new Map<string, Set<(data: any) => void>>();
|
|
5
7
|
return {
|
|
6
8
|
emit(event: string, data: any) {
|
package/src/safePolygon.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { isElement } from '@floating-ui/utils/dom';
|
|
5
5
|
|
|
6
6
|
import { clearTimeoutIfSet, contains, getNodeChildren, getTarget } from './utils';
|
|
7
|
+
import type { HandleClose, HandleCloseContext, SafePolygonOptions } from './types';
|
|
7
8
|
|
|
8
9
|
function isPointInPolygon(point: number[], polygon: number[][]): boolean {
|
|
9
10
|
const [x, y] = point;
|
|
@@ -19,7 +20,10 @@ function isPointInPolygon(point: number[], polygon: number[][]): boolean {
|
|
|
19
20
|
}
|
|
20
21
|
return isInsideResult;
|
|
21
22
|
}
|
|
22
|
-
function isInside(
|
|
23
|
+
function isInside(
|
|
24
|
+
point: number[],
|
|
25
|
+
rect: { x: number; y: number; width: number; height: number },
|
|
26
|
+
): boolean {
|
|
23
27
|
return (
|
|
24
28
|
point[0] >= rect.x &&
|
|
25
29
|
point[0] <= rect.x + rect.width &&
|
|
@@ -28,7 +32,12 @@ function isInside(point: number[], rect: any): boolean {
|
|
|
28
32
|
);
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Generates a safe polygon area that the user can traverse without closing the
|
|
37
|
+
* floating element once leaving the reference element.
|
|
38
|
+
* @see https://floating-ui.com/docs/useHover#safepolygon
|
|
39
|
+
*/
|
|
40
|
+
export function safePolygon(options: SafePolygonOptions = {}): HandleClose {
|
|
32
41
|
const { buffer = 0.5, blockPointerEvents = false, requireIntent = true } = options;
|
|
33
42
|
|
|
34
43
|
const timeoutRef = { current: -1 };
|
|
@@ -56,9 +65,9 @@ export function safePolygon(options: any = {}): any {
|
|
|
56
65
|
return speed;
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
const fn = (_ref:
|
|
68
|
+
const fn: HandleClose = (_ref: HandleCloseContext) => {
|
|
60
69
|
const { x, y, placement, elements, onClose, nodeId, tree } = _ref;
|
|
61
|
-
return function onMouseMove(event:
|
|
70
|
+
return function onMouseMove(event: MouseEvent) {
|
|
62
71
|
function close() {
|
|
63
72
|
clearTimeoutIfSet(timeoutRef);
|
|
64
73
|
onClose();
|
package/src/transitions.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// internal useDelayUnmount) — placement-aware CSS-transition state/styles for a
|
|
3
3
|
// floating element. ReactDOM.flushSync → octane flushSync.
|
|
4
4
|
import { flushSync, useEffect, useMemo, useState } from 'octane';
|
|
5
|
+
import type { Placement, Side } from '@floating-ui/dom';
|
|
5
6
|
|
|
6
7
|
import { splitSlot, subSlot } from './internal';
|
|
7
8
|
import {
|
|
@@ -9,7 +10,20 @@ import {
|
|
|
9
10
|
execWithArgsOrReturn,
|
|
10
11
|
useLatestRef,
|
|
11
12
|
useModernLayoutEffect,
|
|
13
|
+
type CSSProperties,
|
|
12
14
|
} from './utils';
|
|
15
|
+
import type { FloatingContext, ReferenceType } from './types';
|
|
16
|
+
|
|
17
|
+
export type TransitionStatus = 'unmounted' | 'initial' | 'open' | 'close';
|
|
18
|
+
|
|
19
|
+
type Duration = number | Partial<{ open: number; close: number }>;
|
|
20
|
+
|
|
21
|
+
// Styles (or a side/placement-aware factory of styles) for one transition state.
|
|
22
|
+
// Upstream's `CSSStylesProperty`, with `React.CSSProperties` → the octane style
|
|
23
|
+
// object form.
|
|
24
|
+
type CSSStylesProperty =
|
|
25
|
+
| CSSProperties
|
|
26
|
+
| ((params: { side: Side; placement: Placement }) => CSSProperties);
|
|
13
27
|
|
|
14
28
|
function useDelayUnmount(open: boolean, durationMs: number, slot: symbol | undefined): boolean {
|
|
15
29
|
const [isMounted, setIsMounted] = useState(open, subSlot(slot, 'mounted'));
|
|
@@ -29,10 +43,31 @@ function useDelayUnmount(open: boolean, durationMs: number, slot: symbol | undef
|
|
|
29
43
|
return isMounted;
|
|
30
44
|
}
|
|
31
45
|
|
|
32
|
-
export
|
|
46
|
+
export interface UseTransitionStatusProps {
|
|
47
|
+
/**
|
|
48
|
+
* The duration of the transition in milliseconds, or an object containing
|
|
49
|
+
* `open` and `close` keys for different durations.
|
|
50
|
+
*/
|
|
51
|
+
duration?: Duration;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Provides a status string to apply CSS transitions to a floating element,
|
|
56
|
+
* correctly handling placement-aware transitions.
|
|
57
|
+
* @see https://floating-ui.com/docs/useTransition#usetransitionstatus
|
|
58
|
+
*/
|
|
59
|
+
export function useTransitionStatus(
|
|
60
|
+
context: FloatingContext,
|
|
61
|
+
props?: UseTransitionStatusProps,
|
|
62
|
+
slot?: symbol,
|
|
63
|
+
): { isMounted: boolean; status: TransitionStatus };
|
|
64
|
+
export function useTransitionStatus(...args: any[]): {
|
|
65
|
+
isMounted: boolean;
|
|
66
|
+
status: TransitionStatus;
|
|
67
|
+
} {
|
|
33
68
|
const [user, slot] = splitSlot(args);
|
|
34
|
-
const context = user[0];
|
|
35
|
-
const props = (user[1] as
|
|
69
|
+
const context = user[0] as FloatingContext;
|
|
70
|
+
const props = (user[1] as UseTransitionStatusProps) ?? {};
|
|
36
71
|
|
|
37
72
|
const open = context.open;
|
|
38
73
|
const floating = context.elements.floating;
|
|
@@ -40,7 +75,7 @@ export function useTransitionStatus(...args: any[]): any {
|
|
|
40
75
|
|
|
41
76
|
const isNumberDuration = typeof duration === 'number';
|
|
42
77
|
const closeDuration = (isNumberDuration ? duration : duration.close) || 0;
|
|
43
|
-
const [status, setStatus] = useState('unmounted', subSlot(slot, 'status'));
|
|
78
|
+
const [status, setStatus] = useState<TransitionStatus>('unmounted', subSlot(slot, 'status'));
|
|
44
79
|
const isMounted = useDelayUnmount(open, closeDuration, subSlot(slot, 'unmount'));
|
|
45
80
|
if (!isMounted && status === 'close') {
|
|
46
81
|
setStatus('unmounted');
|
|
@@ -71,10 +106,41 @@ export function useTransitionStatus(...args: any[]): any {
|
|
|
71
106
|
return { isMounted, status };
|
|
72
107
|
}
|
|
73
108
|
|
|
74
|
-
export
|
|
109
|
+
export interface UseTransitionStylesProps extends UseTransitionStatusProps {
|
|
110
|
+
/**
|
|
111
|
+
* The styles to apply when the floating element is initially mounted.
|
|
112
|
+
*/
|
|
113
|
+
initial?: CSSStylesProperty;
|
|
114
|
+
/**
|
|
115
|
+
* The styles to apply when the floating element is transitioning to the
|
|
116
|
+
* `open` state.
|
|
117
|
+
*/
|
|
118
|
+
open?: CSSStylesProperty;
|
|
119
|
+
/**
|
|
120
|
+
* The styles to apply when the floating element is transitioning to the
|
|
121
|
+
* `close` state.
|
|
122
|
+
*/
|
|
123
|
+
close?: CSSStylesProperty;
|
|
124
|
+
/**
|
|
125
|
+
* The styles to apply to all states.
|
|
126
|
+
*/
|
|
127
|
+
common?: CSSStylesProperty;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Provides styles to apply CSS transitions to a floating element, correctly
|
|
132
|
+
* handling placement-aware transitions. Wrapper around `useTransitionStatus`.
|
|
133
|
+
* @see https://floating-ui.com/docs/useTransition#usetransitionstyles
|
|
134
|
+
*/
|
|
135
|
+
export function useTransitionStyles<RT extends ReferenceType = ReferenceType>(
|
|
136
|
+
context: FloatingContext<RT>,
|
|
137
|
+
props?: UseTransitionStylesProps,
|
|
138
|
+
slot?: symbol,
|
|
139
|
+
): { isMounted: boolean; styles: CSSProperties };
|
|
140
|
+
export function useTransitionStyles(...args: any[]): { isMounted: boolean; styles: CSSProperties } {
|
|
75
141
|
const [user, slot] = splitSlot(args);
|
|
76
|
-
const context = user[0];
|
|
77
|
-
const props = (user[1] as
|
|
142
|
+
const context = user[0] as FloatingContext;
|
|
143
|
+
const props = (user[1] as UseTransitionStylesProps) ?? {};
|
|
78
144
|
|
|
79
145
|
const unstableInitial = props.initial ?? { opacity: 0 };
|
|
80
146
|
const unstableOpen = props.open;
|
|
@@ -83,13 +149,13 @@ export function useTransitionStyles(...args: any[]): any {
|
|
|
83
149
|
const duration = props.duration ?? 250;
|
|
84
150
|
|
|
85
151
|
const placement = context.placement;
|
|
86
|
-
const side = placement.split('-')[0];
|
|
152
|
+
const side = placement.split('-')[0] as Side;
|
|
87
153
|
const fnArgs = useMemo(() => ({ side, placement }), [side, placement], subSlot(slot, 'args'));
|
|
88
154
|
const isNumberDuration = typeof duration === 'number';
|
|
89
155
|
const openDuration = (isNumberDuration ? duration : duration.open) || 0;
|
|
90
156
|
const closeDuration = (isNumberDuration ? duration : duration.close) || 0;
|
|
91
157
|
|
|
92
|
-
const [styles, setStyles] = useState(
|
|
158
|
+
const [styles, setStyles] = useState<CSSProperties>(
|
|
93
159
|
() => ({
|
|
94
160
|
...execWithArgsOrReturn(unstableCommon, fnArgs),
|
|
95
161
|
...execWithArgsOrReturn(unstableInitial, fnArgs),
|
|
@@ -114,7 +180,7 @@ export function useTransitionStyles(...args: any[]): any {
|
|
|
114
180
|
return acc;
|
|
115
181
|
}, {});
|
|
116
182
|
if (status === 'initial') {
|
|
117
|
-
setStyles((s
|
|
183
|
+
setStyles((s) => ({
|
|
118
184
|
transitionProperty: s.transitionProperty,
|
|
119
185
|
...commonStyles,
|
|
120
186
|
...initialStyles,
|
package/src/tree.ts
CHANGED
|
@@ -10,26 +10,50 @@ import {
|
|
|
10
10
|
useRef,
|
|
11
11
|
useState,
|
|
12
12
|
} from 'octane';
|
|
13
|
+
import type { OctaneNode } from 'octane';
|
|
13
14
|
|
|
14
15
|
import { createPubSub } from './pubsub';
|
|
15
16
|
import { S, splitSlot, subSlot } from './internal';
|
|
16
17
|
import { useId } from './useId';
|
|
17
18
|
import { useModernLayoutEffect } from './utils';
|
|
19
|
+
import type { FloatingNodeType, FloatingTreeType, ReferenceType } from './types';
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
interface FloatingNodeContextValue {
|
|
22
|
+
id: string | undefined;
|
|
23
|
+
parentId: string | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const FloatingNodeContext = createContext<FloatingNodeContextValue | null>(null);
|
|
27
|
+
export const FloatingTreeContext = createContext<FloatingTreeType | null>(null);
|
|
21
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Returns the parent node id for nested floating elements, if available.
|
|
31
|
+
* Returns `null` for top-level floating elements.
|
|
32
|
+
*/
|
|
22
33
|
export const useFloatingParentNodeId = (): string | null => {
|
|
23
34
|
const context = useContext(FloatingNodeContext);
|
|
24
|
-
|
|
35
|
+
// A FloatingNode's `id` may be undefined (upstream allows it); the public
|
|
36
|
+
// contract is `string | null`, so coalesce.
|
|
37
|
+
return context ? (context.id ?? null) : null;
|
|
25
38
|
};
|
|
26
39
|
|
|
27
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Returns the nearest floating tree context, if available.
|
|
42
|
+
*/
|
|
43
|
+
export const useFloatingTree = <
|
|
44
|
+
RT extends ReferenceType = ReferenceType,
|
|
45
|
+
>(): FloatingTreeType<RT> | null => useContext(FloatingTreeContext) as FloatingTreeType<RT> | null;
|
|
28
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Registers a node into the `FloatingTree`, returning its id.
|
|
49
|
+
* @see https://floating-ui.com/docs/FloatingTree
|
|
50
|
+
*/
|
|
51
|
+
export function useFloatingNodeId(customParentId?: string, slot?: symbol): string;
|
|
52
|
+
export function useFloatingNodeId(slot?: symbol): string;
|
|
29
53
|
export function useFloatingNodeId(...args: any[]): string {
|
|
30
54
|
const [user, slotArg] = splitSlot(args);
|
|
31
55
|
const slot = slotArg ?? S('useFloatingNodeId');
|
|
32
|
-
const customParentId = user[0];
|
|
56
|
+
const customParentId = user[0] as string | undefined;
|
|
33
57
|
|
|
34
58
|
const id = useId(subSlot(slot, 'id'));
|
|
35
59
|
const tree = useFloatingTree();
|
|
@@ -50,7 +74,16 @@ export function useFloatingNodeId(...args: any[]): string {
|
|
|
50
74
|
return id;
|
|
51
75
|
}
|
|
52
76
|
|
|
53
|
-
export
|
|
77
|
+
export interface FloatingNodeProps {
|
|
78
|
+
children?: OctaneNode;
|
|
79
|
+
id: string | undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Provides parent node context for nested floating elements.
|
|
84
|
+
* @see https://floating-ui.com/docs/FloatingTree
|
|
85
|
+
*/
|
|
86
|
+
export function FloatingNode(props: FloatingNodeProps): OctaneNode {
|
|
54
87
|
const children = props.children;
|
|
55
88
|
const id = props.id;
|
|
56
89
|
const parentId = useFloatingParentNodeId();
|
|
@@ -58,25 +91,34 @@ export function FloatingNode(props: any): any {
|
|
|
58
91
|
return createElement(FloatingNodeContext.Provider, { value, children });
|
|
59
92
|
}
|
|
60
93
|
|
|
61
|
-
export
|
|
94
|
+
export interface FloatingTreeProps {
|
|
95
|
+
children?: OctaneNode;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Provides context for nested floating elements when they are not children of
|
|
100
|
+
* each other on the DOM.
|
|
101
|
+
* @see https://floating-ui.com/docs/FloatingTree
|
|
102
|
+
*/
|
|
103
|
+
export function FloatingTree(props: FloatingTreeProps): OctaneNode {
|
|
62
104
|
const children = props.children;
|
|
63
|
-
const nodesRef = useRef<
|
|
105
|
+
const nodesRef = useRef<FloatingNodeType[]>([], S('FloatingTree:nodes'));
|
|
64
106
|
const addNode = useCallback(
|
|
65
|
-
(node:
|
|
107
|
+
(node: FloatingNodeType) => {
|
|
66
108
|
nodesRef.current = [...nodesRef.current, node];
|
|
67
109
|
},
|
|
68
110
|
[],
|
|
69
111
|
S('FloatingTree:add'),
|
|
70
112
|
);
|
|
71
113
|
const removeNode = useCallback(
|
|
72
|
-
(node:
|
|
114
|
+
(node: FloatingNodeType) => {
|
|
73
115
|
nodesRef.current = nodesRef.current.filter((n) => n !== node);
|
|
74
116
|
},
|
|
75
117
|
[],
|
|
76
118
|
S('FloatingTree:remove'),
|
|
77
119
|
);
|
|
78
120
|
const [events] = useState(() => createPubSub(), S('FloatingTree:events'));
|
|
79
|
-
const value = useMemo(
|
|
121
|
+
const value = useMemo<FloatingTreeType>(
|
|
80
122
|
() => ({ nodesRef, addNode, removeNode, events }),
|
|
81
123
|
[addNode, removeNode, events],
|
|
82
124
|
S('FloatingTree:value'),
|