@react-hive/honey-utils 3.11.0 → 3.13.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.
@@ -2,6 +2,216 @@
2
2
  /******/ "use strict";
3
3
  /******/ var __webpack_modules__ = ({
4
4
 
5
+ /***/ "./src/a11y/focus/get-focusable-html-elements.ts":
6
+ /*!*******************************************************!*\
7
+ !*** ./src/a11y/focus/get-focusable-html-elements.ts ***!
8
+ \*******************************************************/
9
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
10
+
11
+ __webpack_require__.r(__webpack_exports__);
12
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
13
+ /* harmony export */ getFocusableHtmlElements: () => (/* binding */ getFocusableHtmlElements)
14
+ /* harmony export */ });
15
+ /* harmony import */ var _is_html_element_focusable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./is-html-element-focusable */ "./src/a11y/focus/is-html-element-focusable.ts");
16
+
17
+ /**
18
+ * Collects all focusable descendant elements within a container.
19
+ *
20
+ * The function queries *all* elements under the container and filters them
21
+ * using `isHtmlElementFocusable`, producing a reliable list of elements
22
+ * that can receive keyboard focus in real-world browser conditions.
23
+ *
24
+ * @param container - The root container whose focusable children will be found.
25
+ *
26
+ * @returns An array of focusable HTMLElements in DOM order.
27
+ */
28
+ const getFocusableHtmlElements = (container) => Array.from(container.querySelectorAll('*')).filter(_is_html_element_focusable__WEBPACK_IMPORTED_MODULE_0__.isHtmlElementFocusable);
29
+
30
+
31
+ /***/ }),
32
+
33
+ /***/ "./src/a11y/focus/index.ts":
34
+ /*!*********************************!*\
35
+ !*** ./src/a11y/focus/index.ts ***!
36
+ \*********************************/
37
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
38
+
39
+ __webpack_require__.r(__webpack_exports__);
40
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
41
+ /* harmony export */ FOCUSABLE_HTML_TAGS: () => (/* reexport safe */ _is_html_element_focusable__WEBPACK_IMPORTED_MODULE_0__.FOCUSABLE_HTML_TAGS),
42
+ /* harmony export */ getFocusableHtmlElements: () => (/* reexport safe */ _get_focusable_html_elements__WEBPACK_IMPORTED_MODULE_1__.getFocusableHtmlElements),
43
+ /* harmony export */ isHtmlElementFocusable: () => (/* reexport safe */ _is_html_element_focusable__WEBPACK_IMPORTED_MODULE_0__.isHtmlElementFocusable),
44
+ /* harmony export */ moveFocusWithinContainer: () => (/* reexport safe */ _move_focus_within_container__WEBPACK_IMPORTED_MODULE_2__.moveFocusWithinContainer)
45
+ /* harmony export */ });
46
+ /* harmony import */ var _is_html_element_focusable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./is-html-element-focusable */ "./src/a11y/focus/is-html-element-focusable.ts");
47
+ /* harmony import */ var _get_focusable_html_elements__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./get-focusable-html-elements */ "./src/a11y/focus/get-focusable-html-elements.ts");
48
+ /* harmony import */ var _move_focus_within_container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./move-focus-within-container */ "./src/a11y/focus/move-focus-within-container.ts");
49
+
50
+
51
+
52
+
53
+
54
+ /***/ }),
55
+
56
+ /***/ "./src/a11y/focus/is-html-element-focusable.ts":
57
+ /*!*****************************************************!*\
58
+ !*** ./src/a11y/focus/is-html-element-focusable.ts ***!
59
+ \*****************************************************/
60
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
61
+
62
+ __webpack_require__.r(__webpack_exports__);
63
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
64
+ /* harmony export */ FOCUSABLE_HTML_TAGS: () => (/* binding */ FOCUSABLE_HTML_TAGS),
65
+ /* harmony export */ isHtmlElementFocusable: () => (/* binding */ isHtmlElementFocusable)
66
+ /* harmony export */ });
67
+ /* harmony import */ var _dom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../dom */ "./src/dom/index.ts");
68
+
69
+ const FOCUSABLE_HTML_TAGS = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON', 'A'];
70
+ /**
71
+ * Determines whether an HTMLElement is focusable under standard browser rules.
72
+ *
73
+ * The function checks a combination of factors:
74
+ * - The element must be rendered (not `display: none` or `visibility: hidden`).
75
+ * - Disabled form controls are never focusable.
76
+ * - Elements with `tabindex="-1"` are intentionally removed from the focus order.
77
+ * - Certain native HTML elements are inherently focusable (e.g. inputs, buttons, anchors with `href`).
78
+ * - Elements with `contenteditable="true"` are treated as focusable.
79
+ * - Any element with a valid `tabindex` (not null) is considered focusable.
80
+ *
81
+ * This logic approximates how browsers and the accessibility tree
82
+ * determine real-world focusability—not just tabindex presence.
83
+ *
84
+ * @param element - The element to test. `null` or `undefined` will return `false`.
85
+ *
86
+ * @returns Whether the element is focusable.
87
+ */
88
+ const isHtmlElementFocusable = (element) => {
89
+ if (!element) {
90
+ return false;
91
+ }
92
+ const style = window.getComputedStyle(element);
93
+ if (style.visibility === 'hidden' || style.display === 'none') {
94
+ return false;
95
+ }
96
+ if ('disabled' in element && element.disabled) {
97
+ return false;
98
+ }
99
+ // Explicitly removed from tab order
100
+ const tabIndex = element.getAttribute('tabindex');
101
+ if (tabIndex === '-1') {
102
+ return false;
103
+ }
104
+ if (FOCUSABLE_HTML_TAGS.includes(element.tagName)) {
105
+ if ((0,_dom__WEBPACK_IMPORTED_MODULE_0__.isAnchorHtmlElement)(element)) {
106
+ return element.href !== '';
107
+ }
108
+ return true;
109
+ }
110
+ if ((0,_dom__WEBPACK_IMPORTED_MODULE_0__.isContentEditableHtmlElement)(element)) {
111
+ return true;
112
+ }
113
+ return tabIndex !== null;
114
+ };
115
+
116
+
117
+ /***/ }),
118
+
119
+ /***/ "./src/a11y/focus/move-focus-within-container.ts":
120
+ /*!*******************************************************!*\
121
+ !*** ./src/a11y/focus/move-focus-within-container.ts ***!
122
+ \*******************************************************/
123
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
124
+
125
+ __webpack_require__.r(__webpack_exports__);
126
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
127
+ /* harmony export */ moveFocusWithinContainer: () => (/* binding */ moveFocusWithinContainer)
128
+ /* harmony export */ });
129
+ /* harmony import */ var _get_focusable_html_elements__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./get-focusable-html-elements */ "./src/a11y/focus/get-focusable-html-elements.ts");
130
+
131
+ /**
132
+ * Moves focus to the next or previous focusable element within a container.
133
+ *
134
+ * This utility is commonly used to implement accessible keyboard navigation patterns such as:
135
+ * - roving tabindex
136
+ * - custom dropdowns
137
+ * - tablists
138
+ * - menus
139
+ * - horizontal or vertical navigation groups
140
+ *
141
+ * Focus movement is scoped to a container and operates on the list of
142
+ * focusable descendants returned by `getFocusableHtmlElements`.
143
+ *
144
+ * @param direction - Direction in which focus should move (`'next'` or `'previous'`).
145
+ * @param container - Optional container that defines the focus scope.
146
+ * If omitted, the parent element of the currently focused element is used.
147
+ * @param options - Optional configuration controlling wrapping behavior and custom index resolution.
148
+ *
149
+ * @remarks
150
+ * - This function reads from and mutates the document's focus state.
151
+ * - If no active element exists, no container can be resolved,
152
+ * or the active element is not part of the focusable set, no action is taken.
153
+ * - When `getNextIndex` is provided, it fully overrides the default wrapping and directional logic.
154
+ */
155
+ const moveFocusWithinContainer = (direction, container = null, { wrap = true, getNextIndex } = {}) => {
156
+ const activeElement = document.activeElement;
157
+ const scope = container ?? activeElement?.parentElement;
158
+ if (!activeElement || !scope) {
159
+ return;
160
+ }
161
+ const focusableElements = (0,_get_focusable_html_elements__WEBPACK_IMPORTED_MODULE_0__.getFocusableHtmlElements)(scope);
162
+ if (focusableElements.length === 0) {
163
+ return;
164
+ }
165
+ const currentIndex = focusableElements.indexOf(activeElement);
166
+ if (currentIndex === -1) {
167
+ return;
168
+ }
169
+ let nextIndex;
170
+ if (getNextIndex) {
171
+ nextIndex = getNextIndex(currentIndex, direction, focusableElements);
172
+ }
173
+ else {
174
+ if (direction === 'next') {
175
+ nextIndex = currentIndex + 1;
176
+ if (nextIndex >= focusableElements.length) {
177
+ nextIndex = wrap ? 0 : null;
178
+ }
179
+ }
180
+ else {
181
+ nextIndex = currentIndex - 1;
182
+ if (nextIndex < 0) {
183
+ nextIndex = wrap ? focusableElements.length - 1 : null;
184
+ }
185
+ }
186
+ }
187
+ if (nextIndex === null) {
188
+ return;
189
+ }
190
+ focusableElements[nextIndex]?.focus();
191
+ };
192
+
193
+
194
+ /***/ }),
195
+
196
+ /***/ "./src/a11y/index.ts":
197
+ /*!***************************!*\
198
+ !*** ./src/a11y/index.ts ***!
199
+ \***************************/
200
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
201
+
202
+ __webpack_require__.r(__webpack_exports__);
203
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
204
+ /* harmony export */ FOCUSABLE_HTML_TAGS: () => (/* reexport safe */ _focus__WEBPACK_IMPORTED_MODULE_0__.FOCUSABLE_HTML_TAGS),
205
+ /* harmony export */ getFocusableHtmlElements: () => (/* reexport safe */ _focus__WEBPACK_IMPORTED_MODULE_0__.getFocusableHtmlElements),
206
+ /* harmony export */ isHtmlElementFocusable: () => (/* reexport safe */ _focus__WEBPACK_IMPORTED_MODULE_0__.isHtmlElementFocusable),
207
+ /* harmony export */ moveFocusWithinContainer: () => (/* reexport safe */ _focus__WEBPACK_IMPORTED_MODULE_0__.moveFocusWithinContainer)
208
+ /* harmony export */ });
209
+ /* harmony import */ var _focus__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./focus */ "./src/a11y/focus/index.ts");
210
+
211
+
212
+
213
+ /***/ }),
214
+
5
215
  /***/ "./src/array.ts":
