@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/useDismiss.ts CHANGED
@@ -28,27 +28,101 @@ import {
28
28
  isRootElement,
29
29
  useEffectEvent,
30
30
  } from './utils';
31
+ import type { ElementProps, FloatingRootContext } from './types';
31
32
 
32
- const bubbleHandlerKeys: any = {
33
+ export interface UseDismissProps {
34
+ /**
35
+ * Whether the Hook is enabled, including all internal Effects and event
36
+ * handlers.
37
+ * @default true
38
+ */
39
+ enabled?: boolean;
40
+ /**
41
+ * Whether to dismiss the floating element upon pressing the `esc` key.
42
+ * @default true
43
+ */
44
+ escapeKey?: boolean;
45
+ /**
46
+ * Whether to dismiss the floating element upon pressing the reference
47
+ * element. You likely want to ensure the `move` option in the `useHover()`
48
+ * Hook has been disabled when this is in use.
49
+ * @default false
50
+ */
51
+ referencePress?: boolean;
52
+ /**
53
+ * The type of event to use to determine a “press”.
54
+ * - `pointerdown` is eager on both mouse + touch input.
55
+ * - `mousedown` is eager on mouse input, but lazy on touch input.
56
+ * - `click` is lazy on both mouse + touch input.
57
+ * @default 'pointerdown'
58
+ */
59
+ referencePressEvent?: 'pointerdown' | 'mousedown' | 'click';
60
+ /**
61
+ * Whether to dismiss the floating element upon pressing outside of the
62
+ * floating element. Accepts a guard function to filter which presses count
63
+ * as outside (native `MouseEvent`).
64
+ * @default true
65
+ */
66
+ outsidePress?: boolean | ((event: MouseEvent) => boolean);
67
+ /**
68
+ * The type of event to use to determine an outside “press”.
69
+ * - `pointerdown` is eager on both mouse + touch input.
70
+ * - `mousedown` is eager on mouse input, but lazy on touch input.
71
+ * - `click` is lazy on both mouse + touch input.
72
+ * @default 'pointerdown'
73
+ */
74
+ outsidePressEvent?: 'pointerdown' | 'mousedown' | 'click';
75
+ /**
76
+ * Whether to dismiss the floating element upon scrolling an overflow
77
+ * ancestor.
78
+ * @default false
79
+ */
80
+ ancestorScroll?: boolean;
81
+ /**
82
+ * Determines whether event listeners bubble upwards through a tree of
83
+ * floating elements.
84
+ */
85
+ bubbles?: boolean | { escapeKey?: boolean; outsidePress?: boolean };
86
+ /**
87
+ * Determines whether to use capture phase event listeners.
88
+ */
89
+ capture?: boolean | { escapeKey?: boolean; outsidePress?: boolean };
90
+ }
91
+
92
+ type PressHandlerKey = 'pointerdown' | 'mousedown' | 'click';
93
+
94
+ const bubbleHandlerKeys: Record<PressHandlerKey, string> = {
33
95
  pointerdown: 'onPointerDown',
34
96
  mousedown: 'onMouseDown',
35
97
  click: 'onClick',
36
98
  };
37
- const captureHandlerKeys: any = {
99
+ const captureHandlerKeys: Record<PressHandlerKey, string> = {
38
100
  pointerdown: 'onPointerDownCapture',
39
101
  mousedown: 'onMouseDownCapture',
40
102
  click: 'onClickCapture',
41
103
  };
42
- const normalizeProp = (normalizable: any) => ({
104
+ const normalizeProp = (
105
+ normalizable?: boolean | { escapeKey?: boolean; outsidePress?: boolean },
106
+ ) => ({
43
107
  escapeKey: typeof normalizable === 'boolean' ? normalizable : (normalizable?.escapeKey ?? false),
44
108
  outsidePress:
45
109
  typeof normalizable === 'boolean' ? normalizable : (normalizable?.outsidePress ?? true),
46
110
  });
47
111
 
48
- export function useDismiss(...args: any[]): any {
112
+ /**
113
+ * Closes the floating element when a dismissal is requested — by default, when
114
+ * the user presses the `escape` key or outside of the floating element.
115
+ * @see https://floating-ui.com/docs/useDismiss
116
+ */
117
+ export function useDismiss(
118
+ context: FloatingRootContext,
119
+ props?: UseDismissProps,
120
+ slot?: symbol,
121
+ ): ElementProps;
122
+ export function useDismiss(...args: any[]): ElementProps {
49
123
  const [user, slot] = splitSlot(args);
50
- const context = user[0];
51
- const props = (user[1] as any) ?? {};
124
+ const context = user[0] as FloatingRootContext;
125
+ const props = (user[1] as UseDismissProps) ?? {};
52
126
 
53
127
  const open = context.open;
54
128
  const onOpenChange = context.onOpenChange;
@@ -78,7 +152,7 @@ export function useDismiss(...args: any[]): any {
78
152
  const isComposingRef = useRef(false, subSlot(slot, 'comp'));
79
153
 
80
154
  const closeOnEscapeKeyDown = useEffectEvent(
81
- (event: any) => {
155
+ (event: KeyboardEvent) => {
82
156
  if (!open || !enabled || !escapeKey || event.key !== 'Escape') {
83
157
  return;
84
158
  }
@@ -102,13 +176,13 @@ export function useDismiss(...args: any[]): any {
102
176
  }
103
177
  }
104
178
  }
105
- onOpenChange(false, isReactEvent(event) ? event.nativeEvent : event, 'escape-key');
179
+ onOpenChange(false, isReactEvent(event) ? (event as any).nativeEvent : event, 'escape-key');
106
180
  },
107
181
  subSlot(slot, 'esc'),
108
182
  );
109
183
 
110
184
  const closeOnEscapeKeyDownCapture = useEffectEvent(
111
- (event: any) => {
185
+ (event: KeyboardEvent) => {
112
186
  const callback = () => {
113
187
  closeOnEscapeKeyDown(event);
114
188
  getTarget(event)?.removeEventListener('keydown', callback);
@@ -119,7 +193,7 @@ export function useDismiss(...args: any[]): any {
119
193
  );
120
194
 
121
195
  const closeOnPressOutside = useEffectEvent(
122
- (event: any) => {
196
+ (event: MouseEvent) => {
123
197
  const insideReactTree = dataRef.current.insideReactTree;
124
198
  dataRef.current.insideReactTree = false;
125
199
 
@@ -211,7 +285,7 @@ export function useDismiss(...args: any[]): any {
211
285
  );
212
286
 
213
287
  const closeOnPressOutsideCapture = useEffectEvent(
214
- (event: any) => {
288
+ (event: MouseEvent) => {
215
289
  const callback = () => {
216
290
  closeOnPressOutside(event);
217
291
  getTarget(event)?.removeEventListener(outsidePressEvent, callback);
@@ -371,7 +445,7 @@ export function useDismiss(...args: any[]): any {
371
445
  subSlot(slot, 'm:flo'),
372
446
  );
373
447
 
374
- return useMemo(
448
+ return useMemo<ElementProps>(
375
449
  () => (enabled ? { reference, floating } : {}),
376
450
  [enabled, reference, floating],
377
451
  subSlot(slot, 'm:ret'),
@@ -4,10 +4,25 @@
4
4
  // ./internal). The returned `context`/refs carry the root slot so the interaction
5
5
  // hooks (later phases) can compose without their own slot.
6
6
  import { arrow as arrowCore, computePosition } from '@floating-ui/dom';
7
+ import type {
8
+ ComputePositionConfig,
9
+ Derivable,
10
+ Middleware,
11
+ MiddlewareState,
12
+ } from '@floating-ui/dom';
7
13
  import { flushSync, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'octane';
8
14
 
9
15
  import { splitSlot, subSlot } from './internal';
10
16
  import { deepEqual, getDPR, roundByDPR } from './utils';
17
+ import type {
18
+ ArrowOptions,
19
+ FloatingStyles,
20
+ MutableRefObject,
21
+ ReferenceType,
22
+ UsePositionFloatingData,
23
+ UsePositionFloatingOptions,
24
+ UsePositionFloatingReturn,
25
+ } from './types';
11
26
 
12
27
  // Keep a ref pointed at the latest `value` without retriggering effects.
13
28
  function useLatestRef<T>(value: T, slot: symbol | undefined): { current: T } {
@@ -24,9 +39,22 @@ function useLatestRef<T>(value: T, slot: symbol | undefined): { current: T } {
24
39
 
25
40
  // The positioning core (ported from @floating-ui/react-dom's useFloating). The
26
41
  // PUBLIC useFloating (in ./context) wraps this and adds the interaction context.
42
+ // Called as `usePositionFloating([options, slot])` — the array form keeps the
43
+ // compiler-injected trailing slot detectable for plain-`.ts` callers.
44
+ // RT defaults to `any` (not upstream's `ReferenceType`) so that pre-typing
45
+ // callers that supply untyped elements — and therefore give inference no
46
+ // candidate — keep their loose reference typing (e.g. @octanejs/base-ui builds
47
+ // its own stricter FloatingContext from this return). Callers with typed
48
+ // elements infer RT precisely.
49
+ export function usePositionFloating<RT extends ReferenceType = any>(
50
+ args: [UsePositionFloatingOptions<RT>?, (symbol | undefined)?],
51
+ ): UsePositionFloatingReturn<RT>;
52
+ // Loose fallback: pre-typing callers built untyped option bags in plain `.ts`;
53
+ // they keep compiling while still receiving the typed return.
54
+ export function usePositionFloating(args: unknown[]): UsePositionFloatingReturn<any>;
27
55
  export function usePositionFloating(args: any[]): any {
28
56
  const [user, slot] = splitSlot(args);
29
- const options = (user[0] as any) ?? {};
57
+ const options = (user[0] as UsePositionFloatingOptions) ?? {};
30
58
 
31
59
  const placement = options.placement ?? 'bottom';
32
60
  const strategy = options.strategy ?? 'absolute';
@@ -38,7 +66,7 @@ export function usePositionFloating(args: any[]): any {
38
66
  const whileElementsMounted = options.whileElementsMounted;
39
67
  const open = options.open;
40
68
 
41
- const [data, setData] = useState(
69
+ const [data, setData] = useState<UsePositionFloatingData>(
42
70
  {
43
71
  x: 0,
44
72
  y: 0,
@@ -55,14 +83,14 @@ export function usePositionFloating(args: any[]): any {
55
83
  setLatestMiddleware(middleware);
56
84
  }
57
85
 
58
- const [_reference, _setReference] = useState(null, subSlot(slot, 'ref'));
59
- const [_floating, _setFloating] = useState(null, subSlot(slot, 'flo'));
86
+ const [_reference, _setReference] = useState<ReferenceType | null>(null, subSlot(slot, 'ref'));
87
+ const [_floating, _setFloating] = useState<HTMLElement | null>(null, subSlot(slot, 'flo'));
60
88
 
61
- const referenceRef = useRef<any>(null, subSlot(slot, 'rref'));
62
- const floatingRef = useRef<any>(null, subSlot(slot, 'rflo'));
89
+ const referenceRef = useRef<ReferenceType | null>(null, subSlot(slot, 'rref'));
90
+ const floatingRef = useRef<HTMLElement | null>(null, subSlot(slot, 'rflo'));
63
91
 
64
92
  const setReference = useCallback(
65
- (node: any) => {
93
+ (node: ReferenceType | null) => {
66
94
  if (node !== referenceRef.current) {
67
95
  referenceRef.current = node;
68
96
  _setReference(node);
@@ -72,7 +100,7 @@ export function usePositionFloating(args: any[]): any {
72
100
  subSlot(slot, 'sref'),
73
101
  );
74
102
  const setFloating = useCallback(
75
- (node: any) => {
103
+ (node: HTMLElement | null) => {
76
104
  if (node !== floatingRef.current) {
77
105
  floatingRef.current = node;
78
106
  _setFloating(node);
@@ -97,7 +125,11 @@ export function usePositionFloating(args: any[]): any {
97
125
  if (!referenceRef.current || !floatingRef.current) {
98
126
  return;
99
127
  }
100
- const config: any = { placement, strategy, middleware: latestMiddleware };
128
+ const config: Partial<ComputePositionConfig> = {
129
+ placement,
130
+ strategy,
131
+ middleware: latestMiddleware,
132
+ };
101
133
  if (platformRef.current) {
102
134
  config.platform = platformRef.current;
103
135
  }
@@ -122,7 +154,7 @@ export function usePositionFloating(args: any[]): any {
122
154
  () => {
123
155
  if (open === false && dataRef.current.isPositioned) {
124
156
  dataRef.current.isPositioned = false;
125
- setData((d: any) => ({ ...d, isPositioned: false }));
157
+ setData((d) => ({ ...d, isPositioned: false }));
126
158
  }
127
159
  },
128
160
  [open],
@@ -167,7 +199,7 @@ export function usePositionFloating(args: any[]): any {
167
199
  subSlot(slot, 'm:el'),
168
200
  );
169
201
 
170
- const floatingStyles = useMemo(
202
+ const floatingStyles = useMemo<FloatingStyles>(
171
203
  () => {
172
204
  const initialStyles = { position: strategy, left: 0, top: 0 };
173
205
  if (!elements.floating) {
@@ -196,14 +228,14 @@ export function usePositionFloating(args: any[]): any {
196
228
  }
197
229
 
198
230
  // Ref-aware `arrow` middleware: accepts an octane ref ({current}) or an element.
199
- export const arrow = (options: any) => {
200
- function isRef(value: any) {
231
+ export const arrow = (options: ArrowOptions | Derivable<ArrowOptions>): Middleware => {
232
+ function isRef(value: unknown): value is MutableRefObject<Element | null> {
201
233
  return {}.hasOwnProperty.call(value, 'current');
202
234
  }
203
235
  return {
204
236
  name: 'arrow',
205
237
  options,
206
- fn(state: any) {
238
+ fn(state: MiddlewareState) {
207
239
  const { element, padding } = typeof options === 'function' ? options(state) : options;
208
240
  if (element && isRef(element)) {
209
241
  if (element.current != null) {
package/src/useFocus.ts CHANGED
@@ -15,11 +15,37 @@ import {
15
15
  isTypeableElement,
16
16
  matchesFocusVisible,
17
17
  } from './utils';
18
+ import type { ElementProps, FloatingRootContext, OpenChangeReason } from './types';
18
19
 
19
- export function useFocus(...args: any[]): any {
20
+ export interface UseFocusProps {
21
+ /**
22
+ * Whether the Hook is enabled, including all internal Effects and event
23
+ * handlers.
24
+ * @default true
25
+ */
26
+ enabled?: boolean;
27
+ /**
28
+ * Whether the open state only changes if the focus event is considered
29
+ * visible (`:focus-visible` CSS selector).
30
+ * @default true
31
+ */
32
+ visibleOnly?: boolean;
33
+ }
34
+
35
+ /**
36
+ * Opens the floating element while the reference element has focus, like CSS
37
+ * `:focus`.
38
+ * @see https://floating-ui.com/docs/useFocus
39
+ */
40
+ export function useFocus(
41
+ context: FloatingRootContext,
42
+ props?: UseFocusProps,
43
+ slot?: symbol,
44
+ ): ElementProps;
45
+ export function useFocus(...args: any[]): ElementProps {
20
46
  const [user, slot] = splitSlot(args);
21
- const context = user[0];
22
- const props = (user[1] as any) ?? {};
47
+ const context = user[0] as FloatingRootContext;
48
+ const props = (user[1] as UseFocusProps) ?? {};
23
49
 
24
50
  const open = context.open;
25
51
  const onOpenChange = context.onOpenChange;
@@ -73,7 +99,7 @@ export function useFocus(...args: any[]): any {
73
99
  useEffect(
74
100
  () => {
75
101
  if (!enabled) return;
76
- function onOpenChangeLocal(_ref: any) {
102
+ function onOpenChangeLocal(_ref: { reason?: OpenChangeReason }) {
77
103
  const { reason } = _ref;
78
104
  if (reason === 'reference-press' || reason === 'escape-key') {
79
105
  blockFocusRef.current = true;
@@ -103,7 +129,7 @@ export function useFocus(...args: any[]): any {
103
129
  onMouseLeave() {
104
130
  blockFocusRef.current = false;
105
131
  },
106
- onFocus(event: any) {
132
+ onFocus(event: FocusEvent) {
107
133
  if (blockFocusRef.current) return;
108
134
  const target = getTarget(event);
109
135
  if (visibleOnly && isElement(target)) {
@@ -117,7 +143,7 @@ export function useFocus(...args: any[]): any {
117
143
  }
118
144
  onOpenChange(true, event, 'focus');
119
145
  },
120
- onBlur(event: any) {
146
+ onBlur(event: FocusEvent) {
121
147
  blockFocusRef.current = false;
122
148
  const relatedTarget = event.relatedTarget;
123
149
 
@@ -146,7 +172,7 @@ export function useFocus(...args: any[]): any {
146
172
  subSlot(slot, 'm:ref'),
147
173
  );
148
174
 
149
- return useMemo(
175
+ return useMemo<ElementProps>(
150
176
  () => (enabled ? { reference } : {}),
151
177
  [enabled, reference],
152
178
  subSlot(slot, 'm:ret'),
package/src/useHover.ts CHANGED
@@ -18,20 +18,76 @@ import {
18
18
  useLatestRef,
19
19
  useModernLayoutEffect,
20
20
  } from './utils';
21
+ import type {
22
+ Delay,
23
+ ElementProps,
24
+ FloatingRootContext,
25
+ HandleClose,
26
+ OpenChangeReason,
27
+ } from './types';
28
+
29
+ export interface UseHoverProps {
30
+ /**
31
+ * Whether the Hook is enabled, including all internal Effects and event
32
+ * handlers.
33
+ * @default true
34
+ */
35
+ enabled?: boolean;
36
+ /**
37
+ * Accepts an event handler that runs on `mousemove` to control when the
38
+ * floating element closes once the cursor leaves the reference element.
39
+ * @default null
40
+ */
41
+ handleClose?: HandleClose | null;
42
+ /**
43
+ * Waits until the user’s cursor is at “rest” over the reference element
44
+ * before changing the `open` state.
45
+ * @default 0
46
+ */
47
+ restMs?: number | (() => number);
48
+ /**
49
+ * Waits for the specified time when the event listener runs before changing
50
+ * the `open` state.
51
+ * @default 0
52
+ */
53
+ delay?: Delay | (() => Delay);
54
+ /**
55
+ * Whether the logic only runs for mouse input, ignoring touch input.
56
+ * Note: due to a bug with Linux Chrome, "pen" inputs are considered "mouse".
57
+ * @default false
58
+ */
59
+ mouseOnly?: boolean;
60
+ /**
61
+ * Whether moving the cursor over the floating element will open it, without a
62
+ * regular hover event required.
63
+ * @default true
64
+ */
65
+ move?: boolean;
66
+ }
21
67
 
22
68
  const safePolygonIdentifier = createAttribute('safe-polygon');
23
69
 
24
- function getRestMs(value: any): any {
70
+ function getRestMs(value: number | (() => number)): number {
25
71
  if (typeof value === 'function') {
26
72
  return value();
27
73
  }
28
74
  return value;
29
75
  }
30
76
 
31
- export function useHover(...args: any[]): any {
77
+ /**
78
+ * Opens the floating element while hovering over the reference element, like
79
+ * CSS `:hover`.
80
+ * @see https://floating-ui.com/docs/useHover
81
+ */
82
+ export function useHover(
83
+ context: FloatingRootContext,
84
+ props?: UseHoverProps,
85
+ slot?: symbol,
86
+ ): ElementProps;
87
+ export function useHover(...args: any[]): ElementProps {
32
88
  const [user, slot] = splitSlot(args);
33
- const context = user[0];
34
- const props = (user[1] as any) ?? {};
89
+ const context = user[0] as FloatingRootContext;
90
+ const props = (user[1] as UseHoverProps) ?? {};
35
91
 
36
92
  const open = context.open;
37
93
  const onOpenChange = context.onOpenChange;
@@ -53,13 +109,16 @@ export function useHover(...args: any[]): any {
53
109
  const openRef = useLatestRef(open, subSlot(slot, 'open'));
54
110
  const restMsRef = useLatestRef(restMs, subSlot(slot, 'restms'));
55
111
 
56
- const pointerTypeRef = useRef<any>(undefined, subSlot(slot, 'ptype'));
112
+ const pointerTypeRef = useRef<string | undefined>(undefined, subSlot(slot, 'ptype'));
57
113
  const timeoutRef = useRef(-1, subSlot(slot, 'timeout'));
58
- const handlerRef = useRef<any>(undefined, subSlot(slot, 'handler'));
114
+ const handlerRef = useRef<((event: MouseEvent) => void) | undefined>(
115
+ undefined,
116
+ subSlot(slot, 'handler'),
117
+ );
59
118
  const restTimeoutRef = useRef(-1, subSlot(slot, 'resttimeout'));
60
119
  const blockMouseMoveRef = useRef(true, subSlot(slot, 'block'));
61
120
  const performedPointerEventsMutationRef = useRef(false, subSlot(slot, 'ppem'));
62
- const unbindMouseMoveRef = useRef<any>(() => {}, subSlot(slot, 'unbind'));
121
+ const unbindMouseMoveRef = useRef<() => void>(() => {}, subSlot(slot, 'unbind'));
63
122
  const restTimeoutPendingRef = useRef(false, subSlot(slot, 'rtp'));
64
123
 
65
124
  const isHoverOpen = useEffectEvent(
@@ -96,7 +155,7 @@ export function useHover(...args: any[]): any {
96
155
  if (!enabled) return;
97
156
  if (!handleCloseRef.current) return;
98
157
  if (!open) return;
99
- function onLeave(event: any) {
158
+ function onLeave(event: MouseEvent) {
100
159
  if (isHoverOpen()) {
101
160
  onOpenChange(false, event, 'hover');
102
161
  }
@@ -112,7 +171,7 @@ export function useHover(...args: any[]): any {
112
171
  );
113
172
 
114
173
  const closeWithDelay = useCallback(
115
- (event: any, runElseBranch = true, reason = 'hover') => {
174
+ (event: Event, runElseBranch = true, reason: OpenChangeReason = 'hover') => {
116
175
  const closeDelay = getDelay(delayRef.current, 'close', pointerTypeRef.current);
117
176
  if (closeDelay && !handlerRef.current) {
118
177
  clearTimeoutIfSet(timeoutRef);
@@ -161,7 +220,7 @@ export function useHover(...args: any[]): any {
161
220
  useEffect(
162
221
  () => {
163
222
  if (!enabled) return;
164
- function onReferenceMouseEnter(event: any) {
223
+ function onReferenceMouseEnter(event: MouseEvent) {
165
224
  clearTimeoutIfSet(timeoutRef);
166
225
  blockMouseMoveRef.current = false;
167
226
  if (
@@ -181,7 +240,7 @@ export function useHover(...args: any[]): any {
181
240
  onOpenChange(true, event, 'hover');
182
241
  }
183
242
  }
184
- function onReferenceMouseLeave(event: any) {
243
+ function onReferenceMouseLeave(event: MouseEvent) {
185
244
  if (isClickLikeOpenEvent()) {
186
245
  clearPointerEvents();
187
246
  return;
@@ -216,13 +275,13 @@ export function useHover(...args: any[]): any {
216
275
  }
217
276
  const shouldClose =
218
277
  pointerTypeRef.current === 'touch'
219
- ? !contains(elements.floating, event.relatedTarget)
278
+ ? !contains(elements.floating, event.relatedTarget as Element | null)
220
279
  : true;
221
280
  if (shouldClose) {
222
281
  closeWithDelay(event);
223
282
  }
224
283
  }
225
- function onScrollMouseLeave(event: any) {
284
+ function onScrollMouseLeave(event: MouseEvent) {
226
285
  if (isClickLikeOpenEvent()) return;
227
286
  if (!dataRef.current.floatingContext) return;
228
287
  handleCloseRef.current?.({
@@ -242,13 +301,15 @@ export function useHover(...args: any[]): any {
242
301
  function onFloatingMouseEnter() {
243
302
  clearTimeoutIfSet(timeoutRef);
244
303
  }
245
- function onFloatingMouseLeave(event: any) {
304
+ function onFloatingMouseLeave(event: MouseEvent) {
246
305
  if (!isClickLikeOpenEvent()) {
247
306
  closeWithDelay(event, false);
248
307
  }
249
308
  }
250
309
  if (isElement(elements.domReference)) {
251
- const reference = elements.domReference;
310
+ // lib.dom's `ElementEventMap` lacks mouse events; these listeners are
311
+ // registered on a plain Element, so go through the generic overload.
312
+ const reference = elements.domReference as HTMLElement;
252
313
  const floating = elements.floating;
253
314
  if (open) {
254
315
  reference.addEventListener('mouseleave', onScrollMouseLeave);
@@ -311,7 +372,7 @@ export function useHover(...args: any[]): any {
311
372
  if (isElement(elements.domReference) && floatingEl) {
312
373
  const body = getDocument(elements.floating).body;
313
374
  body.setAttribute(safePolygonIdentifier, '');
314
- const ref = elements.domReference;
375
+ const ref = elements.domReference as HTMLElement;
315
376
  const parentFloating = tree?.nodesRef.current.find((node: any) => node.id === parentId)
316
377
  ?.context?.elements.floating;
317
378
  if (parentFloating) {
@@ -360,13 +421,13 @@ export function useHover(...args: any[]): any {
360
421
 
361
422
  const reference = useMemo(
362
423
  () => {
363
- function setPointerRef(event: any) {
424
+ function setPointerRef(event: PointerEvent) {
364
425
  pointerTypeRef.current = event.pointerType;
365
426
  }
366
427
  return {
367
428
  onPointerDown: setPointerRef,
368
429
  onPointerEnter: setPointerRef,
369
- onMouseMove(event: any) {
430
+ onMouseMove(event: MouseEvent) {
370
431
  function handleMouseMove() {
371
432
  if (!blockMouseMoveRef.current && !openRef.current) {
372
433
  onOpenChange(true, event, 'hover');
@@ -398,7 +459,7 @@ export function useHover(...args: any[]): any {
398
459
  subSlot(slot, 'm:ref'),
399
460
  );
400
461
 
401
- return useMemo(
462
+ return useMemo<ElementProps>(
402
463
  () => (enabled ? { reference } : {}),
403
464
  [enabled, reference],
404
465
  subSlot(slot, 'm:ret'),
package/src/useId.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // @floating-ui/react's useId, backed by octane's SSR-stable useId.
2
2
  import { useId as octaneUseId } from 'octane';
3
3
 
4
- export function useId(slot: symbol | undefined): string {
4
+ export function useId(slot?: symbol | undefined): string {
5
5
  return octaneUseId(slot);
6
6
  }
@@ -4,11 +4,16 @@ import { useCallback, useMemo } from 'octane';
4
4
 
5
5
  import { splitSlot, subSlot } from './internal';
6
6
  import { FOCUSABLE_ATTRIBUTE } from './utils';
7
+ import type { ElementProps, UseInteractionsReturn } from './types';
7
8
 
8
9
  const ACTIVE_KEY = 'active';
9
10
  const SELECTED_KEY = 'selected';
10
11
 
11
- function mergeProps(userProps: any, propsList: any[], elementKey: string): any {
12
+ function mergeProps(
13
+ userProps: any,
14
+ propsList: Array<ElementProps | void | null | undefined>,
15
+ elementKey: 'reference' | 'floating' | 'item',
16
+ ): Record<string, unknown> {
12
17
  const map = new Map<string, any[]>();
13
18
  const isItem = elementKey === 'item';
14
19
  let domUserProps = userProps;
@@ -31,7 +36,7 @@ function mergeProps(userProps: any, propsList: any[], elementKey: string): any {
31
36
  return propsOrGetProps;
32
37
  })
33
38
  .concat(userProps)
34
- .reduce((acc: any, props: any) => {
39
+ .reduce((acc: Record<string, unknown>, props: any) => {
35
40
  if (!props) {
36
41
  return acc;
37
42
  }
@@ -62,9 +67,13 @@ function mergeProps(userProps: any, propsList: any[], elementKey: string): any {
62
67
  };
63
68
  }
64
69
 
70
+ export function useInteractions(
71
+ propsList?: Array<ElementProps | void | null | undefined>,
72
+ slot?: symbol,
73
+ ): UseInteractionsReturn;
65
74
  export function useInteractions(...args: any[]): any {
66
75
  const [user, slot] = splitSlot(args);
67
- const propsList = (user[0] as any[]) ?? [];
76
+ const propsList: Array<ElementProps | void | null | undefined> = (user[0] as any[]) ?? [];
68
77
 
69
78
  const referenceDeps = propsList.map((key) => (key == null ? undefined : key.reference));
70
79
  const floatingDeps = propsList.map((key) => (key == null ? undefined : key.floating));