@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/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
- export function createPubSub(): any {
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) {
@@ -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(point: number[], rect: any): boolean {
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
- export function safePolygon(options: any = {}): any {
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: any) => {
68
+ const fn: HandleClose = (_ref: HandleCloseContext) => {
60
69
  const { x, y, placement, elements, onClose, nodeId, tree } = _ref;
61
- return function onMouseMove(event: any) {
70
+ return function onMouseMove(event: MouseEvent) {
62
71
  function close() {
63
72
  clearTimeoutIfSet(timeoutRef);
64
73
  onClose();
@@ -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 function useTransitionStatus(...args: any[]): any {
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 any) ?? {};
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 function useTransitionStyles(...args: any[]): any {
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 any) ?? {};
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: any) => ({
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
- export const FloatingNodeContext = createContext<any>(null);
20
- export const FloatingTreeContext = createContext<any>(null);
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
- return context ? context.id : null;
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
- export const useFloatingTree = (): any => useContext(FloatingTreeContext);
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 function FloatingNode(props: any): any {
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 function FloatingTree(props: any): any {
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<any[]>([], S('FloatingTree:nodes'));
105
+ const nodesRef = useRef<FloatingNodeType[]>([], S('FloatingTree:nodes'));
64
106
  const addNode = useCallback(
65
- (node: any) => {
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: any) => {
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'),