@actview/floating-ui 0.1.0

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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/dist/_deprecated-inner.d.ts +82 -0
  3. package/dist/components/Composite.d.ts +109 -0
  4. package/dist/components/FloatingArrow.d.ts +66 -0
  5. package/dist/components/FloatingDelayGroup.d.ts +70 -0
  6. package/dist/components/FloatingFocusManager.d.ts +92 -0
  7. package/dist/components/FloatingList.d.ts +43 -0
  8. package/dist/components/FloatingOverlay.d.ts +18 -0
  9. package/dist/components/FloatingPortal.d.ts +63 -0
  10. package/dist/components/FloatingTree.d.ts +45 -0
  11. package/dist/components/FocusGuard.d.ts +19 -0
  12. package/dist/components/NextFloatingDelayGroup.d.ts +57 -0
  13. package/dist/floating-ui.actview.d.mts +37 -0
  14. package/dist/floating-ui.actview.d.ts +37 -0
  15. package/dist/floating-ui.actview.esm.js +6688 -0
  16. package/dist/floating-ui.actview.mjs +6688 -0
  17. package/dist/floating-ui.actview.umd.js +6772 -0
  18. package/dist/floating-ui.actview.umd.min.js +1 -0
  19. package/dist/floating-ui.actview.utils.d.mts +7 -0
  20. package/dist/floating-ui.actview.utils.d.ts +7 -0
  21. package/dist/floating-ui.actview.utils.esm.js +587 -0
  22. package/dist/floating-ui.actview.utils.mjs +587 -0
  23. package/dist/floating-ui.actview.utils.umd.js +634 -0
  24. package/dist/floating-ui.actview.utils.umd.min.js +1 -0
  25. package/dist/hooks/gridNavigation.d.ts +39 -0
  26. package/dist/hooks/useClick.d.ts +45 -0
  27. package/dist/hooks/useClientPoint.d.ts +32 -0
  28. package/dist/hooks/useDismiss.d.ts +80 -0
  29. package/dist/hooks/useFloating.d.ts +18 -0
  30. package/dist/hooks/useFloatingRootContext.d.ts +22 -0
  31. package/dist/hooks/useFocus.d.ts +21 -0
  32. package/dist/hooks/useHover.d.ts +57 -0
  33. package/dist/hooks/useId.d.ts +11 -0
  34. package/dist/hooks/useInteractions.d.ts +13 -0
  35. package/dist/hooks/useListNavigation.d.ts +176 -0
  36. package/dist/hooks/useMergeRefs.d.ts +20 -0
  37. package/dist/hooks/useRole.d.ts +33 -0
  38. package/dist/hooks/useTransition.d.ts +68 -0
  39. package/dist/hooks/useTypeahead.d.ts +66 -0
  40. package/dist/index.d.ts +37 -0
  41. package/dist/safePolygon.d.ts +12 -0
  42. package/dist/types.d.ts +140 -0
  43. package/dist/useFloating.d.ts +57 -0
  44. package/dist/utils/clearTimeoutIfSet.d.ts +2 -0
  45. package/dist/utils/composite.d.ts +41 -0
  46. package/dist/utils/constants.d.ts +8 -0
  47. package/dist/utils/createAttribute.d.ts +1 -0
  48. package/dist/utils/createEventEmitter.d.ts +5 -0
  49. package/dist/utils/deepEqual.d.ts +1 -0
  50. package/dist/utils/element.d.ts +10 -0
  51. package/dist/utils/enqueueFocus.d.ts +8 -0
  52. package/dist/utils/event.d.ts +10 -0
  53. package/dist/utils/getDPR.d.ts +1 -0
  54. package/dist/utils/hooks.d.ts +16 -0
  55. package/dist/utils/log.d.ts +2 -0
  56. package/dist/utils/markOthers.d.ts +4 -0
  57. package/dist/utils/nodes.d.ts +4 -0
  58. package/dist/utils/platform.d.ts +6 -0
  59. package/dist/utils/roundByDPR.d.ts +1 -0
  60. package/dist/utils/safeReact.d.ts +8 -0
  61. package/dist/utils/tabbable.d.ts +13 -0
  62. package/dist/utils/useLiteMergeRefs.d.ts +14 -0
  63. package/dist/utils.d.ts +7 -0
  64. package/package.json +83 -0