6
216
  /*!**********************!*\
7
217
  !*** ./src/array.ts ***!
@@ -406,75 +616,19 @@ const findAsync = async (array, predicate) => {
406
616
 
407
617
  /***/ }),
408
618
 
409
- /***/ "./src/dom.ts":
410
- /*!********************!*\
411
- !*** ./src/dom.ts ***!
412
- \********************/
619
+ /***/ "./src/dom/dom.ts":
620
+ /*!************************!*\
621
+ !*** ./src/dom/dom.ts ***!
622
+ \************************/
413
623
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
414
624
 
415
625
  __webpack_require__.r(__webpack_exports__);
416
626
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
417
- /* harmony export */ FOCUSABLE_HTML_TAGS: () => (/* binding */ FOCUSABLE_HTML_TAGS),
418
- /* harmony export */ calculateCenterOffset: () => (/* binding */ calculateCenterOffset),
419
- /* harmony export */ centerElementInContainer: () => (/* binding */ centerElementInContainer),
420
627
  /* harmony export */ cloneBlob: () => (/* binding */ cloneBlob),
421
- /* harmony export */ getDOMRectIntersectionRatio: () => (/* binding */ getDOMRectIntersectionRatio),
422
628
  /* harmony export */ getElementOffsetRect: () => (/* binding */ getElementOffsetRect),
423
- /* harmony export */ getFocusableHtmlElements: () => (/* binding */ getFocusableHtmlElements),
424
- /* harmony export */ getLocalStorageCapabilities: () => (/* binding */ getLocalStorageCapabilities),
425
- /* harmony export */ getXOverflowWidth: () => (/* binding */ getXOverflowWidth),
426
- /* harmony export */ getYOverflowHeight: () => (/* binding */ getYOverflowHeight),
427
- /* harmony export */ hasXOverflow: () => (/* binding */ hasXOverflow),
428
- /* harmony export */ hasYOverflow: () => (/* binding */ hasYOverflow),
429
629
  /* harmony export */ isAnchorHtmlElement: () => (/* binding */ isAnchorHtmlElement),
430
- /* harmony export */ isContentEditableHtmlElement: () => (/* binding */ isContentEditableHtmlElement),
431
- /* harmony export */ isHtmlElementFocusable: () => (/* binding */ isHtmlElementFocusable),
432
- /* harmony export */ isLocalStorageReadable: () => (/* binding */ isLocalStorageReadable),
433
- /* harmony export */ moveFocusWithinContainer: () => (/* binding */ moveFocusWithinContainer),
434
- /* harmony export */ parse2DMatrix: () => (/* binding */ parse2DMatrix)
630
+ /* harmony export */ isContentEditableHtmlElement: () => (/* binding */ isContentEditableHtmlElement)
435
631
  /* harmony export */ });
436
- const FOCUSABLE_HTML_TAGS = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON', 'A'];
437
- /**
438
- * Extracts transformation values (translate, scale, skew) from the 2D transformation matrix of a given HTML element.
439
- *
440
- * Only works with 2D transforms (i.e., `matrix(a, b, c, d, e, f)`).
441
- *
442
- * @param element - The element with a CSS transform applied.
443
- * @returns An object with parsed transformation values.
444
- *
445
- * @example
446
- * ```ts
447
- * const values = parse2DMatrix(myElement);
448
- * console.log(values.translateX);
449
- * console.log(values.scaleX);
450
- * ```
451
- */
452
- const parse2DMatrix = (element) => {
453
- const computedStyles = window.getComputedStyle(element);
454
- const transformValue = computedStyles.getPropertyValue('transform');
455
- const matrixMatch = transformValue.match(/^matrix\((.+)\)$/);
456
- if (!matrixMatch) {
457
- return {
458
- translateX: 0,
459
- translateY: 0,
460
- scaleX: 1,
461
- scaleY: 1,
462
- skewX: 0,
463
- skewY: 0,
464
- };
465
- }
466
- const [scaleX, skewY, skewX, scaleY, translateX, translateY] = matrixMatch[1]
467
- .split(', ')
468
- .map(parseFloat);
469
- return {
470
- translateX,
471
- translateY,
472
- scaleX,
473
- scaleY,
474
- skewX,
475
- skewY,
476
- };
477
- };
478
632
  /**
479
633
  * Creates a clone of a Blob object.
480
634
  *
@@ -483,24 +637,6 @@ const parse2DMatrix = (element) => {
483
637
  * @returns A new Blob with the same content and type as the original.
484
638
  */
