@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/types.ts
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
// Shared public types for @octanejs/floating-ui — the octane adaptation of
|
|
2
|
+
// @floating-ui/react's (0.27.19) type surface. Names and shapes mirror upstream;
|
|
3
|
+
// React idioms map to the port's actual contract:
|
|
4
|
+
//
|
|
5
|
+
// - `React.MutableRefObject<T>` → octane's plain mutable ref object
|
|
6
|
+
// (`MutableRefObject<T>` below — `{ current: T }`, what octane's useRef
|
|
7
|
+
// returns). Ref callbacks are `(node: T | null) => void` with an optional
|
|
8
|
+
// React-19-style cleanup return.
|
|
9
|
+
// - `React.SetStateAction` / `Dispatch` → octane's useState setter shape
|
|
10
|
+
// (`(value: T | ((prev: T) => T)) => void`).
|
|
11
|
+
// - `React.HTMLProps<T>` prop bags → `HTMLProps<T>`: octane's JSX attribute
|
|
12
|
+
// surface (`Octane.HTMLAttributes` — NATIVE event handlers, no synthetic
|
|
13
|
+
// layer) plus an open string index, because the interaction prop getters
|
|
14
|
+
// merge arbitrary keys (aria-*, data-*, ref).
|
|
15
|
+
// - `whileElementsMounted` may return `void` as well as a cleanup — the port
|
|
16
|
+
// forwards its result straight to an octane effect, which accepts both.
|
|
17
|
+
import type { Octane } from 'octane/jsx-runtime';
|
|
18
|
+
import type {
|
|
19
|
+
ComputePositionConfig,
|
|
20
|
+
ComputePositionReturn,
|
|
21
|
+
Padding,
|
|
22
|
+
Strategy,
|
|
23
|
+
VirtualElement,
|
|
24
|
+
} from '@floating-ui/dom';
|
|
25
|
+
|
|
26
|
+
type Prettify<T> = {
|
|
27
|
+
[K in keyof T]: T[K];
|
|
28
|
+
} & {};
|
|
29
|
+
|
|
30
|
+
export type ReferenceType = Element | VirtualElement;
|
|
31
|
+
|
|
32
|
+
export type NarrowedElement<T> = T extends Element ? T : Element;
|
|
33
|
+
|
|
34
|
+
/** Octane ref-object shape (what octane's `useRef` returns). */
|
|
35
|
+
export interface MutableRefObject<T> {
|
|
36
|
+
current: T;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Octane ref callback (React-19 style: may return a cleanup). */
|
|
40
|
+
export type RefCallback<T> = (node: T | null) => void | (() => void);
|
|
41
|
+
|
|
42
|
+
export type OpenChangeReason =
|
|
43
|
+
| 'outside-press'
|
|
44
|
+
| 'escape-key'
|
|
45
|
+
| 'ancestor-scroll'
|
|
46
|
+
| 'reference-press'
|
|
47
|
+
| 'click'
|
|
48
|
+
| 'hover'
|
|
49
|
+
| 'focus'
|
|
50
|
+
| 'focus-out'
|
|
51
|
+
| 'list-navigation'
|
|
52
|
+
| 'safe-polygon';
|
|
53
|
+
|
|
54
|
+
export interface FloatingEvents {
|
|
55
|
+
emit<T extends string>(event: T, data?: any): void;
|
|
56
|
+
on(event: string, handler: (data: any) => void): void;
|
|
57
|
+
off(event: string, handler: (data: any) => void): void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ContextData {
|
|
61
|
+
openEvent?: Event;
|
|
62
|
+
floatingContext?: FloatingContext;
|
|
63
|
+
/** @deprecated use `onTypingChange` prop in `useTypeahead` */
|
|
64
|
+
typing?: boolean;
|
|
65
|
+
[key: string]: any;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The pre-configured positioning styles for the floating element. Upstream
|
|
70
|
+
* types this as `React.CSSProperties`; the port returns exactly this object
|
|
71
|
+
* shape, which is assignable to octane's `style` prop.
|
|
72
|
+
*/
|
|
73
|
+
export interface FloatingStyles {
|
|
74
|
+
position: Strategy;
|
|
75
|
+
top: number;
|
|
76
|
+
left: number;
|
|
77
|
+
transform?: string;
|
|
78
|
+
willChange?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ─── Positioning core (the @floating-ui/react-dom `useFloating` surface) ─────
|
|
82
|
+
|
|
83
|
+
export type UsePositionFloatingOptions<RT extends ReferenceType = ReferenceType> = Prettify<
|
|
84
|
+
Partial<ComputePositionConfig> & {
|
|
85
|
+
/**
|
|
86
|
+
* A callback invoked when both the reference and floating elements are
|
|
87
|
+
* mounted, and cleaned up when either is unmounted. This is useful for
|
|
88
|
+
* setting up event listeners (e.g. pass `autoUpdate`).
|
|
89
|
+
*/
|
|
90
|
+
whileElementsMounted?: (
|
|
91
|
+
reference: RT,
|
|
92
|
+
floating: HTMLElement,
|
|
93
|
+
update: () => void,
|
|
94
|
+
) => void | (() => void);
|
|
95
|
+
/**
|
|
96
|
+
* Object containing the reference and floating elements.
|
|
97
|
+
*/
|
|
98
|
+
elements?: {
|
|
99
|
+
reference?: RT | null;
|
|
100
|
+
floating?: HTMLElement | null;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* The `open` state of the floating element to synchronize with the
|
|
104
|
+
* `isPositioned` value.
|
|
105
|
+
* @default false
|
|
106
|
+
*/
|
|
107
|
+
open?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Whether to use `transform` for positioning instead of `top` and `left`
|
|
110
|
+
* (layout) in the `floatingStyles` object.
|
|
111
|
+
* @default true
|
|
112
|
+
*/
|
|
113
|
+
transform?: boolean;
|
|
114
|
+
}
|
|
115
|
+
>;
|
|
116
|
+
|
|
117
|
+
export type UsePositionFloatingData = Prettify<ComputePositionReturn & { isPositioned: boolean }>;
|
|
118
|
+
|
|
119
|
+
export type UsePositionFloatingReturn<RT extends ReferenceType = ReferenceType> = Prettify<
|
|
120
|
+
UsePositionFloatingData & {
|
|
121
|
+
/**
|
|
122
|
+
* Update the position of the floating element, re-rendering the component
|
|
123
|
+
* if required.
|
|
124
|
+
*/
|
|
125
|
+
update: () => void;
|
|
126
|
+
/**
|
|
127
|
+
* Pre-configured positioning styles to apply to the floating element.
|
|
128
|
+
*/
|
|
129
|
+
floatingStyles: FloatingStyles;
|
|
130
|
+
/**
|
|
131
|
+
* Object containing the reference and floating refs and reactive setters.
|
|
132
|
+
*/
|
|
133
|
+
refs: {
|
|
134
|
+
reference: MutableRefObject<RT | null>;
|
|
135
|
+
floating: MutableRefObject<HTMLElement | null>;
|
|
136
|
+
setReference: (node: RT | null) => void;
|
|
137
|
+
setFloating: (node: HTMLElement | null) => void;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Object containing the reference and floating elements.
|
|
141
|
+
*/
|
|
142
|
+
elements: {
|
|
143
|
+
reference: RT | null;
|
|
144
|
+
floating: HTMLElement | null;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
>;
|
|
148
|
+
|
|
149
|
+
/** Options for the ref-aware `arrow` middleware (accepts an octane ref or an element). */
|
|
150
|
+
export interface ArrowOptions {
|
|
151
|
+
/**
|
|
152
|
+
* The arrow element to be positioned.
|
|
153
|
+
* @default undefined
|
|
154
|
+
*/
|
|
155
|
+
element: MutableRefObject<Element | null> | Element | null;
|
|
156
|
+
/**
|
|
157
|
+
* The padding between the arrow element and the floating element edges.
|
|
158
|
+
* Useful when the floating element has rounded corners.
|
|
159
|
+
* @default 0
|
|
160
|
+
*/
|
|
161
|
+
padding?: Padding;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ─── Interaction context (the @floating-ui/react `useFloating` surface) ──────
|
|
165
|
+
|
|
166
|
+
export interface ExtendedRefs<RT> {
|
|
167
|
+
reference: MutableRefObject<ReferenceType | null>;
|
|
168
|
+
floating: MutableRefObject<HTMLElement | null>;
|
|
169
|
+
domReference: MutableRefObject<NarrowedElement<RT> | null>;
|
|
170
|
+
setReference(node: RT | null): void;
|
|
171
|
+
setFloating(node: HTMLElement | null): void;
|
|
172
|
+
setPositionReference(node: ReferenceType | null): void;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface ExtendedElements<RT> {
|
|
176
|
+
reference: ReferenceType | null;
|
|
177
|
+
floating: HTMLElement | null;
|
|
178
|
+
domReference: NarrowedElement<RT> | null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export type FloatingContext<RT extends ReferenceType = ReferenceType> = Omit<
|
|
182
|
+
UsePositionFloatingReturn<RT>,
|
|
183
|
+
'refs' | 'elements'
|
|
184
|
+
> & {
|
|
185
|
+
open: boolean;
|
|
186
|
+
onOpenChange(open: boolean, event?: Event, reason?: OpenChangeReason): void;
|
|
187
|
+
events: FloatingEvents;
|
|
188
|
+
dataRef: MutableRefObject<ContextData>;
|
|
189
|
+
nodeId: string | undefined;
|
|
190
|
+
floatingId: string;
|
|
191
|
+
refs: ExtendedRefs<RT>;
|
|
192
|
+
elements: ExtendedElements<RT>;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export interface FloatingRootContext<RT extends ReferenceType = ReferenceType> {
|
|
196
|
+
dataRef: MutableRefObject<ContextData>;
|
|
197
|
+
open: boolean;
|
|
198
|
+
onOpenChange: (open: boolean, event?: Event, reason?: OpenChangeReason) => void;
|
|
199
|
+
elements: {
|
|
200
|
+
domReference: Element | null;
|
|
201
|
+
reference: RT | null;
|
|
202
|
+
floating: HTMLElement | null;
|
|
203
|
+
};
|
|
204
|
+
events: FloatingEvents;
|
|
205
|
+
floatingId: string;
|
|
206
|
+
refs: {
|
|
207
|
+
setPositionReference(node: ReferenceType | null): void;
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface UseFloatingRootContextOptions {
|
|
212
|
+
open?: boolean;
|
|
213
|
+
onOpenChange?: (open: boolean, event?: Event, reason?: OpenChangeReason) => void;
|
|
214
|
+
elements: {
|
|
215
|
+
reference: Element | null;
|
|
216
|
+
floating: HTMLElement | null;
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface UseFloatingOptions<RT extends ReferenceType = ReferenceType> extends Omit<
|
|
221
|
+
UsePositionFloatingOptions<RT>,
|
|
222
|
+
'elements'
|
|
223
|
+
> {
|
|
224
|
+
rootContext?: FloatingRootContext<RT>;
|
|
225
|
+
/**
|
|
226
|
+
* Object of external elements as an alternative to the `refs` object setters.
|
|
227
|
+
*/
|
|
228
|
+
elements?: {
|
|
229
|
+
/**
|
|
230
|
+
* Externally passed reference element. Store in state.
|
|
231
|
+
*/
|
|
232
|
+
reference?: Element | null;
|
|
233
|
+
/**
|
|
234
|
+
* Externally passed floating element. Store in state.
|
|
235
|
+
*/
|
|
236
|
+
floating?: HTMLElement | null;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* An event callback that is invoked when the floating element is opened or
|
|
240
|
+
* closed.
|
|
241
|
+
*/
|
|
242
|
+
onOpenChange?(open: boolean, event?: Event, reason?: OpenChangeReason): void;
|
|
243
|
+
/**
|
|
244
|
+
* Unique node id when using `FloatingTree`.
|
|
245
|
+
*/
|
|
246
|
+
nodeId?: string;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export type UseFloatingReturn<RT extends ReferenceType = ReferenceType> = Prettify<
|
|
250
|
+
Omit<UsePositionFloatingReturn<RT>, 'refs' | 'elements'> & {
|
|
251
|
+
/**
|
|
252
|
+
* `FloatingContext`
|
|
253
|
+
*/
|
|
254
|
+
context: Prettify<FloatingContext<RT>>;
|
|
255
|
+
/**
|
|
256
|
+
* Object containing the reference and floating refs and reactive setters.
|
|
257
|
+
*/
|
|
258
|
+
refs: ExtendedRefs<RT>;
|
|
259
|
+
elements: ExtendedElements<RT>;
|
|
260
|
+
}
|
|
261
|
+
>;
|
|
262
|
+
|
|
263
|
+
export type UseFloatingData = Prettify<UseFloatingReturn>;
|
|
264
|
+
|
|
265
|
+
// ─── Interaction prop getters (useInteractions) ──────────────────────────────
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* An octane HTML prop bag: octane's JSX attribute surface (NATIVE event
|
|
269
|
+
* handlers — octane has no synthetic events) plus an open string index, since
|
|
270
|
+
* the interaction hooks and prop getters merge arbitrary keys (aria-*, data-*,
|
|
271
|
+
* ref). The octane analog of upstream's `React.HTMLProps<T>`.
|
|
272
|
+
*/
|
|
273
|
+
export type HTMLProps<T extends EventTarget = HTMLElement> = Octane.HTMLAttributes<T> & {
|
|
274
|
+
[key: string]: unknown;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
export interface ExtendedUserProps {
|
|
278
|
+
active?: boolean;
|
|
279
|
+
selected?: boolean;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface ElementProps {
|
|
283
|
+
reference?: HTMLProps<Element>;
|
|
284
|
+
floating?: HTMLProps<HTMLElement>;
|
|
285
|
+
item?: HTMLProps<HTMLElement> | ((props: ExtendedUserProps) => HTMLProps<HTMLElement>);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export interface UseInteractionsReturn {
|
|
289
|
+
// The `| object` widening keeps prop bags typed against other libraries'
|
|
290
|
+
// HTML prop shapes (e.g. React's) callable — the getters merge any object.
|
|
291
|
+
getReferenceProps: (userProps?: HTMLProps<Element> | object) => Record<string, unknown>;
|
|
292
|
+
getFloatingProps: (userProps?: HTMLProps<HTMLElement> | object) => Record<string, unknown>;
|
|
293
|
+
getItemProps: (
|
|
294
|
+
userProps?: (Omit<HTMLProps<HTMLElement>, 'selected' | 'active'> & ExtendedUserProps) | object,
|
|
295
|
+
) => Record<string, unknown>;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ─── Tree ────────────────────────────────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
export interface FloatingNodeType<RT extends ReferenceType = ReferenceType> {
|
|
301
|
+
id: string | undefined;
|
|
302
|
+
parentId: string | null;
|
|
303
|
+
context?: FloatingContext<RT>;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export interface FloatingTreeType<RT extends ReferenceType = ReferenceType> {
|
|
307
|
+
nodesRef: MutableRefObject<Array<FloatingNodeType<RT>>>;
|
|
308
|
+
events: FloatingEvents;
|
|
309
|
+
addNode(node: FloatingNodeType): void;
|
|
310
|
+
removeNode(node: FloatingNodeType): void;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ─── Shared interaction-hook types ───────────────────────────────────────────
|
|
314
|
+
|
|
315
|
+
export type Delay =
|
|
316
|
+
| number
|
|
317
|
+
| Partial<{
|
|
318
|
+
open: number;
|
|
319
|
+
close: number;
|
|
320
|
+
}>;
|
|
321
|
+
|
|
322
|
+
export interface SafePolygonOptions {
|
|
323
|
+
buffer?: number;
|
|
324
|
+
blockPointerEvents?: boolean;
|
|
325
|
+
requireIntent?: boolean;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export interface HandleCloseContext extends FloatingContext {
|
|
329
|
+
onClose: () => void;
|
|
330
|
+
tree?: FloatingTreeType | null;
|
|
331
|
+
leave?: boolean;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export interface HandleClose {
|
|
335
|
+
(context: HandleCloseContext): (event: MouseEvent) => void;
|
|
336
|
+
__options?: SafePolygonOptions;
|
|
337
|
+
}
|
package/src/useClick.ts
CHANGED
|
@@ -5,21 +5,71 @@ import { useMemo, useRef } from 'octane';
|
|
|
5
5
|
|
|
6
6
|
import { splitSlot, subSlot } from './internal';
|
|
7
7
|
import { isMouseLikePointerType, isTypeableElement } from './utils';
|
|
8
|
+
import type { ElementProps, FloatingRootContext } from './types';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
export interface UseClickProps {
|
|
11
|
+
/**
|
|
12
|
+
* Whether the Hook is enabled, including all internal Effects and event
|
|
13
|
+
* handlers.
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The type of event to use to determine a “click” with mouse input.
|
|
19
|
+
* Keyboard clicks work as normal.
|
|
20
|
+
* @default 'click'
|
|
21
|
+
*/
|
|
22
|
+
event?: 'click' | 'mousedown';
|
|
23
|
+
/**
|
|
24
|
+
* Whether to toggle the open state with repeated clicks.
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
toggle?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Whether to ignore the logic for mouse input (for example, if `useHover()`
|
|
30
|
+
* is also being used).
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
ignoreMouse?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Whether to add keyboard handlers (Enter and Space key functionality) for
|
|
36
|
+
* non-button elements (to open/close the floating element via keyboard
|
|
37
|
+
* “click”).
|
|
38
|
+
* @default true
|
|
39
|
+
*/
|
|
40
|
+
keyboardHandlers?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* If already open from another event such as the `useHover()` Hook,
|
|
43
|
+
* determines whether to keep the floating element open when clicking the
|
|
44
|
+
* reference element for the first time.
|
|
45
|
+
* @default true
|
|
46
|
+
*/
|
|
47
|
+
stickIfOpen?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isButtonTarget(event: Event): boolean {
|
|
10
51
|
return isHTMLElement(event.target) && event.target.tagName === 'BUTTON';
|
|
11
52
|
}
|
|
12
|
-
function isAnchorTarget(event:
|
|
53
|
+
function isAnchorTarget(event: Event): boolean {
|
|
13
54
|
return isHTMLElement(event.target) && event.target.tagName === 'A';
|
|
14
55
|
}
|
|
15
|
-
function isSpaceIgnored(element:
|
|
56
|
+
function isSpaceIgnored(element: Element | null): boolean {
|
|
16
57
|
return isTypeableElement(element);
|
|
17
58
|
}
|
|
18
59
|
|
|
19
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Opens or closes the floating element when clicking the reference element.
|
|
62
|
+
* @see https://floating-ui.com/docs/useClick
|
|
63
|
+
*/
|
|
64
|
+
export function useClick(
|
|
65
|
+
context: FloatingRootContext,
|
|
66
|
+
props?: UseClickProps,
|
|
67
|
+
slot?: symbol,
|
|
68
|
+
): ElementProps;
|
|
69
|
+
export function useClick(...args: any[]): ElementProps {
|
|
20
70
|
const [user, slot] = splitSlot(args);
|
|
21
|
-
const context = user[0];
|
|
22
|
-
const props = (user[1] as
|
|
71
|
+
const context = user[0] as FloatingRootContext;
|
|
72
|
+
const props = (user[1] as UseClickProps) ?? {};
|
|
23
73
|
|
|
24
74
|
const open = context.open;
|
|
25
75
|
const onOpenChange = context.onOpenChange;
|
|
@@ -33,15 +83,15 @@ export function useClick(...args: any[]): any {
|
|
|
33
83
|
const keyboardHandlers = props.keyboardHandlers ?? true;
|
|
34
84
|
const stickIfOpen = props.stickIfOpen ?? true;
|
|
35
85
|
|
|
36
|
-
const pointerTypeRef = useRef<
|
|
86
|
+
const pointerTypeRef = useRef<string | undefined>(undefined, subSlot(slot, 'ptype'));
|
|
37
87
|
const didKeyDownRef = useRef(false, subSlot(slot, 'kd'));
|
|
38
88
|
|
|
39
89
|
const reference = useMemo(
|
|
40
90
|
() => ({
|
|
41
|
-
onPointerDown(event:
|
|
91
|
+
onPointerDown(event: PointerEvent) {
|
|
42
92
|
pointerTypeRef.current = event.pointerType;
|
|
43
93
|
},
|
|
44
|
-
onMouseDown(event:
|
|
94
|
+
onMouseDown(event: MouseEvent) {
|
|
45
95
|
const pointerType = pointerTypeRef.current;
|
|
46
96
|
if (event.button !== 0) return;
|
|
47
97
|
if (eventOption === 'click') return;
|
|
@@ -59,7 +109,7 @@ export function useClick(...args: any[]): any {
|
|
|
59
109
|
onOpenChange(true, event, 'click');
|
|
60
110
|
}
|
|
61
111
|
},
|
|
62
|
-
onClick(event:
|
|
112
|
+
onClick(event: MouseEvent) {
|
|
63
113
|
const pointerType = pointerTypeRef.current;
|
|
64
114
|
if (eventOption === 'mousedown' && pointerTypeRef.current) {
|
|
65
115
|
pointerTypeRef.current = undefined;
|
|
@@ -78,7 +128,7 @@ export function useClick(...args: any[]): any {
|
|
|
78
128
|
onOpenChange(true, event, 'click');
|
|
79
129
|
}
|
|
80
130
|
},
|
|
81
|
-
onKeyDown(event:
|
|
131
|
+
onKeyDown(event: KeyboardEvent) {
|
|
82
132
|
pointerTypeRef.current = undefined;
|
|
83
133
|
if (event.defaultPrevented || !keyboardHandlers || isButtonTarget(event)) {
|
|
84
134
|
return;
|
|
@@ -98,7 +148,7 @@ export function useClick(...args: any[]): any {
|
|
|
98
148
|
}
|
|
99
149
|
}
|
|
100
150
|
},
|
|
101
|
-
onKeyUp(event:
|
|
151
|
+
onKeyUp(event: KeyboardEvent) {
|
|
102
152
|
if (
|
|
103
153
|
event.defaultPrevented ||
|
|
104
154
|
!keyboardHandlers ||
|
|
@@ -131,7 +181,7 @@ export function useClick(...args: any[]): any {
|
|
|
131
181
|
subSlot(slot, 'm:ref'),
|
|
132
182
|
);
|
|
133
183
|
|
|
134
|
-
return useMemo(
|
|
184
|
+
return useMemo<ElementProps>(
|
|
135
185
|
() => (enabled ? { reference } : {}),
|
|
136
186
|
[enabled, reference],
|
|
137
187
|
subSlot(slot, 'm:ret'),
|
package/src/useClientPoint.ts
CHANGED
|
@@ -11,6 +11,33 @@ import {
|
|
|
11
11
|
useEffectEvent,
|
|
12
12
|
useModernLayoutEffect,
|
|
13
13
|
} from './utils';
|
|
14
|
+
import type { ElementProps, FloatingRootContext } from './types';
|
|
15
|
+
|
|
16
|
+
export interface UseClientPointProps {
|
|
17
|
+
/**
|
|
18
|
+
* Whether the Hook is enabled, including all internal Effects and event
|
|
19
|
+
* handlers.
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
enabled?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Whether to restrict the client point to an axis and use the reference
|
|
25
|
+
* element (if it exists) as the other axis. This can be useful if the
|
|
26
|
+
* floating element is also interactive.
|
|
27
|
+
* @default 'both'
|
|
28
|
+
*/
|
|
29
|
+
axis?: 'x' | 'y' | 'both';
|
|
30
|
+
/**
|
|
31
|
+
* An explicitly defined `x` client coordinate.
|
|
32
|
+
* @default null
|
|
33
|
+
*/
|
|
34
|
+
x?: number | null;
|
|
35
|
+
/**
|
|
36
|
+
* An explicitly defined `y` client coordinate.
|
|
37
|
+
* @default null
|
|
38
|
+
*/
|
|
39
|
+
y?: number | null;
|
|
40
|
+
}
|
|
14
41
|
|
|
15
42
|
function createVirtualElement(domElement: any, data: any): any {
|
|
16
43
|
let offsetX: number | null = null;
|
|
@@ -57,10 +84,20 @@ function isMouseBasedEvent(event: any): boolean {
|
|
|
57
84
|
return event != null && event.clientX != null;
|
|
58
85
|
}
|
|
59
86
|
|
|
60
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Positions the floating element relative to a client point (in the viewport),
|
|
89
|
+
* such as the mouse position. By default, it follows the mouse cursor.
|
|
90
|
+
* @see https://floating-ui.com/docs/useClientPoint
|
|
91
|
+
*/
|
|
92
|
+
export function useClientPoint(
|
|
93
|
+
context: FloatingRootContext,
|
|
94
|
+
props?: UseClientPointProps,
|
|
95
|
+
slot?: symbol,
|
|
96
|
+
): ElementProps;
|
|
97
|
+
export function useClientPoint(...args: any[]): ElementProps {
|
|
61
98
|
const [user, slot] = splitSlot(args);
|
|
62
|
-
const context = user[0];
|
|
63
|
-
const props = (user[1] as
|
|
99
|
+
const context = user[0] as FloatingRootContext;
|
|
100
|
+
const props = (user[1] as UseClientPointProps) ?? {};
|
|
64
101
|
|
|
65
102
|
const open = context.open;
|
|
66
103
|
const dataRef = context.dataRef;
|
|
@@ -74,12 +111,15 @@ export function useClientPoint(...args: any[]): any {
|
|
|
74
111
|
const y = props.y ?? null;
|
|
75
112
|
|
|
76
113
|
const initialRef = useRef(false, subSlot(slot, 'init'));
|
|
77
|
-
const cleanupListenerRef = useRef<
|
|
78
|
-
const [pointerType, setPointerType] = useState<
|
|
114
|
+
const cleanupListenerRef = useRef<(() => void) | null>(null, subSlot(slot, 'cleanup'));
|
|
115
|
+
const [pointerType, setPointerType] = useState<string | undefined>(
|
|
116
|
+
undefined,
|
|
117
|
+
subSlot(slot, 'ptype'),
|
|
118
|
+
);
|
|
79
119
|
const [reactive, setReactive] = useState<any[]>([], subSlot(slot, 'reactive'));
|
|
80
120
|
|
|
81
121
|
const setReference = useEffectEvent(
|
|
82
|
-
(px: number, py: number) => {
|
|
122
|
+
(px: number | null, py: number | null) => {
|
|
83
123
|
if (initialRef.current) return;
|
|
84
124
|
if (dataRef.current.openEvent && !isMouseBasedEvent(dataRef.current.openEvent)) {
|
|
85
125
|
return;
|
|
@@ -92,7 +132,7 @@ export function useClientPoint(...args: any[]): any {
|
|
|
92
132
|
);
|
|
93
133
|
|
|
94
134
|
const handleReferenceEnterOrMove = useEffectEvent(
|
|
95
|
-
(event:
|
|
135
|
+
(event: MouseEvent) => {
|
|
96
136
|
if (x != null || y != null) return;
|
|
97
137
|
if (!open) {
|
|
98
138
|
setReference(event.clientX, event.clientY);
|
|
@@ -108,7 +148,7 @@ export function useClientPoint(...args: any[]): any {
|
|
|
108
148
|
() => {
|
|
109
149
|
if (!openCheck || !enabled || x != null || y != null) return;
|
|
110
150
|
const win = getWindow(floating);
|
|
111
|
-
function handleMouseMove(event:
|
|
151
|
+
function handleMouseMove(event: MouseEvent) {
|
|
112
152
|
const target = getTarget(event);
|
|
113
153
|
if (!contains(floating, target as any)) {
|
|
114
154
|
setReference(event.clientX, event.clientY);
|
|
@@ -170,7 +210,7 @@ export function useClientPoint(...args: any[]): any {
|
|
|
170
210
|
|
|
171
211
|
const reference = useMemo(
|
|
172
212
|
() => {
|
|
173
|
-
function setPointerTypeRef(_ref:
|
|
213
|
+
function setPointerTypeRef(_ref: PointerEvent) {
|
|
174
214
|
const { pointerType: pt } = _ref;
|
|
175
215
|
setPointerType(pt);
|
|
176
216
|
}
|
|
@@ -185,7 +225,7 @@ export function useClientPoint(...args: any[]): any {
|
|
|
185
225
|
subSlot(slot, 'm:ref'),
|
|
186
226
|
);
|
|
187
227
|
|
|
188
|
-
return useMemo(
|
|
228
|
+
return useMemo<ElementProps>(
|
|
189
229
|
() => (enabled ? { reference } : {}),
|
|
190
230
|
[enabled, reference],
|
|
191
231
|
subSlot(slot, 'm:ret'),
|