@@ -0,0 +1,634 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@actview/core'), require('@floating-ui/utils'), require('tabbable')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@actview/core', '@floating-ui/utils', 'tabbable'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.FloatingUIActviewUtils = {}, global.ActviewCore, global.FloatingUIUtils, global.tabbable));
5
+ })(this, (function (exports, core, utils, tabbable) { 'use strict';
6
+
7
+ function hasWindow() {
8
+ return typeof window !== 'undefined';
9
+ }
10
+ function getWindow(node) {
11
+ var _node$ownerDocument;
12
+ return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
13
+ }
14
+ function isHTMLElement(value) {
15
+ if (!hasWindow()) {
16
+ return false;
17
+ }
18
+ return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;
19
+ }
20
+ function isShadowRoot(value) {
21
+ if (!hasWindow() || typeof ShadowRoot === 'undefined') {
22
+ return false;
23
+ }
24
+ return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;
25
+ }
26
+
27
+ // Avoid Chrome DevTools blue warning.
28
+ function getPlatform() {
29
+ const uaData = navigator.userAgentData;
30
+ if (uaData != null && uaData.platform) {
31
+ return uaData.platform;
32
+ }
33
+ return navigator.platform;
34
+ }
35
+ function getUserAgent() {
36
+ const uaData = navigator.userAgentData;
37
+ if (uaData && Array.isArray(uaData.brands)) {
38
+ return uaData.brands.map(_ref => {
39
+ let {
40
+ brand,
41
+ version
42
+ } = _ref;
43
+ return brand + "/" + version;
44
+ }).join(' ');
45
+ }
46
+ return navigator.userAgent;
47
+ }
48
+ function isSafari() {
49
+ // Chrome DevTools does not complain about navigator.vendor
50
+ return /apple/i.test(navigator.vendor);
51
+ }
52
+ function isAndroid() {
53
+ const re = /android/i;
54
+ return re.test(getPlatform()) || re.test(getUserAgent());
55
+ }
56
+ function isMac() {
57
+ return getPlatform().toLowerCase().startsWith('mac') && !navigator.maxTouchPoints;
58
+ }
59
+ function isJSDOM() {
60
+ return getUserAgent().includes('jsdom/');
61
+ }
62
+
63
+ const FOCUSABLE_ATTRIBUTE = 'data-floating-ui-focusable';
64
+ const TYPEABLE_SELECTOR = "input:not([type='hidden']):not([disabled])," + "[contenteditable]:not([contenteditable='false']),textarea:not([disabled])";
65
+ const ARROW_LEFT = 'ArrowLeft';
66
+ const ARROW_RIGHT = 'ArrowRight';
67
+ const ARROW_UP = 'ArrowUp';
68
+ const ARROW_DOWN = 'ArrowDown';
69
+
70
+ function activeElement(doc) {
71
+ let activeElement = doc.activeElement;
72
+ while (((_activeElement = activeElement) == null || (_activeElement = _activeElement.shadowRoot) == null ? void 0 : _activeElement.activeElement) != null) {
73
+ var _activeElement;
74
+ activeElement = activeElement.shadowRoot.activeElement;
75
+ }
76
+ return activeElement;
77
+ }
78
+ function contains(parent, child) {
79
+ if (!parent || !child) {
80
+ return false;
81
+ }
82
+ const rootNode = child.getRootNode == null ? void 0 : child.getRootNode();
83
+
84
+ // First, attempt with faster native method
85
+ // `parent.contains(window)` 在真实浏览器抛 TypeError(window 不是 Node),
86
+ // jsdom 则宽容返回 false;加 Node 防护对齐两环境行为(window mousemove 等
87
+ // 事件经 getTarget 取到 window 时)。
88
+ if (child instanceof Node && parent.contains(child)) {
89
+ return true;
90
+ }
91
+
92
+ // then fallback to custom implementation with Shadow DOM support
93
+ if (rootNode && isShadowRoot(rootNode)) {
94
+ let next = child;
95
+ while (next) {
96
+ if (parent === next) {
97
+ return true;
98
+ }
99
+ // @ts-ignore
100
+ next = next.parentNode || next.host;
101
+ }
102
+ }
103
+
104
+ // Give up, the result is false
105
+ return false;
106
+ }
107
+ function getTarget(event) {
108
+ if ('composedPath' in event) {
109
+ return event.composedPath()[0];
110
+ }
111
+
112
+ // TS thinks `event` is of type never as it assumes all browsers support
113
+ // `composedPath()`, but browsers without shadow DOM don't.
114
+ return event.target;
115
+ }
116
+ function isEventTargetWithin(event, node) {
117
+ if (node == null) {
118
+ return false;
119
+ }
120
+ if ('composedPath' in event) {
121
+ return event.composedPath().includes(node);
122
+ }
123
+
124
+ // TS thinks `event` is of type never as it assumes all browsers support composedPath, but browsers without shadow dom don't
125
+ const e = event;
126
+ return e.target != null && node.contains(e.target);
127
+ }
128
+ function isRootElement(element) {
129
+ return element.matches('html,body');
130
+ }
131
+ function getDocument(node) {
132
+ return (node == null ? void 0 : node.ownerDocument) || document;
133
+ }
134
+ function isTypeableElement(element) {
135
+ return isHTMLElement(element) && element.matches(TYPEABLE_SELECTOR);
136
+ }
137
+ function isTypeableCombobox(element) {
138
+ if (!element) return false;
139
+ return element.getAttribute('role') === 'combobox' && isTypeableElement(element);
140
+ }
141
+ function matchesFocusVisible(element) {
142
+ // We don't want to block focus from working with `visibleOnly`
143
+ // (JSDOM doesn't match `:focus-visible` when the element has `:focus`)
144
+ if (!element || isJSDOM()) return true;
145
+ try {
146
+ return element.matches(':focus-visible');
147
+ } catch (_e) {
148
+ return true;
149
+ }
150
+ }
151
+ function getFloatingFocusElement(floatingElement) {
152
+ if (!floatingElement) {
153
+ return null;
154
+ }
155
+ // Try to find the element that has `{...getFloatingProps()}` spread on it.
156
+ // This indicates the floating element is acting as a positioning wrapper, and
157
+ // so focus should be managed on the child element with the event handlers and
158
+ // aria props.
159
+ return floatingElement.hasAttribute(FOCUSABLE_ATTRIBUTE) ? floatingElement : floatingElement.querySelector("[" + FOCUSABLE_ATTRIBUTE + "]") || floatingElement;
160
+ }
161
+
162
+ function getNodeChildren(nodes, id, onlyOpenChildren) {
163
+ if (onlyOpenChildren === void 0) {
164
+ onlyOpenChildren = true;
165
+ }
166
+ const directChildren = nodes.filter(node => {
167
+ var _node$context;
168
+ return node.parentId === id && (!onlyOpenChildren || ((_node$context = node.context) == null ? void 0 : _node$context.open.value));
169
+ });
170
+ return directChildren.flatMap(child => [child, ...getNodeChildren(nodes, child.id, onlyOpenChildren)]);
171
+ }
172
+ function getDeepestNode(nodes, id) {
173
+ let deepestNodeId;
174
+ let maxDepth = -1;
175
+ function findDeepest(nodeId, depth) {
176
+ if (depth > maxDepth) {
177
+ deepestNodeId = nodeId;
178
+ maxDepth = depth;
179
+ }
180
+ const children = getNodeChildren(nodes, nodeId);
181
+ children.forEach(child => {
182
+ findDeepest(child.id, depth + 1);
183
+ });
184
+ }
185
+ findDeepest(id, 0);
186
+ return nodes.find(node => node.id === deepestNodeId);
187
+ }
188
+ function getNodeAncestors(nodes, id) {
189
+ var _nodes$find;
190
+ let allAncestors = [];
191
+ let currentParentId = (_nodes$find = nodes.find(node => node.id === id)) == null ? void 0 : _nodes$find.parentId;
192
+ while (currentParentId) {
193
+ const currentNode = nodes.find(node => node.id === currentParentId);
194
+ currentParentId = currentNode == null ? void 0 : currentNode.parentId;
195
+ if (currentNode) {
196
+ allAncestors = allAncestors.concat(currentNode);
197
+ }
198
+ }
199
+ return allAncestors;
200
+ }
201
+
202
+ // actview: `React.SyntheticEvent` → 结构类型(actview 事件是原生 Event)
203
+
204
+ function stopEvent(event) {
205
+ event.preventDefault();
206
+ event.stopPropagation();
207
+ }
208
+ function isReactEvent(event) {
209
+ return 'nativeEvent' in event;
210
+ }
211
+
212
+ // License: https://github.com/adobe/react-spectrum/blob/b35d5c02fe900badccd0cf1a8f23bb593419f238/packages/@react-aria/utils/src/isVirtualEvent.ts
213
+ function isVirtualClick(event) {
214
+ // FIXME: Firefox is now emitting a deprecation warning for `mozInputSource`.
215
+ // Try to find a workaround for this. `react-aria` source still has the check.
216
+ if (event.mozInputSource === 0 && event.isTrusted) {
217
+ return true;
218
+ }
219
+ if (isAndroid() && event.pointerType) {
220
+ return event.type === 'click' && event.buttons === 1;
221
+ }
222
+ return event.detail === 0 && !event.pointerType;
223
+ }
224
+ function isVirtualPointerEvent(event) {
225
+ if (isJSDOM()) return false;
226
+ return !isAndroid() && event.width === 0 && event.height === 0 || isAndroid() && event.width === 1 && event.height === 1 && event.pressure === 0 && event.detail === 0 && event.pointerType === 'mouse' ||
227
+ // iOS VoiceOver returns 0.333• for width/height.
228
+ event.width < 1 && event.height < 1 && event.pressure === 0 && event.detail === 0 && event.pointerType === 'touch';
229
+ }
230
+ function isMouseLikePointerType(pointerType, strict) {
231
+ // On some Linux machines with Chromium, mouse inputs return a `pointerType`
232
+ // of "pen": https://github.com/floating-ui/floating-ui/issues/2015
233
+ const values = ['mouse', 'pen'];
234
+ if (!strict) {
235
+ values.push('', undefined);
236
+ }
237
+ return values.includes(pointerType);
238
+ }
239
+
240
+ /**
241
+ * actview 版(upstream 为 React hooks:useLatestRef / useEffectEvent)。
242
+ *
243
+ * 与 upstream 的差异:
244
+ * - ref 为 actview 框架类型 `Ref<T>`(.value,非 React 的 .current),创建用 `ref()`
245
+ * - actview 无渲染循环与 effect 概念(setup 只执行一次、render 闭包每次渲染执行)——
246
+ * `useLatestRef` 每次调用返回新的 `ref()`(引用稳定性由调用方负责)
247
+ * - `useEffectEvent` 无 useInsertionEffect 概念:回调在调用时同步写入 ref,
248
+ * 返回的稳定闭包始终调用最新回调。
249
+ * - `useModernLayoutEffect` 不再导出(React 专属,actview 无 layout effect)。
250
+ */
251
+ function useLatestRef(value) {
252
+ const r = core.ref(value);
253
+ return r;
254
+ }
255
+ function useEffectEvent(callback) {
256
+ const r = core.ref(callback);
257
+ return function () {
258
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
259
+ args[_key] = arguments[_key];
260
+ }
261
+ return r.value == null ? void 0 : r.value(...args);
262
+ };
263
+ }
264
+
265
+ // actview: `React.MutableRefObject<T>` → actview 框架类型 `Ref<T>`(.value)、
266
+ // `React.KeyboardEvent` → `KeyboardEventLike`(结构类型,无 React 依赖)
267
+
268
+ function isDifferentGridRow(index, cols, prevRow) {
269
+ return Math.floor(index / cols) !== prevRow;
270
+ }
271
+ function isIndexOutOfListBounds(listRef, index) {
272
+ return index < 0 || index >= listRef.value.length;
273
+ }
274
+ function getMinListIndex(listRef, disabledIndices) {
275
+ return findNonDisabledListIndex(listRef, {
276
+ disabledIndices
277
+ });
278
+ }
279
+ function getMaxListIndex(listRef, disabledIndices) {
280
+ return findNonDisabledListIndex(listRef, {
281
+ decrement: true,
282
+ startingIndex: listRef.value.length,
283
+ disabledIndices
284
+ });
285
+ }
286
+ function findNonDisabledListIndex(listRef, _temp) {
287
+ let {
288
+ startingIndex = -1,
289
+ decrement = false,
290
+ disabledIndices,
291
+ amount = 1
292
+ } = _temp === void 0 ? {} : _temp;
293
+ let index = startingIndex;
294
+ do {
295
+ index += decrement ? -amount : amount;
296
+ } while (index >= 0 && index <= listRef.value.length - 1 && isListIndexDisabled(listRef, index, disabledIndices));
297
+ return index;
298
+ }
299
+ function getGridNavigatedIndex(listRef, _ref) {
300
+ let {
301
+ event,
302
+ orientation,
303
+ loop,
304
+ rtl,
305
+ cols,
306
+ disabledIndices,
307
+ minIndex,
308
+ maxIndex,
309
+ prevIndex,
310
+ stopEvent: stop = false
311
+ } = _ref;
312
+ let nextIndex = prevIndex;
313
+ if (event.key === ARROW_UP) {
314
+ stop && stopEvent(event);
315
+ if (prevIndex === -1) {
316
+ nextIndex = maxIndex;
317
+ } else {
318
+ nextIndex = findNonDisabledListIndex(listRef, {
319
+ startingIndex: nextIndex,
320
+ amount: cols,
321
+ decrement: true,
322
+ disabledIndices
323
+ });
324
+ if (loop && (prevIndex - cols < minIndex || nextIndex < 0)) {
325
+ const col = prevIndex % cols;
326
+ const maxCol = maxIndex % cols;
327
+ const offset = maxIndex - (maxCol - col);
328
+ if (maxCol === col) {
329
+ nextIndex = maxIndex;
330
+ } else {
331
+ nextIndex = maxCol > col ? offset : offset - cols;
332
+ }
333
+ }
334
+ }
335
+ if (isIndexOutOfListBounds(listRef, nextIndex)) {
336
+ nextIndex = prevIndex;
337
+ }
338
+ }
339
+ if (event.key === ARROW_DOWN) {
340
+ stop && stopEvent(event);
341
+ if (prevIndex === -1) {
342
+ nextIndex = minIndex;
343
+ } else {
344
+ nextIndex = findNonDisabledListIndex(listRef, {
345
+ startingIndex: prevIndex,
346
+ amount: cols,
347
+ disabledIndices
348
+ });
349
+ if (loop && prevIndex + cols > maxIndex) {
350
+ nextIndex = findNonDisabledListIndex(listRef, {
351
+ startingIndex: prevIndex % cols - cols,
352
+ amount: cols,
353
+ disabledIndices
354
+ });
355
+ }
356
+ }
357
+ if (isIndexOutOfListBounds(listRef, nextIndex)) {
358
+ nextIndex = prevIndex;
359
+ }
360
+ }
361
+
362
+ // Remains on the same row/column.
363
+ if (orientation === 'both') {
364
+ const prevRow = utils.floor(prevIndex / cols);
365
+ if (event.key === (rtl ? ARROW_LEFT : ARROW_RIGHT)) {
366
+ stop && stopEvent(event);
367
+ if (prevIndex % cols !== cols - 1) {
368
+ nextIndex = findNonDisabledListIndex(listRef, {
369
+ startingIndex: prevIndex,
370
+ disabledIndices
371
+ });
372
+ if (loop && isDifferentGridRow(nextIndex, cols, prevRow)) {
373
+ nextIndex = findNonDisabledListIndex(listRef, {
374
+ startingIndex: prevIndex - prevIndex % cols - 1,
375
+ disabledIndices
376
+ });
377
+ }
378
+ } else if (loop) {
379
+ nextIndex = findNonDisabledListIndex(listRef, {
380
+ startingIndex: prevIndex - prevIndex % cols - 1,
381
+ disabledIndices
382
+ });
383
+ }
384
+ if (isDifferentGridRow(nextIndex, cols, prevRow)) {
385
+ nextIndex = prevIndex;
386
+ }
387
+ }
388
+ if (event.key === (rtl ? ARROW_RIGHT : ARROW_LEFT)) {
389
+ stop && stopEvent(event);
390
+ if (prevIndex % cols !== 0) {
391
+ nextIndex = findNonDisabledListIndex(listRef, {
392
+ startingIndex: prevIndex,
393
+ decrement: true,
394
+ disabledIndices
395
+ });
396
+ if (loop && isDifferentGridRow(nextIndex, cols, prevRow)) {
397
+ nextIndex = findNonDisabledListIndex(listRef, {
398
+ startingIndex: prevIndex + (cols - prevIndex % cols),
399
+ decrement: true,
400
+ disabledIndices
401
+ });
402
+ }
403
+ } else if (loop) {
404
+ nextIndex = findNonDisabledListIndex(listRef, {
405
+ startingIndex: prevIndex + (cols - prevIndex % cols),
406
+ decrement: true,
407
+ disabledIndices
408
+ });
409
+ }
410
+ if (isDifferentGridRow(nextIndex, cols, prevRow)) {
411
+ nextIndex = prevIndex;
412
+ }
413
+ }
414
+ const lastRow = utils.floor(maxIndex / cols) === prevRow;
415
+ if (isIndexOutOfListBounds(listRef, nextIndex)) {
416
+ if (loop && lastRow) {
417
+ nextIndex = event.key === (rtl ? ARROW_RIGHT : ARROW_LEFT) ? maxIndex : findNonDisabledListIndex(listRef, {
418
+ startingIndex: prevIndex - prevIndex % cols - 1,
419
+ disabledIndices
420
+ });
421
+ } else {
422
+ nextIndex = prevIndex;
423
+ }
424
+ }
425
+ }
426
+ return nextIndex;
427
+ }
428
+
429
+ /** For each cell index, gets the item index that occupies that cell */
430
+ function createGridCellMap(sizes, cols, dense) {
431
+ const cellMap = [];
432
+ let startIndex = 0;
433
+ sizes.forEach((_ref2, index) => {
434
+ let {
435
+ width,
436
+ height
437
+ } = _ref2;
438
+ if (width > cols) {
439
+ {
440
+ throw new Error("[Floating UI]: Invalid grid - item width at index " + index + " is greater than grid columns");
441
+ }
442
+ }
443
+ let itemPlaced = false;
444
+ if (dense) {
445
+ startIndex = 0;
446
+ }
447
+ while (!itemPlaced) {
448
+ const targetCells = [];
449
+ for (let i = 0; i < width; i++) {
450
+ for (let j = 0; j < height; j++) {
451
+ targetCells.push(startIndex + i + j * cols);
452
+ }
453
+ }
454
+ if (startIndex % cols + width <= cols && targetCells.every(cell => cellMap[cell] == null)) {
455
+ targetCells.forEach(cell => {
456
+ cellMap[cell] = index;
457
+ });
458
+ itemPlaced = true;
459
+ } else {
460
+ startIndex++;
461
+ }
462
+ }
463
+ });
464
+
465
+ // convert into a non-sparse array
466
+ return [...cellMap];
467
+ }
468
+
469
+ /** Gets cell index of an item's corner or -1 when index is -1. */
470
+ function getGridCellIndexOfCorner(index, sizes, cellMap, cols, corner) {
471
+ if (index === -1) return -1;
472
+ const firstCellIndex = cellMap.indexOf(index);
473
+ const sizeItem = sizes[index];
474
+ switch (corner) {
475
+ case 'tl':
476
+ return firstCellIndex;
477
+ case 'tr':
478
+ if (!sizeItem) {
479
+ return firstCellIndex;
480
+ }
481
+ return firstCellIndex + sizeItem.width - 1;
482
+ case 'bl':
483
+ if (!sizeItem) {
484
+ return firstCellIndex;
485
+ }
486
+ return firstCellIndex + (sizeItem.height - 1) * cols;
487
+ case 'br':
488
+ return cellMap.lastIndexOf(index);
489
+ }
490
+ }
491
+
492
+ /** Gets all cell indices that correspond to the specified indices */
493
+ function getGridCellIndices(indices, cellMap) {
494
+ return cellMap.flatMap((index, cellIndex) => indices.includes(index) ? [cellIndex] : []);
495
+ }
496
+ function isListIndexDisabled(listRef, index, disabledIndices) {
497
+ var _disabledIndices$incl;
498
+ const isExplicitlyDisabled = typeof disabledIndices === 'function' ? disabledIndices(index) : (_disabledIndices$incl = disabledIndices == null ? void 0 : disabledIndices.includes(index)) != null ? _disabledIndices$incl : false;
499
+ if (isExplicitlyDisabled) {
500
+ return true;
501
+ }
502
+ const element = listRef.value[index];
503
+ if (!element) {
504
+ return false;
505
+ }
506
+ if (!isElementVisible(element)) {
507
+ return true;
508
+ }
509
+
510
+ // A natively disabled element can never receive focus, so it must always be
511
+ // skipped, even when `disabledIndices` marks it as enabled. Only
512
+ // `aria-disabled` items can be focusable-while-disabled.
513
+ if (element.matches(':disabled')) {
514
+ return true;
515
+ }
516
+
517
+ // base-ui 语义:只有未显式传入 disabledIndices 时才检查属性
518
+ // (disabled/aria-disabled)。initial sync 故意不传 disabledIndices
519
+ // (mui/base-ui#2604),使属性禁用的项在打开时也被跳过;导航时传入
520
+ // disabledIndices 数组,由数组控制(aria-disabled 项可聚焦)。
521
+ return !disabledIndices && (element.hasAttribute('disabled') || element.getAttribute('aria-disabled') === 'true');
522
+ }
523
+ function isHiddenByStyles(styles) {
524
+ return styles.visibility === 'hidden' || styles.visibility === 'collapse';
525
+ }
526
+ function isElementVisible(element, styles) {
527
+ if (styles === void 0) {
528
+ styles = element ? getComputedStyle(element) : null;
529
+ }
530
+ if (!element || !element.isConnected || !styles || isHiddenByStyles(styles)) {
531
+ return false;
532
+ }
533
+ if (typeof element.checkVisibility === 'function') {
534
+ return element.checkVisibility();
535
+ }
536
+ return styles.display !== 'none' && styles.display !== 'contents';
537
+ }
538
+
539
+ const getTabbableOptions = () => ({
540
+ getShadowRoot: true,
541
+ displayCheck:
542
+ // JSDOM does not support the `tabbable` library. To solve this we can
543
+ // check if `ResizeObserver` is a real function (not polyfilled), which
544
+ // determines if the current environment is JSDOM-like.
545
+ typeof ResizeObserver === 'function' && ResizeObserver.toString().includes('[native code]') ? 'full' : 'none'
546
+ });
547
+ function getTabbableIn(container, dir) {
548
+ const list = tabbable.tabbable(container, getTabbableOptions());
549
+ const len = list.length;
550
+ if (len === 0) return;
551
+ const active = activeElement(getDocument(container));
552
+ const index = list.indexOf(active);
553
+ const nextIndex = index === -1 ? dir === 1 ? 0 : len - 1 : index + dir;
554
+ return list[nextIndex];
555
+ }
556
+ function getNextTabbable(referenceElement) {
557
+ return getTabbableIn(getDocument(referenceElement).body, 1) || referenceElement;
558
+ }
559
+ function getPreviousTabbable(referenceElement) {
560
+ return getTabbableIn(getDocument(referenceElement).body, -1) || referenceElement;
561
+ }
562
+
563
+ // actview: `React.FocusEvent` → 结构类型(actview 事件是原生 FocusEvent)
564
+ function isOutsideEvent(event, container) {
565
+ const containerElement = container || event.currentTarget;
566
+ const relatedTarget = event.relatedTarget;
567
+ return !relatedTarget || !contains(containerElement, relatedTarget);
568
+ }
569
+ function disableFocusInside(container) {
570
+ const tabbableElements = tabbable.tabbable(container, getTabbableOptions());
571
+ tabbableElements.forEach(element => {
572
+ element.dataset.tabindex = element.getAttribute('tabindex') || '';
573
+ element.setAttribute('tabindex', '-1');
574
+ });
575
+ }
576
+ function enableFocusInside(container) {
577
+ const elements = container.querySelectorAll('[data-tabindex]');
578
+ elements.forEach(element => {
579
+ const tabindex = element.dataset.tabindex;
580
+ delete element.dataset.tabindex;
581
+ if (tabindex) {
582
+ element.setAttribute('tabindex', tabindex);
583
+ } else {
584
+ element.removeAttribute('tabindex');
585
+ }
586
+ });
587
+ }
588
+
589
+ exports.activeElement = activeElement;
590
+ exports.contains = contains;
591
+ exports.createGridCellMap = createGridCellMap;
592
+ exports.disableFocusInside = disableFocusInside;
593
+ exports.enableFocusInside = enableFocusInside;
594
+ exports.findNonDisabledListIndex = findNonDisabledListIndex;
595
+ exports.getDeepestNode = getDeepestNode;
596
+ exports.getDocument = getDocument;
597
+ exports.getFloatingFocusElement = getFloatingFocusElement;
598
+ exports.getGridCellIndexOfCorner = getGridCellIndexOfCorner;
599
+ exports.getGridCellIndices = getGridCellIndices;
600
+ exports.getGridNavigatedIndex = getGridNavigatedIndex;
601
+ exports.getMaxListIndex = getMaxListIndex;
602
+ exports.getMinListIndex = getMinListIndex;
603
+ exports.getNextTabbable = getNextTabbable;
604
+ exports.getNodeAncestors = getNodeAncestors;
605
+ exports.getNodeChildren = getNodeChildren;
606
+ exports.getPlatform = getPlatform;
607
+ exports.getPreviousTabbable = getPreviousTabbable;
608
+ exports.getTabbableOptions = getTabbableOptions;
609
+ exports.getTarget = getTarget;
610
+ exports.getUserAgent = getUserAgent;
611
+ exports.isAndroid = isAndroid;
612
+ exports.isDifferentGridRow = isDifferentGridRow;
613
+ exports.isElementVisible = isElementVisible;
614
+ exports.isEventTargetWithin = isEventTargetWithin;
615
+ exports.isHiddenByStyles = isHiddenByStyles;
616
+ exports.isIndexOutOfListBounds = isIndexOutOfListBounds;
617
+ exports.isJSDOM = isJSDOM;
618
+ exports.isListIndexDisabled = isListIndexDisabled;
619
+ exports.isMac = isMac;
620
+ exports.isMouseLikePointerType = isMouseLikePointerType;
621
+ exports.isOutsideEvent = isOutsideEvent;
622
+ exports.isReactEvent = isReactEvent;
623
+ exports.isRootElement = isRootElement;
624
+ exports.isSafari = isSafari;
625
+ exports.isTypeableCombobox = isTypeableCombobox;
626
+ exports.isTypeableElement = isTypeableElement;
627
+ exports.isVirtualClick = isVirtualClick;
628
+ exports.isVirtualPointerEvent = isVirtualPointerEvent;
629
+ exports.matchesFocusVisible = matchesFocusVisible;
630
+ exports.stopEvent = stopEvent;
631
+ exports.useEffectEvent = useEffectEvent;
632
+ exports.useLatestRef = useLatestRef;
633
+
634
+ }));
@@ -0,0 +1 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@actview/core"),require("@floating-ui/utils"),require("tabbable")):"function"==typeof define&&define.amd?define(["exports","@actview/core","@floating-ui/utils","tabbable"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).FloatingUIActviewUtils={},e.ActviewCore,e.FloatingUIUtils,e.tabbable)}(this,(function(e,t,n,i){"use strict";function r(){return"undefined"!=typeof window}function o(e){var t;return(null==e||null==(t=e.ownerDocument)?void 0:t.defaultView)||window}function a(){const e=navigator.userAgentData;return null!=e&&e.platform?e.platform:navigator.platform}function s(){const e=navigator.userAgentData;return e&&Array.isArray(e.brands)?e.brands.map((e=>{let{brand:t,version:n}=e;return t+"/"+n})).join(" "):navigator.userAgent}function d(){const e=/android/i;return e.test(a())||e.test(s())}function u(){return s().includes("jsdom/")}const l="data-floating-ui-focusable",c="ArrowLeft",f="ArrowRight";function b(e){let t=e.activeElement;for(;null!=(null==(n=t)||null==(n=n.shadowRoot)?void 0:n.activeElement);){var n;t=t.shadowRoot.activeElement}return t}function g(e,t){if(!e||!t)return!1;const n=null==t.getRootNode?void 0:t.getRootNode();if(t instanceof Node&&e.contains(t))return!0;if(n&&(i=n,r()&&"undefined"!=typeof ShadowRoot&&(i instanceof ShadowRoot||i instanceof o(i).ShadowRoot))){let n=t;for(;n;){if(e===n)return!0;n=n.parentNode||n.host}}var i;return!1}function v(e){return(null==e?void 0:e.ownerDocument)||document}function h(e){return t=e,!!r()&&(t instanceof HTMLElement||t instanceof o(t).HTMLElement)&&e.matches("input:not([type='hidden']):not([disabled]),[contenteditable]:not([contenteditable='false']),textarea:not([disabled])");var t}function p(e,t,n){void 0===n&&(n=!0);return e.filter((e=>{var i;return e.parentId===t&&(!n||(null==(i=e.context)?void 0:i.open.value))})).flatMap((t=>[t,...p(e,t.id,n)]))}function m(e){e.preventDefault(),e.stopPropagation()}function I(e,t,n){return Math.floor(e/t)!==n}function x(e,t){return t<0||t>=e.value.length}function y(e,t){let{startingIndex:n=-1,decrement:i=!1,disabledIndices:r,amount:o=1}=void 0===t?{}:t,a=n;do{a+=i?-o:o}while(a>=0&&a<=e.value.length-1&&w(e,a,r));return a}function w(e,t,n){var i;if("function"==typeof n?n(t):null!=(i=null==n?void 0:n.includes(t))&&i)return!0;const r=e.value[t];return!!r&&(!E(r)||(!!r.matches(":disabled")||!n&&(r.hasAttribute("disabled")||"true"===r.getAttribute("aria-disabled"))))}function A(e){return"hidden"===e.visibility||"collapse"===e.visibility}function E(e,t){return void 0===t&&(t=e?getComputedStyle(e):null),!(!(e&&e.isConnected&&t)||A(t))&&("function"==typeof e.checkVisibility?e.checkVisibility():"none"!==t.display&&"contents"!==t.display)}const T=()=>({getShadowRoot:!0,displayCheck:"function"==typeof ResizeObserver&&ResizeObserver.toString().includes("[native code]")?"full":"none"});function R(e,t){const n=i.tabbable(e,T()),r=n.length;if(0===r)return;const o=b(v(e)),a=n.indexOf(o);return n[-1===a?1===t?0:r-1:a+t]}e.activeElement=b,e.contains=g,e.createGridCellMap=function(e,t,n){const i=[];let r=0;return e.forEach(((e,o)=>{let{width:a,height:s}=e,d=!1;for(n&&(r=0);!d;){const e=[];for(let n=0;n<a;n++)for(let i=0;i<s;i++)e.push(r+n+i*t);r%t+a<=t&&e.every((e=>null==i[e]))?(e.forEach((e=>{i[e]=o})),d=!0):r++}})),[...i]},e.disableFocusInside=function(e){i.tabbable(e,T()).forEach((e=>{e.dataset.tabindex=e.getAttribute("tabindex")||"",e.setAttribute("tabindex","-1")}))},e.enableFocusInside=function(e){e.querySelectorAll("[data-tabindex]").forEach((e=>{const t=e.dataset.tabindex;delete e.dataset.tabindex,t?e.setAttribute("tabindex",t):e.removeAttribute("tabindex")}))},e.findNonDisabledListIndex=y,e.getDeepestNode=function(e,t){let n,i=-1;return function t(r,o){o>i&&(n=r,i=o),p(e,r).forEach((e=>{t(e.id,o+1)}))}(t,0),e.find((e=>e.id===n))},e.getDocument=v,e.getFloatingFocusElement=function(e){return e?e.hasAttribute(l)?e:e.querySelector("["+l+"]")||e:null},e.getGridCellIndexOfCorner=function(e,t,n,i,r){if(-1===e)return-1;const o=n.indexOf(e),a=t[e];switch(r){case"tl":return o;case"tr":return a?o+a.width-1:o;case"bl":return a?o+(a.height-1)*i:o;case"br":return n.lastIndexOf(e)}},e.getGridCellIndices=function(e,t){return t.flatMap(((t,n)=>e.includes(t)?[n]:[]))},e.getGridNavigatedIndex=function(e,t){let{event:i,orientation:r,loop:o,rtl:a,cols:s,disabledIndices:d,minIndex:u,maxIndex:l,prevIndex:b,stopEvent:g=!1}=t,v=b;if("ArrowUp"===i.key){if(g&&m(i),-1===b)v=l;else if(v=y(e,{startingIndex:v,amount:s,decrement:!0,disabledIndices:d}),o&&(b-s<u||v<0)){const e=b%s,t=l%s,n=l-(t-e);v=t===e?l:t>e?n:n-s}x(e,v)&&(v=b)}if("ArrowDown"===i.key&&(g&&m(i),-1===b?v=u:(v=y(e,{startingIndex:b,amount:s,disabledIndices:d}),o&&b+s>l&&(v=y(e,{startingIndex:b%s-s,amount:s,disabledIndices:d}))),x(e,v)&&(v=b)),"both"===r){const t=n.floor(b/s);i.key===(a?c:f)&&(g&&m(i),b%s!=s-1?(v=y(e,{startingIndex:b,disabledIndices:d}),o&&I(v,s,t)&&(v=y(e,{startingIndex:b-b%s-1,disabledIndices:d}))):o&&(v=y(e,{startingIndex:b-b%s-1,disabledIndices:d})),I(v,s,t)&&(v=b)),i.key===(a?f:c)&&(g&&m(i),b%s!=0?(v=y(e,{startingIndex:b,decrement:!0,disabledIndices:d}),o&&I(v,s,t)&&(v=y(e,{startingIndex:b+(s-b%s),decrement:!0,disabledIndices:d}))):o&&(v=y(e,{startingIndex:b+(s-b%s),decrement:!0,disabledIndices:d})),I(v,s,t)&&(v=b));const r=n.floor(l/s)===t;x(e,v)&&(v=o&&r?i.key===(a?f:c)?l:y(e,{startingIndex:b-b%s-1,disabledIndices:d}):b)}return v},e.getMaxListIndex=function(e,t){return y(e,{decrement:!0,startingIndex:e.value.length,disabledIndices:t})},e.getMinListIndex=function(e,t){return y(e,{disabledIndices:t})},e.getNextTabbable=function(e){return R(v(e).body,1)||e},e.getNodeAncestors=function(e,t){var n;let i=[],r=null==(n=e.find((e=>e.id===t)))?void 0:n.parentId;for(;r;){const t=e.find((e=>e.id===r));r=null==t?void 0:t.parentId,t&&(i=i.concat(t))}return i},e.getNodeChildren=p,e.getPlatform=a,e.getPreviousTabbable=function(e){return R(v(e).body,-1)||e},e.getTabbableOptions=T,e.getTarget=function(e){return"composedPath"in e?e.composedPath()[0]:e.target},e.getUserAgent=s,e.isAndroid=d,e.isDifferentGridRow=I,e.isElementVisible=E,e.isEventTargetWithin=function(e,t){if(null==t)return!1;if("composedPath"in e)return e.composedPath().includes(t);const n=e;return null!=n.target&&t.contains(n.target)},e.isHiddenByStyles=A,e.isIndexOutOfListBounds=x,e.isJSDOM=u,e.isListIndexDisabled=w,e.isMac=function(){return a().toLowerCase().startsWith("mac")&&!navigator.maxTouchPoints},e.isMouseLikePointerType=function(e,t){const n=["mouse","pen"];return t||n.push("",void 0),n.includes(e)},e.isOutsideEvent=function(e,t){const n=t||e.currentTarget,i=e.relatedTarget;return!i||!g(n,i)},e.isReactEvent=function(e){return"nativeEvent"in e},e.isRootElement=function(e){return e.matches("html,body")},e.isSafari=function(){return/apple/i.test(navigator.vendor)},e.isTypeableCombobox=function(e){return!!e&&("combobox"===e.getAttribute("role")&&h(e))},e.isTypeableElement=h,e.isVirtualClick=function(e){return!(0!==e.mozInputSource||!e.isTrusted)||(d()&&e.pointerType?"click"===e.type&&1===e.buttons:0===e.detail&&!e.pointerType)},e.isVirtualPointerEvent=function(e){return!u()&&(!d()&&0===e.width&&0===e.height||d()&&1===e.width&&1===e.height&&0===e.pressure&&0===e.detail&&"mouse"===e.pointerType||e.width<1&&e.height<1&&0===e.pressure&&0===e.detail&&"touch"===e.pointerType)},e.matchesFocusVisible=function(e){if(!e||u())return!0;try{return e.matches(":focus-visible")}catch(e){return!0}},e.stopEvent=m,e.useEffectEvent=function(e){const n=t.ref(e);return function(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];return null==n.value?void 0:n.value(...t)}},e.useLatestRef=function(e){return t.ref(e)}}));
@@ -0,0 +1,39 @@
1
+ import type { Ref } from '@actview/core';
2
+ import { type DisabledIndices } from '../utils/composite';
3
+ interface KeyboardEventLike {
4
+ key: string;
5
+ which: number;
6
+ altKey: boolean;
7
+ ctrlKey: boolean;
8
+ metaKey: boolean;
9
+ shiftKey: boolean;
10
+ defaultPrevented: boolean;
11
+ }
12
+ type List = Array<HTMLElement | null>;
13
+ type ListRef = Ref<List>;
14
+ export declare function getGridNavigatedIndex(list: List, { event, orientation, loop, onLoop, rtl, cols, disabledIndices, minIndex, maxIndex, prevIndex, stopEvent: stop, }: {
15
+ event: KeyboardEventLike;
16
+ orientation: 'horizontal' | 'vertical' | 'both';
17
+ loop: boolean;
18
+ onLoop?: ((event: KeyboardEventLike, prevIndex: number, nextIndex: number) => number) | undefined;
19
+ rtl: boolean;
20
+ cols: number;
21
+ disabledIndices: DisabledIndices | undefined;
22
+ minIndex: number;
23
+ maxIndex: number;
24
+ prevIndex: number;
25
+ stopEvent?: boolean | undefined;
26
+ }): number;
27
+ /**
28
+ * Positional arguments are deliberate: property names of an options object
29
+ * don't minify, and the signature is locked to the caller via `typeof` on the
30
+ * `grid` option of `useListNavigation`.
31
+ *
32
+ * The injected grid navigator only ever operates on a uniform 1x1 grid (sizes are
33
+ * always `1x1` and packing is never dense), so the cell-map machinery that supports
34
+ * multi-cell items collapses to an identity transform over the item list. Calling
35
+ * `getGridNavigatedIndex` directly keeps the cell-map helpers out of
36
+ * grid-combobox bundles.
37
+ */
38
+ export declare function gridNavigation(event: KeyboardEventLike, prevIndex: number, listRef: ListRef, orientation: 'horizontal' | 'vertical' | 'both', loop: boolean, rtl: boolean, disabledIndices: DisabledIndices | undefined, minIndex: number, maxIndex: number, cols?: number): number | undefined;
39
+ export {};