485
639
  const cloneBlob = (blob) => new Blob([blob], { type: blob.type });
486
- /**
487
- * Calculates the intersection ratio between two DOM rectangles.
488
- *
489
- * The ratio represents the proportion of the `targetRect` that is covered by `sourceRect`.
490
- * A value of `1` means `sourceRect` completely covers `targetRect`, and `0` means no overlap.
491
- *
492
- * @param sourceRect - The rectangle used to measure overlap against the target.
493
- * @param targetRect - The rectangle whose covered area is measured.
494
- *
495
- * @returns A number between `0` and `1` representing the intersection ratio.
496
- */
497
- const getDOMRectIntersectionRatio = (sourceRect, targetRect) => {
498
- const xOverlap = Math.max(0, Math.min(sourceRect.right, targetRect.right) - Math.max(sourceRect.left, targetRect.left));
499
- const yOverlap = Math.max(0, Math.min(sourceRect.bottom, targetRect.bottom) - Math.max(sourceRect.top, targetRect.top));
500
- const intersectionArea = xOverlap * yOverlap;
501
- const targetArea = targetRect.width * targetRect.height;
502
- return intersectionArea / targetArea;
503
- };
504
640
  /**
505
641
  * Returns the bounding DOMRect of an element based on offset and client dimensions.
506
642
  *
@@ -535,125 +671,219 @@ const isAnchorHtmlElement = (element) => element.tagName === 'A';
535
671
  * @returns True if `contenteditable="true"` is set.
536
672
  */
537
673
  const isContentEditableHtmlElement = (element) => element.getAttribute('contenteditable') === 'true';
674
+
675
+
676
+ /***/ }),
677
+
678
+ /***/ "./src/dom/file/download-file.ts":
679
+ /*!***************************************!*\
680
+ !*** ./src/dom/file/download-file.ts ***!
681
+ \***************************************/
682
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
683
+
684
+ __webpack_require__.r(__webpack_exports__);
685
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
686
+ /* harmony export */ downloadFile: () => (/* binding */ downloadFile)
687
+ /* harmony export */ });
688
+ /* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../string */ "./src/string.ts");
689
+ /* harmony import */ var _guards__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../guards */ "./src/guards.ts");
690
+
691
+
538
692
  /**
539
- * Determines whether an HTMLElement is focusable under standard browser rules.
693
+ * Initiates a file download in a browser environment.
540
694
  *
541
- * The function checks a combination of factors:
542
- * - The element must be rendered (not `display: none` or `visibility: hidden`).
543
- * - Disabled form controls are never focusable.
544
- * - Elements with `tabindex="-1"` are intentionally removed from the focus order.
545
- * - Certain native HTML elements are inherently focusable (e.g. inputs, buttons, anchors with `href`).
546
- * - Elements with `contenteditable="true"` are treated as focusable.
547
- * - Any element with a valid `tabindex` (not null) is considered focusable.
695
+ * This utility supports downloading from:
696
+ * - a URL string
697
+ * - a `Blob`
698
+ * - a `MediaSource`
548
699
  *
549
- * This logic approximates how browsers and the accessibility tree
550
- * determine real-world focusability—not just tabindex presence.
700
+ * For non-string inputs, an object URL is created temporarily and
701
+ * automatically revoked after the download is triggered.
551
702
  *
552
- * @param element - The element to test. `null` or `undefined` will return `false`.
703
+ * @remarks
704
+ * - This function performs direct DOM manipulation and must be executed in a browser environment.
705
+ * - In non-DOM contexts (e.g. SSR), the function exits without side effects.
706
+ * - Object URLs are revoked asynchronously to avoid Safari-related issues.
553
707
  *
554
- * @returns Whether the element is focusable.
708
+ * @param file - The file source to download (URL string or binary object).
709
+ * @param options - Optional configuration controlling filename and link target.
555
710
  */
