@octanejs/floating-ui 0.1.2

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.
@@ -0,0 +1,656 @@
1
+ // Ported from @floating-ui/react/utils. The DOM/grid/tabbable helpers are
2
+ // framework-agnostic and copied ~verbatim; the three React hooks (useLatestRef,
3
+ // useEffectEvent, useModernLayoutEffect) become octane hooks that take an explicit
4
+ // slot (forwarded by their callers via subSlot — see ../internal).
5
+ import { isShadowRoot, isHTMLElement } from '@floating-ui/utils/dom';
6
+ import { floor } from '@floating-ui/utils';
7
+ import { tabbable } from 'tabbable';
8
+ import { useCallback, useLayoutEffect, useRef } from 'octane';
9
+
10
+ import { subSlot } from '../internal';
11
+
12
+ export function getPlatform(): string {
13
+ const uaData = (navigator as any).userAgentData;
14
+ if (uaData != null && uaData.platform) {
15
+ return uaData.platform;
16
+ }
17
+ return navigator.platform;
18
+ }
19
+ export function getUserAgent(): string {
20
+ const uaData = (navigator as any).userAgentData;
21
+ if (uaData && Array.isArray(uaData.brands)) {
22
+ return uaData.brands.map((b: any) => b.brand + '/' + b.version).join(' ');
23
+ }
24
+ return navigator.userAgent;
25
+ }
26
+ export function isSafari(): boolean {
27
+ return /apple/i.test(navigator.vendor);
28
+ }
29
+ export function isAndroid(): boolean {
30
+ const re = /android/i;
31
+ return re.test(getPlatform()) || re.test(getUserAgent());
32
+ }
33
+ export function isMac(): boolean {
34
+ return getPlatform().toLowerCase().startsWith('mac') && !navigator.maxTouchPoints;
35
+ }
36
+ export function isJSDOM(): boolean {
37
+ return getUserAgent().includes('jsdom/');
38
+ }
39
+ export function isMacSafari(): boolean {
40
+ return isMac() && isSafari();
41
+ }
42
+
43
+ export function createAttribute(name: string): string {
44
+ return 'data-floating-ui-' + name;
45
+ }
46
+ export function clearTimeoutIfSet(timeoutRef: { current: number }): void {
47
+ if (timeoutRef.current !== -1) {
48
+ clearTimeout(timeoutRef.current);
49
+ timeoutRef.current = -1;
50
+ }
51
+ }
52
+
53
+ export const FOCUSABLE_ATTRIBUTE = 'data-floating-ui-focusable';
54
+ const TYPEABLE_SELECTOR =
55
+ "input:not([type='hidden']):not([disabled])," +
56
+ "[contenteditable]:not([contenteditable='false']),textarea:not([disabled])";
57
+ const ARROW_LEFT = 'ArrowLeft';
58
+ const ARROW_RIGHT = 'ArrowRight';
59
+ const ARROW_UP = 'ArrowUp';
60
+ const ARROW_DOWN = 'ArrowDown';
61
+
62
+ export function activeElement(doc: Document): Element | null {
63
+ let active = doc.activeElement;
64
+ while (active?.shadowRoot?.activeElement != null) {
65
+ active = active.shadowRoot.activeElement;
66
+ }
67
+ return active;
68
+ }
69
+ export function contains(parent?: Element | null, child?: Element | null): boolean {
70
+ if (!parent || !child) {
71
+ return false;
72
+ }
73
+ const rootNode = child.getRootNode?.();
74
+ if (parent.contains(child)) {
75
+ return true;
76
+ }
77
+ if (rootNode && isShadowRoot(rootNode)) {
78
+ let next: any = child;
79
+ while (next) {
80
+ if (parent === next) {
81
+ return true;
82
+ }
83
+ next = next.parentNode || next.host;
84
+ }
85
+ }
86
+ return false;
87
+ }
88
+ export function getTarget(event: any): EventTarget | null {
89
+ if ('composedPath' in event) {
90
+ return event.composedPath()[0];
91
+ }
92
+ return event.target;
93
+ }
94
+ export function isEventTargetWithin(event: any, node: any): boolean {
95
+ if (node == null) {
96
+ return false;
97
+ }
98
+ if ('composedPath' in event) {
99
+ return event.composedPath().includes(node);
100
+ }
101
+ const e = event;
102
+ return e.target != null && node.contains(e.target);
103
+ }
104
+ export function isRootElement(element: Element): boolean {
105
+ return element.matches('html,body');
106
+ }
107
+ export function getDocument(node: any): Document {
108
+ return node?.ownerDocument || document;
109
+ }
110
+ export function isTypeableElement(element: any): boolean {
111
+ return isHTMLElement(element) && element.matches(TYPEABLE_SELECTOR);
112
+ }
113
+ export function isTypeableCombobox(element: any): boolean {
114
+ if (!element) return false;
115
+ return element.getAttribute('role') === 'combobox' && isTypeableElement(element);
116
+ }
117
+ export function matchesFocusVisible(element: any): boolean {
118
+ if (!element || isJSDOM()) return true;
119
+ try {
120
+ return element.matches(':focus-visible');
121
+ } catch (_e) {
122
+ return true;
123
+ }
124
+ }
125
+ export function getFloatingFocusElement(floatingElement: any): any {
126
+ if (!floatingElement) {
127
+ return null;
128
+ }
129
+ return floatingElement.hasAttribute(FOCUSABLE_ATTRIBUTE)
130
+ ? floatingElement
131
+ : floatingElement.querySelector('[' + FOCUSABLE_ATTRIBUTE + ']') || floatingElement;
132
+ }
133
+
134
+ export function getNodeChildren(nodes: any[], id: any, onlyOpenChildren = true): any[] {
135
+ const directChildren = nodes.filter(
136
+ (node) => node.parentId === id && (!onlyOpenChildren || node.context?.open),
137
+ );
138
+ return directChildren.flatMap((child) => [
139
+ child,
140
+ ...getNodeChildren(nodes, child.id, onlyOpenChildren),
141
+ ]);
142
+ }
143
+ export function getDeepestNode(nodes: any[], id: any): any {
144
+ let deepestNodeId: any;
145
+ let maxDepth = -1;
146
+ function findDeepest(nodeId: any, depth: number) {
147
+ if (depth > maxDepth) {
148
+ deepestNodeId = nodeId;
149
+ maxDepth = depth;
150
+ }
151
+ const children = getNodeChildren(nodes, nodeId);
152
+ children.forEach((child) => {
153
+ findDeepest(child.id, depth + 1);
154
+ });
155
+ }
156
+ findDeepest(id, 0);
157
+ return nodes.find((node) => node.id === deepestNodeId);
158
+ }
159
+ export function getNodeAncestors(nodes: any[], id: any): any[] {
160
+ let allAncestors: any[] = [];
161
+ let currentParentId = nodes.find((node) => node.id === id)?.parentId;
162
+ while (currentParentId) {
163
+ const currentNode = nodes.find((node) => node.id === currentParentId);
164
+ currentParentId = currentNode?.parentId;
165
+ if (currentNode) {
166
+ allAncestors = allAncestors.concat(currentNode);
167
+ }
168
+ }
169
+ return allAncestors;
170
+ }
171
+
172
+ export function stopEvent(event: any): void {
173
+ event.preventDefault();
174
+ event.stopPropagation();
175
+ }
176
+ export function isReactEvent(event: any): boolean {
177
+ return 'nativeEvent' in event;
178
+ }
179
+
180
+ export function isVirtualClick(event: any): boolean {
181
+ if (event.mozInputSource === 0 && event.isTrusted) {
182
+ return true;
183
+ }
184
+ if (isAndroid() && event.pointerType) {
185
+ return event.type === 'click' && event.buttons === 1;
186
+ }
187
+ return event.detail === 0 && !event.pointerType;
188
+ }
189
+ export function isVirtualPointerEvent(event: any): boolean {
190
+ if (isJSDOM()) return false;
191
+ return (
192
+ (!isAndroid() && event.width === 0 && event.height === 0) ||
193
+ (isAndroid() &&
194
+ event.width === 1 &&
195
+ event.height === 1 &&
196
+ event.pressure === 0 &&
197
+ event.detail === 0 &&
198
+ event.pointerType === 'mouse') ||
199
+ (event.width < 1 &&
200
+ event.height < 1 &&
201
+ event.pressure === 0 &&
202
+ event.detail === 0 &&
203
+ event.pointerType === 'touch')
204
+ );
205
+ }
206
+ export function isMouseLikePointerType(pointerType: any, strict?: boolean): boolean {
207
+ const values: any[] = ['mouse', 'pen'];
208
+ if (!strict) {
209
+ values.push('', undefined);
210
+ }
211
+ return values.includes(pointerType);
212
+ }
213
+
214
+ export function getDelay(value: any, prop: string, pointerType?: any): any {
215
+ if (pointerType && !isMouseLikePointerType(pointerType)) {
216
+ return 0;
217
+ }
218
+ if (typeof value === 'number') {
219
+ return value;
220
+ }
221
+ if (typeof value === 'function') {
222
+ const result = value();
223
+ if (typeof result === 'number') {
224
+ return result;
225
+ }
226
+ return result?.[prop];
227
+ }
228
+ return value?.[prop];
229
+ }
230
+
231
+ // JS style key (`backgroundColor`) → CSS property (`background-color`).
232
+ export function camelCaseToKebabCase(str: string): string {
233
+ return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? '-' : '') + $.toLowerCase());
234
+ }
235
+ export function execWithArgsOrReturn(valueOrFn: any, args: any): any {
236
+ return typeof valueOrFn === 'function' ? valueOrFn(args) : valueOrFn;
237
+ }
238
+
239
+ // Fork of `fast-deep-equal` (from @floating-ui/react-dom) — compares functions by
240
+ // source. Used by the positioning core.
241
+ export function deepEqual(a: any, b: any): boolean {
242
+ if (a === b) {
243
+ return true;
244
+ }
245
+ if (typeof a !== typeof b) {
246
+ return false;
247
+ }
248
+ if (typeof a === 'function' && a.toString() === b.toString()) {
249
+ return true;
250
+ }
251
+ let length: number;
252
+ let i: number;
253
+ let keys: string[];
254
+ if (a && b && typeof a === 'object') {
255
+ if (Array.isArray(a)) {
256
+ length = a.length;
257
+ if (length !== b.length) return false;
258
+ for (i = length; i-- !== 0; ) {
259
+ if (!deepEqual(a[i], b[i])) {
260
+ return false;
261
+ }
262
+ }
263
+ return true;
264
+ }
265
+ keys = Object.keys(a);
266
+ length = keys.length;
267
+ if (length !== Object.keys(b).length) {
268
+ return false;
269
+ }
270
+ for (i = length; i-- !== 0; ) {
271
+ if (!{}.hasOwnProperty.call(b, keys[i])) {
272
+ return false;
273
+ }
274
+ }
275
+ for (i = length; i-- !== 0; ) {
276
+ const key = keys[i];
277
+ if (key === '_owner' && a.$$typeof) {
278
+ continue;
279
+ }
280
+ if (!deepEqual(a[key], b[key])) {
281
+ return false;
282
+ }
283
+ }
284
+ return true;
285
+ }
286
+ return a !== a && b !== b;
287
+ }
288
+
289
+ export function getDPR(element: Element): number {
290
+ if (typeof window === 'undefined') {
291
+ return 1;
292
+ }
293
+ const win = element.ownerDocument.defaultView || window;
294
+ return win.devicePixelRatio || 1;
295
+ }
296
+
297
+ export function roundByDPR(element: Element, value: number): number {
298
+ const dpr = getDPR(element);
299
+ return Math.round(value * dpr) / dpr;
300
+ }
301
+
302
+ export const isClient = typeof document !== 'undefined';
303
+
304
+ // useLayoutEffect on the client; a no-op on the server (positioning/interactions
305
+ // are client-only). Conditional hook calls are legal in octane (slot-keyed).
306
+ export function useModernLayoutEffect(
307
+ fn: () => void | (() => void),
308
+ deps: any[] | undefined,
309
+ slot: symbol | undefined,
310
+ ): void {
311
+ if (isClient) {
312
+ useLayoutEffect(fn, deps, slot);
313
+ }
314
+ }
315
+
316
+ export function useLatestRef<T>(value: T, slot: symbol | undefined): { current: T } {
317
+ const ref = useRef(value, subSlot(slot, 'lr:ref'));
318
+ useModernLayoutEffect(
319
+ () => {
320
+ ref.current = value;
321
+ },
322
+ undefined,
323
+ subSlot(slot, 'lr:eff'),
324
+ );
325
+ return ref;
326
+ }
327
+
328
+ // octane has no useInsertionEffect; the safe fallback runs synchronously, keeping
329
+ // the event ref current each render (matches @floating-ui/react's fallback path).
330
+ const useSafeInsertionEffect = (fn: () => void) => fn();
331
+
332
+ export function useEffectEvent<T extends (...args: any[]) => any>(
333
+ callback: T | undefined,
334
+ slot: symbol | undefined,
335
+ ): T {
336
+ const ref = useRef<any>(
337
+ () => {
338
+ throw new Error('Cannot call an event handler while rendering.');
339
+ },
340
+ subSlot(slot, 'ee:ref'),
341
+ );
342
+ useSafeInsertionEffect(() => {
343
+ ref.current = callback;
344
+ });
345
+ return useCallback(
346
+ (...args: any[]) => (ref.current == null ? undefined : ref.current(...args)),
347
+ [],
348
+ subSlot(slot, 'ee:cb'),
349
+ ) as T;
350
+ }
351
+
352
+ export function isDifferentGridRow(index: number, cols: number, prevRow: number): boolean {
353
+ return Math.floor(index / cols) !== prevRow;
354
+ }
355
+ export function isIndexOutOfListBounds(listRef: any, index: number): boolean {
356
+ return index < 0 || index >= listRef.current.length;
357
+ }
358
+ export function getMinListIndex(listRef: any, disabledIndices: any): number {
359
+ return findNonDisabledListIndex(listRef, { disabledIndices });
360
+ }
361
+ export function getMaxListIndex(listRef: any, disabledIndices: any): number {
362
+ return findNonDisabledListIndex(listRef, {
363
+ decrement: true,
364
+ startingIndex: listRef.current.length,
365
+ disabledIndices,
366
+ });
367
+ }
368
+ export function findNonDisabledListIndex(listRef: any, _temp?: any): number {
369
+ const {
370
+ startingIndex = -1,
371
+ decrement = false,
372
+ disabledIndices,
373
+ amount = 1,
374
+ } = _temp === void 0 ? {} : _temp;
375
+ let index = startingIndex;
376
+ do {
377
+ index += decrement ? -amount : amount;
378
+ } while (
379
+ index >= 0 &&
380
+ index <= listRef.current.length - 1 &&
381
+ isListIndexDisabled(listRef, index, disabledIndices)
382
+ );
383
+ return index;
384
+ }
385
+ export function getGridNavigatedIndex(listRef: any, _ref: any): number {
386
+ const {
387
+ event,
388
+ orientation,
389
+ loop,
390
+ rtl,
391
+ cols,
392
+ disabledIndices,
393
+ minIndex,
394
+ maxIndex,
395
+ prevIndex,
396
+ stopEvent: stop = false,
397
+ } = _ref;
398
+ let nextIndex = prevIndex;
399
+ if (event.key === ARROW_UP) {
400
+ stop && stopEvent(event);
401
+ if (prevIndex === -1) {
402
+ nextIndex = maxIndex;
403
+ } else {
404
+ nextIndex = findNonDisabledListIndex(listRef, {
405
+ startingIndex: nextIndex,
406
+ amount: cols,
407
+ decrement: true,
408
+ disabledIndices,
409
+ });
410
+ if (loop && (prevIndex - cols < minIndex || nextIndex < 0)) {
411
+ const col = prevIndex % cols;
412
+ const maxCol = maxIndex % cols;
413
+ const offset = maxIndex - (maxCol - col);
414
+ if (maxCol === col) {
415
+ nextIndex = maxIndex;
416
+ } else {
417
+ nextIndex = maxCol > col ? offset : offset - cols;
418
+ }
419
+ }
420
+ }
421
+ if (isIndexOutOfListBounds(listRef, nextIndex)) {
422
+ nextIndex = prevIndex;
423
+ }
424
+ }
425
+ if (event.key === ARROW_DOWN) {
426
+ stop && stopEvent(event);
427
+ if (prevIndex === -1) {
428
+ nextIndex = minIndex;
429
+ } else {
430
+ nextIndex = findNonDisabledListIndex(listRef, {
431
+ startingIndex: prevIndex,
432
+ amount: cols,
433
+ disabledIndices,
434
+ });
435
+ if (loop && prevIndex + cols > maxIndex) {
436
+ nextIndex = findNonDisabledListIndex(listRef, {
437
+ startingIndex: (prevIndex % cols) - cols,
438
+ amount: cols,
439
+ disabledIndices,
440
+ });
441
+ }
442
+ }
443
+ if (isIndexOutOfListBounds(listRef, nextIndex)) {
444
+ nextIndex = prevIndex;
445
+ }
446
+ }
447
+ if (orientation === 'both') {
448
+ const prevRow = floor(prevIndex / cols);
449
+ if (event.key === (rtl ? ARROW_LEFT : ARROW_RIGHT)) {
450
+ stop && stopEvent(event);
451
+ if (prevIndex % cols !== cols - 1) {
452
+ nextIndex = findNonDisabledListIndex(listRef, {
453
+ startingIndex: prevIndex,
454
+ disabledIndices,
455
+ });
456
+ if (loop && isDifferentGridRow(nextIndex, cols, prevRow)) {
457
+ nextIndex = findNonDisabledListIndex(listRef, {
458
+ startingIndex: prevIndex - (prevIndex % cols) - 1,
459
+ disabledIndices,
460
+ });
461
+ }
462
+ } else if (loop) {
463
+ nextIndex = findNonDisabledListIndex(listRef, {
464
+ startingIndex: prevIndex - (prevIndex % cols) - 1,
465
+ disabledIndices,
466
+ });
467
+ }
468
+ if (isDifferentGridRow(nextIndex, cols, prevRow)) {
469
+ nextIndex = prevIndex;
470
+ }
471
+ }
472
+ if (event.key === (rtl ? ARROW_RIGHT : ARROW_LEFT)) {
473
+ stop && stopEvent(event);
474
+ if (prevIndex % cols !== 0) {
475
+ nextIndex = findNonDisabledListIndex(listRef, {
476
+ startingIndex: prevIndex,
477
+ decrement: true,
478
+ disabledIndices,
479
+ });
480
+ if (loop && isDifferentGridRow(nextIndex, cols, prevRow)) {
481
+ nextIndex = findNonDisabledListIndex(listRef, {
482
+ startingIndex: prevIndex + (cols - (prevIndex % cols)),
483
+ decrement: true,
484
+ disabledIndices,
485
+ });
486
+ }
487
+ } else if (loop) {
488
+ nextIndex = findNonDisabledListIndex(listRef, {
489
+ startingIndex: prevIndex + (cols - (prevIndex % cols)),
490
+ decrement: true,
491
+ disabledIndices,
492
+ });
493
+ }
494
+ if (isDifferentGridRow(nextIndex, cols, prevRow)) {
495
+ nextIndex = prevIndex;
496
+ }
497
+ }
498
+ const lastRow = floor(maxIndex / cols) === prevRow;
499
+ if (isIndexOutOfListBounds(listRef, nextIndex)) {
500
+ if (loop && lastRow) {
501
+ nextIndex =
502
+ event.key === (rtl ? ARROW_RIGHT : ARROW_LEFT)
503
+ ? maxIndex
504
+ : findNonDisabledListIndex(listRef, {
505
+ startingIndex: prevIndex - (prevIndex % cols) - 1,
506
+ disabledIndices,
507
+ });
508
+ } else {
509
+ nextIndex = prevIndex;
510
+ }
511
+ }
512
+ }
513
+ return nextIndex;
514
+ }
515
+
516
+ export function createGridCellMap(sizes: any[], cols: number, dense: boolean): any[] {
517
+ const cellMap: any[] = [];
518
+ let startIndex = 0;
519
+ sizes.forEach((_ref2, index) => {
520
+ const { width, height } = _ref2;
521
+ if (width > cols) {
522
+ throw new Error(
523
+ '[Floating UI]: Invalid grid - item width at index ' +
524
+ index +
525
+ ' is greater than grid columns',
526
+ );
527
+ }
528
+ let itemPlaced = false;
529
+ if (dense) {
530
+ startIndex = 0;
531
+ }
532
+ while (!itemPlaced) {
533
+ const targetCells: number[] = [];
534
+ for (let i = 0; i < width; i++) {
535
+ for (let j = 0; j < height; j++) {
536
+ targetCells.push(startIndex + i + j * cols);
537
+ }
538
+ }
539
+ if (
540
+ (startIndex % cols) + width <= cols &&
541
+ targetCells.every((cell) => cellMap[cell] == null)
542
+ ) {
543
+ targetCells.forEach((cell) => {
544
+ cellMap[cell] = index;
545
+ });
546
+ itemPlaced = true;
547
+ } else {
548
+ startIndex++;
549
+ }
550
+ }
551
+ });
552
+ return [...cellMap];
553
+ }
554
+ export function getGridCellIndexOfCorner(
555
+ index: number,
556
+ sizes: any[],
557
+ cellMap: any[],
558
+ cols: number,
559
+ corner: string,
560
+ ): number {
561
+ if (index === -1) return -1;
562
+ const firstCellIndex = cellMap.indexOf(index);
563
+ const sizeItem = sizes[index];
564
+ switch (corner) {
565
+ case 'tl':
566
+ return firstCellIndex;
567
+ case 'tr':
568
+ if (!sizeItem) {
569
+ return firstCellIndex;
570
+ }
571
+ return firstCellIndex + sizeItem.width - 1;
572
+ case 'bl':
573
+ if (!sizeItem) {
574
+ return firstCellIndex;
575
+ }
576
+ return firstCellIndex + (sizeItem.height - 1) * cols;
577
+ case 'br':
578
+ return cellMap.lastIndexOf(index);
579
+ }
580
+ return -1;
581
+ }
582
+ export function getGridCellIndices(indices: any[], cellMap: any[]): number[] {
583
+ return cellMap.flatMap((index, cellIndex) => (indices.includes(index) ? [cellIndex] : []));
584
+ }
585
+ export function isListIndexDisabled(listRef: any, index: number, disabledIndices?: any): boolean {
586
+ if (typeof disabledIndices === 'function') {
587
+ return disabledIndices(index);
588
+ } else if (disabledIndices) {
589
+ return disabledIndices.includes(index);
590
+ }
591
+ const element = listRef.current[index];
592
+ return (
593
+ element == null ||
594
+ element.hasAttribute('disabled') ||
595
+ element.getAttribute('aria-disabled') === 'true'
596
+ );
597
+ }
598
+
599
+ export const getTabbableOptions = (): any => ({
600
+ getShadowRoot: true,
601
+ displayCheck:
602
+ typeof ResizeObserver === 'function' && ResizeObserver.toString().includes('[native code]')
603
+ ? 'full'
604
+ : 'none',
605
+ });
606
+ function getTabbableIn(container: any, dir: number): any {
607
+ const list = tabbable(container, getTabbableOptions());
608
+ const len = list.length;
609
+ if (len === 0) return;
610
+ const active = activeElement(getDocument(container));
611
+ const index = list.indexOf(active as any);
612
+ const nextIndex = index === -1 ? (dir === 1 ? 0 : len - 1) : index + dir;
613
+ return list[nextIndex];
614
+ }
615
+ export function getNextTabbable(referenceElement: any): any {
616
+ return getTabbableIn(getDocument(referenceElement).body, 1) || referenceElement;
617
+ }
618
+ export function getPreviousTabbable(referenceElement: any): any {
619
+ return getTabbableIn(getDocument(referenceElement).body, -1) || referenceElement;
620
+ }
621
+ let rafId = 0;
622
+ export function enqueueFocus(el: any, options: any = {}): void {
623
+ const { preventScroll = false, cancelPrevious = true, sync = false } = options;
624
+ cancelPrevious && cancelAnimationFrame(rafId);
625
+ const exec = () => el?.focus({ preventScroll });
626
+ if (sync) {
627
+ exec();
628
+ } else {
629
+ rafId = requestAnimationFrame(exec);
630
+ }
631
+ }
632
+
633
+ export function isOutsideEvent(event: any, container?: any): boolean {
634
+ const containerElement = container || event.currentTarget;
635
+ const relatedTarget = event.relatedTarget;
636
+ return !relatedTarget || !contains(containerElement, relatedTarget);
637
+ }
638
+ export function disableFocusInside(container: any): void {
639
+ const tabbableElements = tabbable(container, getTabbableOptions());
640
+ tabbableElements.forEach((element: any) => {
641
+ element.dataset.tabindex = element.getAttribute('tabindex') || '';
642
+ element.setAttribute('tabindex', '-1');
643
+ });
644
+ }
645
+ export function enableFocusInside(container: any): void {
646
+ const elements = container.querySelectorAll('[data-tabindex]');
647
+ elements.forEach((element: any) => {
648
+ const tabindex = element.dataset.tabindex;
649
+ delete element.dataset.tabindex;
650
+ if (tabindex) {
651
+ element.setAttribute('tabindex', tabindex);
652
+ } else {
653
+ element.removeAttribute('tabindex');
654
+ }
655
+ });
656
+ }