556
- const isHtmlElementFocusable = (element) => {
557
- if (!element) {
558
- return false;
559
- }
560
- // Hidden or not rendered
561
- const style = window.getComputedStyle(element);
562
- if (style.visibility === 'hidden' || style.display === 'none') {
563
- return false;
564
- }
565
- if ('disabled' in element && element.disabled) {
566
- return false;
567
- }
568
- // Explicitly removed from tab order
569
- const tabIndex = element.getAttribute('tabindex');
570
- if (tabIndex === '-1') {
571
- return false;
711
+ const downloadFile = (file, { fileName, target } = {}) => {
712
+ // Browser guard (SSR / non-DOM environments)
713
+ if ((0,_guards__WEBPACK_IMPORTED_MODULE_1__.isUndefined)(document)) {
714
+ return;
572
715
  }
573
- if (FOCUSABLE_HTML_TAGS.includes(element.tagName)) {
574
- if (isAnchorHtmlElement(element)) {
575
- return element.href !== '';
716
+ const link = document.createElement('a');
717
+ let objectUrl = null;
718
+ try {
719
+ const href = (0,_string__WEBPACK_IMPORTED_MODULE_0__.isString)(file) ? file : (objectUrl = URL.createObjectURL(file));
720
+ link.href = href;
721
+ if (fileName) {
722
+ link.download = fileName;
576
723
  }
577
- return true;
724
+ if (target) {
725
+ link.target = target;
726
+ }
727
+ // Required for Firefox / Safari
728
+ document.body.appendChild(link);
729
+ link.click();
578
730
  }
579
- if (isContentEditableHtmlElement(element)) {
580
- return true;
731
+ finally {
732
+ link.remove();
733
+ if (objectUrl) {
734
+ // Delay revocation to avoid Safari issues
735
+ setTimeout(() => {
736
+ (0,_guards__WEBPACK_IMPORTED_MODULE_1__.assert)(objectUrl, 'Object URL should not be null');
737
+ URL.revokeObjectURL(objectUrl);
738
+ }, 0);
739
+ }
581
740
  }
582
- return tabIndex !== null;
583
741
  };
742
+
743
+
744
+ /***/ }),
745
+
746
+ /***/ "./src/dom/file/index.ts":
747
+ /*!*******************************!*\
748
+ !*** ./src/dom/file/index.ts ***!
749
+ \*******************************/
750
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
751
+
752
+ __webpack_require__.r(__webpack_exports__);
753
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
754
+ /* harmony export */ downloadFile: () => (/* reexport safe */ _download_file__WEBPACK_IMPORTED_MODULE_0__.downloadFile)
755
+ /* harmony export */ });
756
+ /* harmony import */ var _download_file__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./download-file */ "./src/dom/file/download-file.ts");
757
+
758
+
759
+
760
+ /***/ }),
761
+
762
+ /***/ "./src/dom/index.ts":
763
+ /*!**************************!*\
764
+ !*** ./src/dom/index.ts ***!
765
+ \**************************/
766
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
767
+
768
+ __webpack_require__.r(__webpack_exports__);
769
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
770
+ /* harmony export */ centerElementInContainer: () => (/* reexport safe */ _layout__WEBPACK_IMPORTED_MODULE_2__.centerElementInContainer),
771
+ /* harmony export */ cloneBlob: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_0__.cloneBlob),
772
+ /* harmony export */ downloadFile: () => (/* reexport safe */ _file__WEBPACK_IMPORTED_MODULE_1__.downloadFile),
773
+ /* harmony export */ getElementOffsetRect: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_0__.getElementOffsetRect),
774
+ /* harmony export */ getXOverflowWidth: () => (/* reexport safe */ _layout__WEBPACK_IMPORTED_MODULE_2__.getXOverflowWidth),
775
+ /* harmony export */ getYOverflowHeight: () => (/* reexport safe */ _layout__WEBPACK_IMPORTED_MODULE_2__.getYOverflowHeight),
776
+ /* harmony export */ hasXOverflow: () => (/* reexport safe */ _layout__WEBPACK_IMPORTED_MODULE_2__.hasXOverflow),
777
+ /* harmony export */ hasYOverflow: () => (/* reexport safe */ _layout__WEBPACK_IMPORTED_MODULE_2__.hasYOverflow),
778
+ /* harmony export */ isAnchorHtmlElement: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_0__.isAnchorHtmlElement),
779
+ /* harmony export */ isContentEditableHtmlElement: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_0__.isContentEditableHtmlElement),
780
+ /* harmony export */ parse2DMatrix: () => (/* reexport safe */ _transform__WEBPACK_IMPORTED_MODULE_3__.parse2DMatrix)
781
+ /* harmony export */ });
782
+ /* harmony import */ var _dom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dom */ "./src/dom/dom.ts");
783
+ /* harmony import */ var _file__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./file */ "./src/dom/file/index.ts");
784
+ /* harmony import */ var _layout__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./layout */ "./src/dom/layout/index.ts");
785
+ /* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./transform */ "./src/dom/transform/index.ts");
786
+
787
+
788
+
789
+
790
+
791
+
792
+ /***/ }),
793
+
794
+ /***/ "./src/dom/layout/center-element-in-container.ts":
795
+ /*!*******************************************************!*\
796
+ !*** ./src/dom/layout/center-element-in-container.ts ***!
797
+ \*******************************************************/
798
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
799
+
800
+ __webpack_require__.r(__webpack_exports__);
801
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
802
+ /* harmony export */ centerElementInContainer: () => (/* binding */ centerElementInContainer)
803
+ /* harmony export */ });
804
+ /* harmony import */ var _overflow__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./overflow */ "./src/dom/layout/overflow.ts");
805
+ /* harmony import */ var _geometry__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../geometry */ "./src/geometry/index.ts");
806
+
807
+
584
808
  /**
585
- * Collects all focusable descendant elements within a container.
586
- *
587
- * The function queries *all* elements under the container and filters them
588
- * using `isHtmlElementFocusable`, producing a reliable list of elements
589
- * that can receive keyboard focus in real-world browser conditions.
590
- *
591
- * @param container - The root container whose focusable children will be found.
592
- *
593
- * @returns An array of focusable HTMLElements in DOM order.
594
- */
595
- const getFocusableHtmlElements = (container) => Array.from(container.querySelectorAll('*')).filter(isHtmlElementFocusable);
596
- /**
597
- * Moves focus to the next or previous focusable element within a container.
809
+ * Translates a container so that a target element is visually centered within its visible bounds.
598
810
  *
599
- * This utility is commonly used to implement accessible keyboard navigation patterns such as:
600
- * - roving tabindex
601
- * - custom dropdowns
602
- * - tablists
603
- * - menus
604
- * - horizontal or vertical navigation groups
811
+ * Centering is achieved by applying a CSS `transform: translate(...)` to the
812
+ * container element rather than using native scrolling.
605
813
  *
606
- * Focus movement is scoped to a container and operates on the list of
607
- * focusable descendants returned by `getFocusableHtmlElements`.
814
+ * ### Behavior
815
+ * - Centering is calculated independently for each enabled axis.
816
+ * - Translation is applied only when the container content overflows on that axis.
817
+ * - When no overflow exists, the container remains untransformed for that axis.
608
818
  *
609
- * @param direction - Direction in which focus should move (`'next'` or `'previous'`).
610
- * @param container - Optional container that defines the focus scope.
611
- * If omitted, the parent element of the currently focused element is used.
612
- * @param options - Optional configuration controlling wrapping behavior and custom index resolution.
819
+ * ### Notes
820
+ * - This function performs immediate DOM reads and writes.
821
+ * - The resulting transform is clamped to valid scrollable bounds.
613
822
  *
614
- * @remarks
615
- * - This function reads from and mutates the document's focus state.
616
- * - If no active element exists, no container can be resolved,
617
- * or the active element is not part of the focusable set, no action is taken.
618
- * - When `getNextIndex` is provided, it fully overrides the default wrapping and directional logic.
823
+ * @param containerElement - The container whose content is translated.
824
+ * @param elementToCenter - The descendant element to align to the container’s center.
825
+ * @param options - Optional configuration controlling which axis or axes are centered.
619
826
  */
620
- const moveFocusWithinContainer = (direction, container = null, { wrap = true, getNextIndex } = {}) => {
621
- const activeElement = document.activeElement;
622
- const scope = container ?? activeElement?.parentElement;
623
- if (!activeElement || !scope) {
624
- return;
625
- }
626
- const focusableElements = getFocusableHtmlElements(scope);
627
- if (focusableElements.length === 0) {
628
- return;
629
- }
630
- const currentIndex = focusableElements.indexOf(activeElement);
631
- if (currentIndex === -1) {
632
- return;
633
- }
634
- let nextIndex;
635
- if (getNextIndex) {
636
- nextIndex = getNextIndex(currentIndex, direction, focusableElements);
637
- }
638
- else {
639
- if (direction === 'next') {
640
- nextIndex = currentIndex + 1;
641
- if (nextIndex >= focusableElements.length) {
642
- nextIndex = wrap ? 0 : null;
643
- }
644
- }
645
- else {
646
- nextIndex = currentIndex - 1;
647
- if (nextIndex < 0) {
648
- nextIndex = wrap ? focusableElements.length - 1 : null;
649
- }
650
- }
827
+ const centerElementInContainer = (containerElement, elementToCenter, { axis = 'both' } = {}) => {
828
+ let translateX = 0;
829
+ let translateY = 0;
830
+ if (axis === 'x' || axis === 'both') {
831
+ translateX = (0,_geometry__WEBPACK_IMPORTED_MODULE_1__.calculateCenterOffset)({
832
+ overflowSize: (0,_overflow__WEBPACK_IMPORTED_MODULE_0__.getXOverflowWidth)(containerElement),
833
+ containerSize: containerElement.clientWidth,
834
+ elementOffset: elementToCenter.offsetLeft,
835
+ elementSize: elementToCenter.clientWidth,
836
+ });
651
837
  }
652
- if (nextIndex === null) {
653
- return;
838
+ if (axis === 'y' || axis === 'both') {
839
+ translateY = (0,_geometry__WEBPACK_IMPORTED_MODULE_1__.calculateCenterOffset)({
840
+ overflowSize: (0,_overflow__WEBPACK_IMPORTED_MODULE_0__.getYOverflowHeight)(containerElement),
841
+ containerSize: containerElement.clientHeight,
842
+ elementOffset: elementToCenter.offsetTop,
843
+ elementSize: elementToCenter.clientHeight,
844
+ });
654
845
  }
655
- focusableElements[nextIndex]?.focus();
846
+ containerElement.style.transform = `translate(${translateX}px, ${translateY}px)`;
656
847
  };
848
+
849
+
850
+ /***/ }),
851
+
852
+ /***/ "./src/dom/layout/index.ts":
853
+ /*!*********************************!*\
854
+ !*** ./src/dom/layout/index.ts ***!
855
+ \*********************************/
856
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
857
+
858
+ __webpack_require__.r(__webpack_exports__);
859
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
860
+ /* harmony export */ centerElementInContainer: () => (/* reexport safe */ _center_element_in_container__WEBPACK_IMPORTED_MODULE_1__.centerElementInContainer),
861
+ /* harmony export */ getXOverflowWidth: () => (/* reexport safe */ _overflow__WEBPACK_IMPORTED_MODULE_0__.getXOverflowWidth),
862
+ /* harmony export */ getYOverflowHeight: () => (/* reexport safe */ _overflow__WEBPACK_IMPORTED_MODULE_0__.getYOverflowHeight),
863
+ /* harmony export */ hasXOverflow: () => (/* reexport safe */ _overflow__WEBPACK_IMPORTED_MODULE_0__.hasXOverflow),
864
+ /* harmony export */ hasYOverflow: () => (/* reexport safe */ _overflow__WEBPACK_IMPORTED_MODULE_0__.hasYOverflow)
865
+ /* harmony export */ });
866
+ /* harmony import */ var _overflow__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./overflow */ "./src/dom/layout/overflow.ts");
867
+ /* harmony import */ var _center_element_in_container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./center-element-in-container */ "./src/dom/layout/center-element-in-container.ts");
868
+
869
+
870
+
871
+
872
+ /***/ }),
873
+
874
+ /***/ "./src/dom/layout/overflow.ts":
875
+ /*!************************************!*\
876
+ !*** ./src/dom/layout/overflow.ts ***!
877
+ \************************************/
878
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
879
+
880
+ __webpack_require__.r(__webpack_exports__);
881
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
882
+ /* harmony export */ getXOverflowWidth: () => (/* binding */ getXOverflowWidth),
883
+ /* harmony export */ getYOverflowHeight: () => (/* binding */ getYOverflowHeight),
884
+ /* harmony export */ hasXOverflow: () => (/* binding */ hasXOverflow),
885
+ /* harmony export */ hasYOverflow: () => (/* binding */ hasYOverflow)
886
+ /* harmony export */ });
657
887
  /**
658
888
  * Checks whether an element has horizontal overflow.
659
889
  *
@@ -692,66 +922,126 @@ const hasYOverflow = (element) => element.scrollHeight > element.clientHeight;
692
922
  * @returns The overflow height in pixels. Returns `0` when the content does not overflow vertically.
693
923
  */
694
924
  const getYOverflowHeight = (element) => Math.max(0, element.scrollHeight - element.clientHeight);
925
+
926
+
927
+ /***/ }),
928
+
929
+ /***/ "./src/dom/transform/index.ts":
930
+ /*!************************************!*\
931
+ !*** ./src/dom/transform/index.ts ***!
932
+ \************************************/
933
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
934
+
935
+ __webpack_require__.r(__webpack_exports__);
936
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
937
+ /* harmony export */ parse2DMatrix: () => (/* reexport safe */ _parse_2d_matrix__WEBPACK_IMPORTED_MODULE_0__.parse2DMatrix)
938
+ /* harmony export */ });
939
+ /* harmony import */ var _parse_2d_matrix__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./parse-2d-matrix */ "./src/dom/transform/parse-2d-matrix.ts");
940
+
941
+
942
+
943
+ /***/ }),
944
+
945
+ /***/ "./src/dom/transform/parse-2d-matrix.ts":
946
+ /*!**********************************************!*\
947
+ !*** ./src/dom/transform/parse-2d-matrix.ts ***!
948
+ \**********************************************/
949
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
950
+
951
+ __webpack_require__.r(__webpack_exports__);
952
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
953
+ /* harmony export */ parse2DMatrix: () => (/* binding */ parse2DMatrix)
954
+ /* harmony export */ });
695
955
  /**
696
- * Calculates the offset required to center an element within a container along a single axis.
697
- *
698
- * The returned value is clamped so that the resulting translation does not
699
- * exceed the container's scrollable bounds.
700
- *
701
- * This function performs pure math only and does not access the DOM.
702
- *
703
- * @returns A negative offset value suitable for use in a CSS `translate`
704
- * transform, or `0` when no overflow exists on the axis.
705
- */
706
- const calculateCenterOffset = ({ overflowSize, containerSize, elementOffset, elementSize, }) => {
707
- if (overflowSize <= 0) {
708
- return 0;
709
- }
710
- const containerCenter = containerSize / 2;
711
- const elementCenter = elementOffset + elementSize / 2;
712
- const targetOffset = elementCenter - containerCenter;
713
- return -Math.max(0, Math.min(targetOffset, overflowSize));
714
- };
715
- /**
716
- * Translates a container so that a target element is visually centered within its visible bounds.
717
- *
718
- * Centering is achieved by applying a CSS `transform: translate(...)` to the
719
- * container element rather than using native scrolling.
956
+ * Extracts transformation values (translate, scale, skew) from the 2D transformation matrix of a given HTML element.
720
957
  *
721
- * ### Behavior
722
- * - Centering is calculated independently for each enabled axis.
723
- * - Translation is applied only when the container content overflows on that axis.
724
- * - When no overflow exists, the container remains untransformed for that axis.
958
+ * Only works with 2D transforms (i.e., `matrix(a, b, c, d, e, f)`).
725
959
  *
726
- * ### Notes
727
- * - This function performs immediate DOM reads and writes.
728
- * - The resulting transform is clamped to valid scrollable bounds.
960
+ * @param element - The element with a CSS transform applied.
961
+ * @returns An object with parsed transformation values.
729
962
  *
730
- * @param containerElement - The container whose content is translated.
731
- * @param elementToCenter - The descendant element to align to the container’s center.
732
- * @param options - Optional configuration controlling which axis or axes are centered.
963
+ * @example
964
+ * ```ts
965
+ * const values = parse2DMatrix(myElement);
966
+ * console.log(values.translateX);
967
+ * console.log(values.scaleX);
968
+ * ```
733
969
  */
734
- const centerElementInContainer = (containerElement, elementToCenter, { axis = 'both' } = {}) => {
735
- let translateX = 0;
736
- let translateY = 0;
737
- if (axis === 'x' || axis === 'both') {
738
- translateX = calculateCenterOffset({
739
- overflowSize: getXOverflowWidth(containerElement),
740
- containerSize: containerElement.clientWidth,
741
- elementOffset: elementToCenter.offsetLeft,
742
- elementSize: elementToCenter.clientWidth,
743
- });
744
- }
745
- if (axis === 'y' || axis === 'both') {
746
- translateY = calculateCenterOffset({
747
- overflowSize: getYOverflowHeight(containerElement),
748
- containerSize: containerElement.clientHeight,
749
- elementOffset: elementToCenter.offsetTop,
750
- elementSize: elementToCenter.clientHeight,
751
- });
970
+ const parse2DMatrix = (element) => {
971
+ const computedStyles = window.getComputedStyle(element);
972
+ const transformValue = computedStyles.getPropertyValue('transform');
973
+ const matrixMatch = transformValue.match(/^matrix\((.+)\)$/);
974
+ if (!matrixMatch) {
975
+ return {
976
+ translateX: 0,
977
+ translateY: 0,
978
+ scaleX: 1,
979
+ scaleY: 1,
980
+ skewX: 0,
981
+ skewY: 0,
982
+ };
752
983
  }
753
- containerElement.style.transform = `translate(${translateX}px, ${translateY}px)`;
984
+ const [scaleX, skewY, skewX, scaleY, translateX, translateY] = matrixMatch[1]
985
+ .split(', ')
986
+ .map(parseFloat);
987
+ return {
988
+ translateX,
989
+ translateY,
990
+ scaleX,
991
+ scaleY,
992
+ skewX,
993
+ skewY,
994
+ };
754
995
  };
996
+
997
+
998
+ /***/ }),
999
+
1000
+ /***/ "./src/env/index.ts":
1001
+ /*!**************************!*\
1002
+ !*** ./src/env/index.ts ***!
1003
+ \**************************/
1004
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1005
+
1006
+ __webpack_require__.r(__webpack_exports__);
1007
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1008
+ /* harmony export */ getLocalStorageCapabilities: () => (/* reexport safe */ _storage__WEBPACK_IMPORTED_MODULE_0__.getLocalStorageCapabilities),
1009
+ /* harmony export */ isLocalStorageReadable: () => (/* reexport safe */ _storage__WEBPACK_IMPORTED_MODULE_0__.isLocalStorageReadable)
1010
+ /* harmony export */ });
1011
+ /* harmony import */ var _storage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./storage */ "./src/env/storage/index.ts");
1012
+
1013
+
1014
+
1015
+ /***/ }),
1016
+
1017
+ /***/ "./src/env/storage/index.ts":
1018
+ /*!**********************************!*\
1019
+ !*** ./src/env/storage/index.ts ***!
1020
+ \**********************************/
1021
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1022
+
1023
+ __webpack_require__.r(__webpack_exports__);
1024
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1025
+ /* harmony export */ getLocalStorageCapabilities: () => (/* reexport safe */ _local_storage__WEBPACK_IMPORTED_MODULE_0__.getLocalStorageCapabilities),
1026
+ /* harmony export */ isLocalStorageReadable: () => (/* reexport safe */ _local_storage__WEBPACK_IMPORTED_MODULE_0__.isLocalStorageReadable)
1027
+ /* harmony export */ });
1028
+ /* harmony import */ var _local_storage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./local-storage */ "./src/env/storage/local-storage.ts");
1029
+
1030
+
1031
+
1032
+ /***/ }),
1033
+
1034
+ /***/ "./src/env/storage/local-storage.ts":
1035
+ /*!******************************************!*\
1036
+ !*** ./src/env/storage/local-storage.ts ***!
1037
+ \******************************************/
1038
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1039
+
1040
+ __webpack_require__.r(__webpack_exports__);
1041
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1042
+ /* harmony export */ getLocalStorageCapabilities: () => (/* binding */ getLocalStorageCapabilities),
1043
+ /* harmony export */ isLocalStorageReadable: () => (/* binding */ isLocalStorageReadable)
1044
+ /* harmony export */ });
755
1045
  /**
756
1046
  * Determines whether the browser environment allows safe read access to
757
1047
  * `localStorage`. Some platforms (e.g., Safari Private Mode, sandboxed iframes)
@@ -1239,6 +1529,72 @@ const once = (fn) => {
1239
1529
  };
1240
1530
 
1241
1531
 
1532
+ /***/ }),
1533
+
1534
+ /***/ "./src/geometry/index.ts":
1535
+ /*!*******************************!*\
1536
+ !*** ./src/geometry/index.ts ***!
1537
+ \*******************************/
1538
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1539
+
1540
+ __webpack_require__.r(__webpack_exports__);
1541
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1542
+ /* harmony export */ calculateCenterOffset: () => (/* reexport safe */ _layout__WEBPACK_IMPORTED_MODULE_0__.calculateCenterOffset)
1543
+ /* harmony export */ });
1544
+ /* harmony import */ var _layout__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./layout */ "./src/geometry/layout/index.ts");
1545
+
1546
+
1547
+
1548
+ /***/ }),
1549
+
1550
+ /***/ "./src/geometry/layout/calculate-center-offset.ts":
1551
+ /*!********************************************************!*\
1552
+ !*** ./src/geometry/layout/calculate-center-offset.ts ***!
1553
+ \********************************************************/
1554
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1555
+
1556
+ __webpack_require__.r(__webpack_exports__);
1557
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1558
+ /* harmony export */ calculateCenterOffset: () => (/* binding */ calculateCenterOffset)
1559
+ /* harmony export */ });
1560
+ /**
1561
+ * Calculates the offset required to center an element within a container along a single axis.
1562
+ *
1563
+ * The returned value is clamped so that the resulting translation does not
1564
+ * exceed the container's scrollable bounds.
1565
+ *
1566
+ * This function performs pure math only and does not access the DOM.
1567
+ *
1568
+ * @returns A negative offset value suitable for use in a CSS `translate`
1569
+ * transform, or `0` when no overflow exists on the axis.
1570
+ */
1571
+ const calculateCenterOffset = ({ overflowSize, containerSize, elementOffset, elementSize, }) => {
1572
+ if (overflowSize <= 0) {
1573
+ return 0;
1574
+ }
1575
+ const containerCenter = containerSize / 2;
1576
+ const elementCenter = elementOffset + elementSize / 2;
1577
+ const targetOffset = elementCenter - containerCenter;
1578
+ return -Math.max(0, Math.min(targetOffset, overflowSize));
1579
+ };
1580
+
1581
+
1582
+ /***/ }),
1583
+
1584
+ /***/ "./src/geometry/layout/index.ts":
1585
+ /*!**************************************!*\
1586
+ !*** ./src/geometry/layout/index.ts ***!
1587
+ \**************************************/
1588
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1589
+
1590
+ __webpack_require__.r(__webpack_exports__);
1591
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1592
+ /* harmony export */ calculateCenterOffset: () => (/* reexport safe */ _calculate_center_offset__WEBPACK_IMPORTED_MODULE_0__.calculateCenterOffset)
1593
+ /* harmony export */ });
1594
+ /* harmony import */ var _calculate_center_offset__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./calculate-center-offset */ "./src/geometry/layout/calculate-center-offset.ts");
1595
+
1596
+
1597
+
1242
1598
  /***/ }),
1243
1599
 
1244
1600
  /***/ "./src/guards.ts":
@@ -1481,6 +1837,120 @@ const isInteger = (value) => isNumber(value) && Number.isInteger(value);
1481
1837
  const isDecimal = (value) => isFiniteNumber(value) && !Number.isInteger(value);
1482
1838
 
1483
1839
 
1840
+ /***/ }),
1841
+
1842
+ /***/ "./src/intersection/get-dom-rect-intersection-ratio.ts":
1843
+ /*!*************************************************************!*\
1844
+ !*** ./src/intersection/get-dom-rect-intersection-ratio.ts ***!
1845
+ \*************************************************************/
1846
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1847
+
1848
+ __webpack_require__.r(__webpack_exports__);
1849
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1850
+ /* harmony export */ getDOMRectIntersectionRatio: () => (/* binding */ getDOMRectIntersectionRatio)
1851
+ /* harmony export */ });
1852
+ /**
1853
+ * Calculates the intersection ratio between two DOM rectangles.
1854
+ *
1855
+ * The ratio represents the proportion of the `targetRect` that is covered by `sourceRect`.
1856
+ * A value of `1` means `sourceRect` completely covers `targetRect`, and `0` means no overlap.
1857
+ *
1858
+ * @param sourceRect - The rectangle used to measure overlap against the target.
1859
+ * @param targetRect - The rectangle whose covered area is measured.
1860
+ *
1861
+ * @returns A number between `0` and `1` representing the intersection ratio.
1862
+ */
1863
+ const getDOMRectIntersectionRatio = (sourceRect, targetRect) => {
1864
+ const xOverlap = Math.max(0, Math.min(sourceRect.right, targetRect.right) - Math.max(sourceRect.left, targetRect.left));
1865
+ const yOverlap = Math.max(0, Math.min(sourceRect.bottom, targetRect.bottom) - Math.max(sourceRect.top, targetRect.top));
1866
+ const intersectionArea = xOverlap * yOverlap;
1867
+ const targetArea = targetRect.width * targetRect.height;
1868
+ return intersectionArea / targetArea;
1869
+ };
1870
+
1871
+
1872
+ /***/ }),
1873
+
1874
+ /***/ "./src/intersection/index.ts":
1875
+ /*!***********************************!*\
1876
+ !*** ./src/intersection/index.ts ***!
1877
+ \***********************************/
1878
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1879
+
1880
+ __webpack_require__.r(__webpack_exports__);
1881
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1882
+ /* harmony export */ getDOMRectIntersectionRatio: () => (/* reexport safe */ _get_dom_rect_intersection_ratio__WEBPACK_IMPORTED_MODULE_0__.getDOMRectIntersectionRatio),
1883
+ /* harmony export */ resolveAxisDelta: () => (/* reexport safe */ _resolve_axis_delta__WEBPACK_IMPORTED_MODULE_1__.resolveAxisDelta)
1884
+ /* harmony export */ });
1885
+ /* harmony import */ var _get_dom_rect_intersection_ratio__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./get-dom-rect-intersection-ratio */ "./src/intersection/get-dom-rect-intersection-ratio.ts");
1886
+ /* harmony import */ var _resolve_axis_delta__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./resolve-axis-delta */ "./src/intersection/resolve-axis-delta.ts");
1887
+
1888
+
1889
+
1890
+
1891
+ /***/ }),
1892
+
1893
+ /***/ "./src/intersection/resolve-axis-delta.ts":
1894
+ /*!************************************************!*\
1895
+ !*** ./src/intersection/resolve-axis-delta.ts ***!
1896
+ \************************************************/
1897
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1898
+
1899
+ __webpack_require__.r(__webpack_exports__);
1900
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1901
+ /* harmony export */ resolveAxisDelta: () => (/* binding */ resolveAxisDelta)
1902
+ /* harmony export */ });
1903
+ /**
1904
+ * Resolves a raw two-dimensional input delta into an axis-aligned delta
1905
+ * according to the specified axis and resolution rules.
1906
+ *
1907
+ * This function is input-agnostic and does not perform any DOM mutations.
1908
+ * It is intended to be used as a normalization step for wheel, drag,
1909
+ * touch, or other pointer-based input before applying scroll physics.
1910
+ *
1911
+ * ### Resolution rules
1912
+ * - `'x'`:
1913
+ * - Uses horizontal input when available.
1914
+ * - Optionally falls back to vertical input when horizontal input is zero.
1915
+ * - `'y'`:
1916
+ * - Uses vertical input only.
1917
+ * - `'both'`:
1918
+ * - Preserves both horizontal and vertical input.
1919
+ *
1920
+ * ### Direction handling
1921
+ * - When `invert` is enabled, the resolved delta direction is inverted
1922
+ * to match typical synthetic scrolling behavior.
1923
+ *
1924
+ * @param delta - Raw input movement delta.
1925
+ * @param axis - Axis along which movement should be resolved.
1926
+ * @param options - Resolution behavior configuration.
1927
+ *
1928
+ * @returns A normalized, axis-aligned delta suitable for further scroll or movement processing.
1929
+ */
1930
+ const resolveAxisDelta = (delta, axis, { allowFallback = true, invert = true } = {}) => {
1931
+ const sign = invert ? -1 : 1;
1932
+ switch (axis) {
1933
+ case 'x': {
1934
+ const value = delta.deltaX !== 0 ? delta.deltaX : allowFallback ? delta.deltaY : 0;
1935
+ return {
1936
+ deltaX: sign * value,
1937
+ deltaY: 0,
1938
+ };
1939
+ }
1940
+ case 'y':
1941
+ return {
1942
+ deltaX: 0,
1943
+ deltaY: sign * delta.deltaY,
1944
+ };
1945
+ default:
1946
+ return {
1947
+ deltaX: sign * delta.deltaX,
1948
+ deltaY: sign * delta.deltaY,
1949
+ };
1950
+ }
1951
+ };
1952
+
1953
+
1484
1954
  /***/ }),
1485
1955
 
1486
1956
  /***/ "./src/math.ts":
@@ -1779,18 +2249,17 @@ var __webpack_exports__ = {};
1779
2249
  \**********************/
1780
2250
  __webpack_require__.r(__webpack_exports__);
1781
2251
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1782
- /* harmony export */ FOCUSABLE_HTML_TAGS: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.FOCUSABLE_HTML_TAGS),
2252
+ /* harmony export */ FOCUSABLE_HTML_TAGS: () => (/* reexport safe */ _a11y__WEBPACK_IMPORTED_MODULE_11__.FOCUSABLE_HTML_TAGS),
1783
2253
  /* harmony export */ assert: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.assert),
1784
2254
  /* harmony export */ blobToFile: () => (/* reexport safe */ _file__WEBPACK_IMPORTED_MODULE_7__.blobToFile),
1785
- /* harmony export */ calculateCenterOffset: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.calculateCenterOffset),
2255
+ /* harmony export */ calculateCenterOffset: () => (/* reexport safe */ _geometry__WEBPACK_IMPORTED_MODULE_9__.calculateCenterOffset),
1786
2256
  /* harmony export */ calculateEuclideanDistance: () => (/* reexport safe */ _math__WEBPACK_IMPORTED_MODULE_5__.calculateEuclideanDistance),
1787
2257
  /* harmony export */ calculateMovingSpeed: () => (/* reexport safe */ _math__WEBPACK_IMPORTED_MODULE_5__.calculateMovingSpeed),
1788
2258
  /* harmony export */ calculatePercentage: () => (/* reexport safe */ _math__WEBPACK_IMPORTED_MODULE_5__.calculatePercentage),
1789
2259
  /* harmony export */ camelToDashCase: () => (/* reexport safe */ _string__WEBPACK_IMPORTED_MODULE_1__.camelToDashCase),
1790
2260
  /* harmony export */ camelToWords: () => (/* reexport safe */ _string__WEBPACK_IMPORTED_MODULE_1__.camelToWords),
1791
- /* harmony export */ centerElementInContainer: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.centerElementInContainer),
1792
2261
  /* harmony export */ chunk: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_2__.chunk),
1793
- /* harmony export */ cloneBlob: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.cloneBlob),
2262
+ /* harmony export */ cloneBlob: () => (/* reexport safe */ _dom_dom__WEBPACK_IMPORTED_MODULE_6__.cloneBlob),
1794
2263
  /* harmony export */ compact: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_2__.compact),
1795
2264
  /* harmony export */ compose: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_2__.compose),
1796
2265
  /* harmony export */ definedProps: () => (/* reexport safe */ _object__WEBPACK_IMPORTED_MODULE_8__.definedProps),
@@ -1801,22 +2270,18 @@ __webpack_require__.r(__webpack_exports__);
1801
2270
  /* harmony export */ filterParallel: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_0__.filterParallel),
1802
2271
  /* harmony export */ filterSequential: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_0__.filterSequential),
1803
2272
  /* harmony export */ findAsync: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_0__.findAsync),
1804
- /* harmony export */ getDOMRectIntersectionRatio: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.getDOMRectIntersectionRatio),
1805
- /* harmony export */ getElementOffsetRect: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.getElementOffsetRect),
1806
- /* harmony export */ getFocusableHtmlElements: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.getFocusableHtmlElements),
1807
- /* harmony export */ getLocalStorageCapabilities: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.getLocalStorageCapabilities),
1808
- /* harmony export */ getXOverflowWidth: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.getXOverflowWidth),
1809
- /* harmony export */ getYOverflowHeight: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.getYOverflowHeight),
1810
- /* harmony export */ hasXOverflow: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.hasXOverflow),
1811
- /* harmony export */ hasYOverflow: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.hasYOverflow),
2273
+ /* harmony export */ getDOMRectIntersectionRatio: () => (/* reexport safe */ _intersection__WEBPACK_IMPORTED_MODULE_10__.getDOMRectIntersectionRatio),
2274
+ /* harmony export */ getElementOffsetRect: () => (/* reexport safe */ _dom_dom__WEBPACK_IMPORTED_MODULE_6__.getElementOffsetRect),
2275
+ /* harmony export */ getFocusableHtmlElements: () => (/* reexport safe */ _a11y__WEBPACK_IMPORTED_MODULE_11__.getFocusableHtmlElements),
2276
+ /* harmony export */ getLocalStorageCapabilities: () => (/* reexport safe */ _env__WEBPACK_IMPORTED_MODULE_12__.getLocalStorageCapabilities),
1812
2277
  /* harmony export */ hashString: () => (/* reexport safe */ _string__WEBPACK_IMPORTED_MODULE_1__.hashString),
1813
2278
  /* harmony export */ intersection: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_2__.intersection),
1814
2279
  /* harmony export */ invokeIfFunction: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.invokeIfFunction),
1815
- /* harmony export */ isAnchorHtmlElement: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.isAnchorHtmlElement),
2280
+ /* harmony export */ isAnchorHtmlElement: () => (/* reexport safe */ _dom_dom__WEBPACK_IMPORTED_MODULE_6__.isAnchorHtmlElement),
1816
2281
  /* harmony export */ isArray: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_2__.isArray),
1817
2282
  /* harmony export */ isBlob: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isBlob),
1818
2283
  /* harmony export */ isBool: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isBool),
1819
- /* harmony export */ isContentEditableHtmlElement: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.isContentEditableHtmlElement),
2284
+ /* harmony export */ isContentEditableHtmlElement: () => (/* reexport safe */ _dom_dom__WEBPACK_IMPORTED_MODULE_6__.isContentEditableHtmlElement),
1820
2285
  /* harmony export */ isDate: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isDate),
1821
2286
  /* harmony export */ isDecimal: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isDecimal),
1822
2287
  /* harmony export */ isDefined: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isDefined),
@@ -1826,9 +2291,9 @@ __webpack_require__.r(__webpack_exports__);
1826
2291
  /* harmony export */ isFile: () => (/* reexport safe */ _file__WEBPACK_IMPORTED_MODULE_7__.isFile),
1827
2292
  /* harmony export */ isFiniteNumber: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isFiniteNumber),
1828
2293
  /* harmony export */ isFunction: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.isFunction),
1829
- /* harmony export */ isHtmlElementFocusable: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.isHtmlElementFocusable),
2294
+ /* harmony export */ isHtmlElementFocusable: () => (/* reexport safe */ _a11y__WEBPACK_IMPORTED_MODULE_11__.isHtmlElementFocusable),
1830
2295
  /* harmony export */ isInteger: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isInteger),
1831
- /* harmony export */ isLocalStorageReadable: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.isLocalStorageReadable),
2296
+ /* harmony export */ isLocalStorageReadable: () => (/* reexport safe */ _env__WEBPACK_IMPORTED_MODULE_12__.isLocalStorageReadable),
1832
2297
  /* harmony export */ isMap: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isMap),
1833
2298
  /* harmony export */ isNil: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isNil),
1834
2299
  /* harmony export */ isNilOrEmptyString: () => (/* reexport safe */ _string__WEBPACK_IMPORTED_MODULE_1__.isNilOrEmptyString),
@@ -1842,15 +2307,15 @@ __webpack_require__.r(__webpack_exports__);
1842
2307
  /* harmony export */ isSymbol: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isSymbol),
1843
2308
  /* harmony export */ isUndefined: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isUndefined),
1844
2309
  /* harmony export */ isValidDate: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isValidDate),
1845
- /* harmony export */ moveFocusWithinContainer: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.moveFocusWithinContainer),
2310
+ /* harmony export */ moveFocusWithinContainer: () => (/* reexport safe */ _a11y__WEBPACK_IMPORTED_MODULE_11__.moveFocusWithinContainer),
1846
2311
  /* harmony export */ noop: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.noop),
1847
2312
  /* harmony export */ not: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.not),
1848
2313
  /* harmony export */ once: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.once),
1849
- /* harmony export */ parse2DMatrix: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.parse2DMatrix),
1850
2314
  /* harmony export */ parseFileName: () => (/* reexport safe */ _file__WEBPACK_IMPORTED_MODULE_7__.parseFileName),
1851
2315
  /* harmony export */ pipe: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_2__.pipe),
1852
2316
  /* harmony export */ readFilesFromDataTransfer: () => (/* reexport safe */ _file__WEBPACK_IMPORTED_MODULE_7__.readFilesFromDataTransfer),
1853
2317
  /* harmony export */ reduceAsync: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_0__.reduceAsync),
2318
+ /* harmony export */ resolveAxisDelta: () => (/* reexport safe */ _intersection__WEBPACK_IMPORTED_MODULE_10__.resolveAxisDelta),
1854
2319
  /* harmony export */ retry: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.retry),
1855
2320
  /* harmony export */ runParallel: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_0__.runParallel),
1856
2321
  /* harmony export */ runSequential: () => (/* reexport safe */ _async__WEBPACK_IMPORTED_MODULE_0__.runSequential),
@@ -1867,9 +2332,17 @@ __webpack_require__.r(__webpack_exports__);
1867
2332
  /* harmony import */ var _function__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./function */ "./src/function.ts");
1868
2333
  /* harmony import */ var _guards__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./guards */ "./src/guards.ts");
1869
2334
  /* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./math */ "./src/math.ts");
1870
- /* harmony import */ var _dom__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./dom */ "./src/dom.ts");
2335
+ /* harmony import */ var _dom_dom__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./dom/dom */ "./src/dom/dom.ts");
1871
2336
  /* harmony import */ var _file__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./file */ "./src/file.ts");
1872
2337
  /* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./object */ "./src/object.ts");
2338
+ /* harmony import */ var _geometry__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./geometry */ "./src/geometry/index.ts");
2339
+ /* harmony import */ var _intersection__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./intersection */ "./src/intersection/index.ts");
2340
+ /* harmony import */ var _a11y__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./a11y */ "./src/a11y/index.ts");
2341
+ /* harmony import */ var _env__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./env */ "./src/env/index.ts");
2342
+
2343
+
2344
+
2345
+
1873
2346
 
1874
2347
 
1875
2348