@jsenv/dom 0.6.1 → 0.7.1

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 (109) hide show
  1. package/dist/jsenv_dom.js +339 -327
  2. package/package.json +2 -4
  3. package/index.js +0 -124
  4. package/src/attr/add_attribute_effect.js +0 -93
  5. package/src/attr/attributes.js +0 -32
  6. package/src/color/color_constrast.js +0 -69
  7. package/src/color/color_parsing.js +0 -319
  8. package/src/color/color_scheme.js +0 -28
  9. package/src/color/pick_light_or_dark.js +0 -34
  10. package/src/color/resolve_css_color.js +0 -60
  11. package/src/demos/3_columns_resize_demo.html +0 -84
  12. package/src/demos/3_rows_resize_demo.html +0 -89
  13. package/src/demos/aside_and_main_demo.html +0 -93
  14. package/src/demos/coordinates_demo.html +0 -450
  15. package/src/demos/document_autoscroll_demo.html +0 -517
  16. package/src/demos/drag_gesture_constraints_demo.html +0 -701
  17. package/src/demos/drag_gesture_demo.html +0 -1047
  18. package/src/demos/drag_gesture_element_to_impact_demo.html +0 -445
  19. package/src/demos/drag_reference_element_demo.html +0 -480
  20. package/src/demos/flex_details_set_demo.html +0 -302
  21. package/src/demos/flex_details_set_demo_2.html +0 -315
  22. package/src/demos/visible_rect_demo.html +0 -525
  23. package/src/element_signature.js +0 -100
  24. package/src/interaction/drag/constraint_feedback_line.js +0 -92
  25. package/src/interaction/drag/drag_constraint.js +0 -659
  26. package/src/interaction/drag/drag_debug_markers.js +0 -635
  27. package/src/interaction/drag/drag_element_positioner.js +0 -382
  28. package/src/interaction/drag/drag_gesture.js +0 -566
  29. package/src/interaction/drag/drag_resize_demo.html +0 -571
  30. package/src/interaction/drag/drag_to_move.js +0 -301
  31. package/src/interaction/drag/drag_to_resize_gesture.js +0 -68
  32. package/src/interaction/drag/drop_target_detection.js +0 -148
  33. package/src/interaction/drag/sticky_frontiers.js +0 -160
  34. package/src/interaction/event_marker.js +0 -14
  35. package/src/interaction/focus/active_element.js +0 -33
  36. package/src/interaction/focus/arrow_navigation.js +0 -599
  37. package/src/interaction/focus/element_is_focusable.js +0 -57
  38. package/src/interaction/focus/element_visibility.js +0 -111
  39. package/src/interaction/focus/find_focusable.js +0 -21
  40. package/src/interaction/focus/focus_group.js +0 -91
  41. package/src/interaction/focus/focus_group_registry.js +0 -12
  42. package/src/interaction/focus/focus_nav.js +0 -12
  43. package/src/interaction/focus/focus_nav_event_marker.js +0 -14
  44. package/src/interaction/focus/focus_trap.js +0 -105
  45. package/src/interaction/focus/tab_navigation.js +0 -128
  46. package/src/interaction/focus/tests/focus_group_skip_tab_test.html +0 -206
  47. package/src/interaction/focus/tests/tree_focus_test.html +0 -304
  48. package/src/interaction/focus/tests/tree_focus_test.jsx +0 -261
  49. package/src/interaction/focus/tests/tree_focus_test_preact.html +0 -13
  50. package/src/interaction/isolate_interactions.js +0 -161
  51. package/src/interaction/keyboard.js +0 -26
  52. package/src/interaction/scroll/capture_scroll.js +0 -47
  53. package/src/interaction/scroll/is_scrollable.js +0 -159
  54. package/src/interaction/scroll/scroll_container.js +0 -110
  55. package/src/interaction/scroll/scroll_trap.js +0 -44
  56. package/src/interaction/scroll/scrollbar_size.js +0 -20
  57. package/src/interaction/scroll/wheel_through.js +0 -138
  58. package/src/iterable_weak_set.js +0 -66
  59. package/src/position/dom_coords.js +0 -340
  60. package/src/position/offset_parent.js +0 -15
  61. package/src/position/position_fixed.js +0 -15
  62. package/src/position/position_sticky.js +0 -213
  63. package/src/position/sticky_rect.js +0 -79
  64. package/src/position/visible_rect.js +0 -486
  65. package/src/pub_sub.js +0 -31
  66. package/src/size/can_take_size.js +0 -11
  67. package/src/size/details_content_full_height.js +0 -63
  68. package/src/size/flex_details_set.js +0 -974
  69. package/src/size/get_available_height.js +0 -22
  70. package/src/size/get_available_width.js +0 -22
  71. package/src/size/get_border_sizes.js +0 -14
  72. package/src/size/get_height.js +0 -4
  73. package/src/size/get_inner_height.js +0 -15
  74. package/src/size/get_inner_width.js +0 -15
  75. package/src/size/get_margin_sizes.js +0 -10
  76. package/src/size/get_max_height.js +0 -57
  77. package/src/size/get_max_width.js +0 -47
  78. package/src/size/get_min_height.js +0 -14
  79. package/src/size/get_min_width.js +0 -14
  80. package/src/size/get_padding_sizes.js +0 -10
  81. package/src/size/get_width.js +0 -4
  82. package/src/size/hooks/use_available_height.js +0 -27
  83. package/src/size/hooks/use_available_width.js +0 -27
  84. package/src/size/hooks/use_max_height.js +0 -10
  85. package/src/size/hooks/use_max_width.js +0 -10
  86. package/src/size/hooks/use_resize_status.js +0 -62
  87. package/src/size/resize.js +0 -695
  88. package/src/size/resolve_css_size.js +0 -32
  89. package/src/style/dom_styles.js +0 -97
  90. package/src/style/style_composition.js +0 -121
  91. package/src/style/style_controller.js +0 -345
  92. package/src/style/style_default.js +0 -153
  93. package/src/style/style_default_demo.html +0 -128
  94. package/src/style/style_parsing.js +0 -375
  95. package/src/transition/demos/animation_resumption_test.xhtml +0 -500
  96. package/src/transition/demos/height_toggle_test.xhtml +0 -515
  97. package/src/transition/dom_transition.js +0 -254
  98. package/src/transition/easing.js +0 -48
  99. package/src/transition/group_transition.js +0 -261
  100. package/src/transition/transform_style_parser.js +0 -32
  101. package/src/transition/transition_playback.js +0 -366
  102. package/src/transition/transition_timeline.js +0 -79
  103. package/src/traversal.js +0 -247
  104. package/src/ui_transition/demos/content_states_transition_demo.html +0 -628
  105. package/src/ui_transition/demos/smooth_height_transition_demo.html +0 -149
  106. package/src/ui_transition/demos/transition_testing.html +0 -354
  107. package/src/ui_transition/ui_transition.js +0 -1470
  108. package/src/utils.js +0 -69
  109. package/src/value_effect.js +0 -35
@@ -1,161 +0,0 @@
1
- import { setAttributes } from "../attr/attributes.js";
2
- import { getAssociatedElements } from "../utils.js";
3
-
4
- /**
5
- * Isolates user interactions to only the specified elements, making everything else non-interactive.
6
- *
7
- * This creates a controlled interaction environment where only the target elements (and their ancestors)
8
- * can receive user input like clicks, keyboard events, focus, etc. All other DOM elements become
9
- * non-interactive, preventing conflicting or unwanted interactions during critical operations
10
- * like drag gestures, modal dialogs, or complex UI states.
11
- *
12
- * The function uses the `inert` attribute to achieve this isolation, applying it strategically
13
- * to parts of the DOM tree while preserving the interactive elements and their ancestor chains.
14
- *
15
- * Example DOM structure and inert application:
16
- *
17
- * Before calling isolateInteractions:
18
- * ```
19
- * <body>
20
- * <header>...</header>
21
- * <main>
22
- * <div>
23
- * <span>some content</span>
24
- * <div class="modal">modal content</div>
25
- * <span>more content</span>
26
- * </div>
27
- * <aside inert>already inert</aside>
28
- * <div class="dropdown">dropdown menu</div>
29
- * </main>
30
- * <footer>...</footer>
31
- * </body>
32
- * ```
33
- *
34
- * After calling isolateInteractions([modal, dropdown]):
35
- * ```
36
- * <body>
37
- * <header inert>...</header> ← made inert (no active descendants)
38
- * <main> ← not inert because it contains active elements
39
- * <div> ← not inert because it contains .modal
40
- * <span inert>some content</span> ← made inert selectively
41
- * <div class="modal">modal content</div> ← stays active
42
- * <span inert>more content</span> ← made inert selectively
43
- * </div>
44
- * <aside inert>already inert</aside>
45
- * <div class="dropdown">dropdown menu</div> ← stays active
46
- * </main>
47
- * <footer inert>...</footer>
48
- * </body>
49
- * ```
50
- *
51
- * After calling cleanup():
52
- * ```
53
- * <body>
54
- * <header>...</header>
55
- * <main>
56
- * <div>
57
- * <span>some content</span>
58
- * <div class="modal">modal content</div>
59
- * <span>more content</span>
60
- * </div>
61
- * <aside inert>already inert</aside> ← [inert] preserved
62
- * <div class="dropdown">dropdown menu</div>
63
- * </main>
64
- * <footer>...</footer>
65
- * </body>
66
- * ```
67
- *
68
- * @param {Array<Element>} elements - Array of elements to keep interactive (non-inert)
69
- * @returns {Function} cleanup - Function to restore original inert states
70
- */
71
- export const isolateInteractions = (elements) => {
72
- const cleanupCallbackSet = new Set();
73
- const cleanup = () => {
74
- for (const cleanupCallback of cleanupCallbackSet) {
75
- cleanupCallback();
76
- }
77
- cleanupCallbackSet.clear();
78
- };
79
-
80
- const toKeepInteractiveSet = new Set();
81
- const keepSelfAndAncestors = (el) => {
82
- if (toKeepInteractiveSet.has(el)) {
83
- return;
84
- }
85
- const associatedElements = getAssociatedElements(el);
86
- if (associatedElements) {
87
- for (const associatedElement of associatedElements) {
88
- keepSelfAndAncestors(associatedElement);
89
- }
90
- }
91
-
92
- // Add the element itself
93
- toKeepInteractiveSet.add(el);
94
- // Add all its ancestors up to document.body
95
- let ancestor = el.parentNode;
96
- while (ancestor && ancestor !== document.body) {
97
- toKeepInteractiveSet.add(ancestor);
98
- ancestor = ancestor.parentNode;
99
- }
100
- };
101
-
102
- // Build set of elements to keep interactive
103
- for (const element of elements) {
104
- keepSelfAndAncestors(element);
105
- }
106
- // backdrop elements are meant to control interactions happening at document level
107
- // and should stay interactive
108
- const backdropElements = document.querySelectorAll("[data-backdrop]");
109
- for (const backdropElement of backdropElements) {
110
- keepSelfAndAncestors(backdropElement);
111
- }
112
-
113
- const setInert = (el) => {
114
- if (toKeepInteractiveSet.has(el)) {
115
- // element should stay interactive
116
- return;
117
- }
118
- const restoreAttributes = setAttributes(el, {
119
- inert: "",
120
- });
121
- cleanupCallbackSet.add(() => {
122
- restoreAttributes();
123
- });
124
- };
125
-
126
- const makeElementInertSelectivelyOrCompletely = (el) => {
127
- // If this element should stay interactive, keep it active
128
- if (toKeepInteractiveSet.has(el)) {
129
- return;
130
- }
131
-
132
- // Since we put all ancestors in toKeepInteractiveSet, if this element
133
- // is not in the set, we can check if any of its direct children are.
134
- // If none of the direct children are in the set, then no descendants are either.
135
- const children = Array.from(el.children);
136
- const hasInteractiveChildren = children.some((child) =>
137
- toKeepInteractiveSet.has(child),
138
- );
139
-
140
- if (!hasInteractiveChildren) {
141
- // No interactive descendants, make the entire element inert
142
- setInert(el);
143
- return;
144
- }
145
-
146
- // Some children need to stay interactive, process them selectively
147
- for (const child of children) {
148
- makeElementInertSelectivelyOrCompletely(child);
149
- }
150
- };
151
-
152
- // Apply inert to all top-level elements that aren't in our keep-interactive set
153
- const bodyChildren = Array.from(document.body.children);
154
- for (const child of bodyChildren) {
155
- makeElementInertSelectivelyOrCompletely(child);
156
- }
157
-
158
- return () => {
159
- cleanup();
160
- };
161
- };
@@ -1,26 +0,0 @@
1
- export const canInterceptKeys = (event) => {
2
- const target = event.target;
3
- // Don't handle shortcuts when user is typing
4
- if (
5
- target.tagName === "INPUT" ||
6
- target.tagName === "TEXTAREA" ||
7
- target.contentEditable === "true" ||
8
- target.isContentEditable
9
- ) {
10
- return false;
11
- }
12
- // Don't handle shortcuts when select dropdown is open
13
- if (target.tagName === "SELECT") {
14
- return false;
15
- }
16
- // Don't handle shortcuts when target or container is disabled
17
- if (
18
- target.disabled ||
19
- target.closest("[disabled]") ||
20
- target.inert ||
21
- target.closest("[inert]")
22
- ) {
23
- return false;
24
- }
25
- return true;
26
- };
@@ -1,47 +0,0 @@
1
- // Helper to create scroll state capture/restore function for an element
2
- export const captureScrollState = (element) => {
3
- const scrollLeft = element.scrollLeft;
4
- const scrollTop = element.scrollTop;
5
- const scrollWidth = element.scrollWidth;
6
- const scrollHeight = element.scrollHeight;
7
- const clientWidth = element.clientWidth;
8
- const clientHeight = element.clientHeight;
9
-
10
- // Calculate scroll percentages to preserve relative position
11
- const scrollLeftPercent =
12
- scrollWidth > clientWidth ? scrollLeft / (scrollWidth - clientWidth) : 0;
13
- const scrollTopPercent =
14
- scrollHeight > clientHeight ? scrollTop / (scrollHeight - clientHeight) : 0;
15
-
16
- // Return preserve function that maintains scroll position relative to content
17
- return () => {
18
- // Get current dimensions after DOM changes
19
- const newScrollWidth = element.scrollWidth;
20
- const newScrollHeight = element.scrollHeight;
21
- const newClientWidth = element.clientWidth;
22
- const newClientHeight = element.clientHeight;
23
-
24
- // If content dimensions changed significantly, use percentage-based positioning
25
- if (
26
- Math.abs(newScrollWidth - scrollWidth) > 1 ||
27
- Math.abs(newScrollHeight - scrollHeight) > 1 ||
28
- Math.abs(newClientWidth - clientWidth) > 1 ||
29
- Math.abs(newClientHeight - clientHeight) > 1
30
- ) {
31
- if (newScrollWidth > newClientWidth) {
32
- const newScrollLeft =
33
- scrollLeftPercent * (newScrollWidth - newClientWidth);
34
- element.scrollLeft = newScrollLeft;
35
- }
36
-
37
- if (newScrollHeight > newClientHeight) {
38
- const newScrollTop =
39
- scrollTopPercent * (newScrollHeight - newClientHeight);
40
- element.scrollTop = newScrollTop;
41
- }
42
- } else {
43
- element.scrollLeft = scrollLeft;
44
- element.scrollTop = scrollTop;
45
- }
46
- };
47
- };
@@ -1,159 +0,0 @@
1
- import { getStyle } from "../../style/dom_styles.js";
2
- import { isDocumentElement } from "../../utils.js";
3
-
4
- // note: keep in mind that an element with overflow: 'hidden' is scrollable
5
- // it can be scrolled using keyboard arrows or JavaScript properties such as scrollTop, scrollLeft
6
- // the only overflow that prevents scroll is "visible"
7
- export const isScrollable = (element, { includeHidden } = {}) => {
8
- if (canHaveVerticalScroll(element, { includeHidden })) {
9
- return true;
10
- }
11
- if (canHaveHorizontalScroll(element, { includeHidden })) {
12
- return true;
13
- }
14
- return false;
15
- };
16
-
17
- const canHaveVerticalScroll = (element, { includeHidden }) => {
18
- const verticalOverflow = getStyle(element, "overflow-y");
19
- if (verticalOverflow === "visible") {
20
- // browser returns "visible" on documentElement even if it is scrollable
21
- if (isDocumentElement(element)) {
22
- return true;
23
- }
24
- return false;
25
- }
26
- if (verticalOverflow === "hidden" || verticalOverflow === "clip") {
27
- return includeHidden;
28
- }
29
- const overflow = getStyle(element, "overflow");
30
- if (overflow === "visible") {
31
- // browser returns "visible" on documentElement even if it is scrollable
32
- if (isDocumentElement(element)) {
33
- return true;
34
- }
35
- return false;
36
- }
37
- if (overflow === "hidden" || overflow === "clip") {
38
- return includeHidden;
39
- }
40
- return true; // "auto", "scroll"
41
- };
42
- const canHaveHorizontalScroll = (element, { includeHidden }) => {
43
- const horizontalOverflow = getStyle(element, "overflow-x");
44
- if (horizontalOverflow === "visible") {
45
- // browser returns "visible" on documentElement even if it is scrollable
46
- if (isDocumentElement(element)) {
47
- return true;
48
- }
49
- return false;
50
- }
51
- if (horizontalOverflow === "hidden" || horizontalOverflow === "clip") {
52
- return includeHidden;
53
- }
54
- const overflow = getStyle(element, "overflow");
55
- if (overflow === "visible") {
56
- if (isDocumentElement(element)) {
57
- // browser returns "visible" on documentElement even if it is scrollable
58
- return true;
59
- }
60
- return false;
61
- }
62
- if (overflow === "hidden" || overflow === "clip") {
63
- return includeHidden;
64
- }
65
- return true; // "auto", "scroll"
66
- };
67
-
68
- export const getScrollingElement = (document) => {
69
- const { scrollingElement } = document;
70
- if (scrollingElement) {
71
- return scrollingElement;
72
- }
73
-
74
- if (isCompliant(document)) {
75
- return document.documentElement;
76
- }
77
-
78
- const body = document.body;
79
- const isFrameset = body && !/body/i.test(body.tagName);
80
- const possiblyScrollingElement = isFrameset ? getNextBodyElement(body) : body;
81
-
82
- // If `body` is itself scrollable, it is not the `scrollingElement`.
83
- return possiblyScrollingElement && bodyIsScrollable(possiblyScrollingElement)
84
- ? null
85
- : possiblyScrollingElement;
86
- };
87
-
88
- const isHidden = (element) => {
89
- const display = getStyle(element, "display");
90
- if (display === "none") {
91
- return false;
92
- }
93
-
94
- if (
95
- display === "table-row" ||
96
- display === "table-group" ||
97
- display === "table-column"
98
- ) {
99
- return getStyle(element, "visibility") !== "collapsed";
100
- }
101
-
102
- return true;
103
- };
104
- const isCompliant = (document) => {
105
- // Note: document.compatMode can be toggle at runtime by document.write
106
- const isStandardsMode = /^CSS1/.test(document.compatMode);
107
- if (isStandardsMode) {
108
- return testScrollCompliance(document);
109
- }
110
- return false;
111
- };
112
- const testScrollCompliance = (document) => {
113
- const iframe = document.createElement("iframe");
114
- iframe.style.height = "1px";
115
- const parentNode = document.body || document.documentElement || document;
116
- parentNode.appendChild(iframe);
117
- const iframeDocument = iframe.contentWindow.document;
118
- iframeDocument.write('<!DOCTYPE html><div style="height:9999em">x</div>');
119
- iframeDocument.close();
120
- const scrollComplianceResult =
121
- iframeDocument.documentElement.scrollHeight >
122
- iframeDocument.body.scrollHeight;
123
- iframe.parentNode.removeChild(iframe);
124
- return scrollComplianceResult;
125
- };
126
- const getNextBodyElement = (frameset) => {
127
- // We use this function to be correct per spec in case `document.body` is
128
- // a `frameset` but there exists a later `body`. Since `document.body` is
129
- // a `frameset`, we know the root is an `html`, and there was no `body`
130
- // before the `frameset`, so we just need to look at siblings after the
131
- // `frameset`.
132
- let current = frameset;
133
- while ((current = current.nextSibling)) {
134
- if (current.nodeType === 1 && isBodyElement(current)) {
135
- return current;
136
- }
137
- }
138
- return null;
139
- };
140
- const isBodyElement = (element) => element.ownerDocument.body === element;
141
- const bodyIsScrollable = (body) => {
142
- // a body element is scrollable if body and html are scrollable and rendered
143
- if (!isScrollable(body)) {
144
- return false;
145
- }
146
- if (isHidden(body)) {
147
- return false;
148
- }
149
-
150
- const documentElement = body.ownerDocument.documentElement;
151
- if (!isScrollable(documentElement)) {
152
- return false;
153
- }
154
- if (isHidden(documentElement)) {
155
- return false;
156
- }
157
-
158
- return true;
159
- };
@@ -1,110 +0,0 @@
1
- // https://developer.mozilla.org/en-US/docs/Glossary/Scroll_container
2
-
3
- import { getStyle } from "../../style/dom_styles.js";
4
- import { getScrollingElement, isScrollable } from "./is_scrollable.js";
5
-
6
- const { documentElement } = document;
7
-
8
- export const getScrollContainer = (arg, { includeHidden } = {}) => {
9
- if (typeof arg !== "object" || arg.nodeType !== 1) {
10
- throw new TypeError("getScrollContainer first argument must be DOM node");
11
- }
12
- const element = arg;
13
- if (element === document) {
14
- return null;
15
- }
16
- if (element === documentElement) {
17
- if (isScrollable(element, { includeHidden })) {
18
- return element;
19
- }
20
- return null;
21
- }
22
- const position = getStyle(element, "position");
23
- if (position === "fixed") {
24
- return getScrollingElement(element.ownerDocument);
25
- }
26
- return (
27
- findScrollContainer(element, { includeHidden }) ||
28
- getScrollingElement(element.ownerDocument)
29
- );
30
- };
31
-
32
- const findScrollContainer = (element, { includeHidden } = {}) => {
33
- const position = getStyle(element, "position");
34
- let parent = element.parentNode;
35
- // Si l'élément est en position absolute, d'abord trouver le premier parent positionné
36
- if (position === "absolute") {
37
- while (parent && parent !== document) {
38
- if (parent === documentElement) {
39
- break; // documentElement est considéré comme positionné
40
- }
41
- const parentPosition = getStyle(parent, "position");
42
- if (parentPosition !== "static") {
43
- break; // Trouvé le premier parent positionné
44
- }
45
- parent = parent.parentNode;
46
- }
47
- }
48
-
49
- // Maintenant chercher le premier parent scrollable à partir du parent positionné
50
- while (parent) {
51
- if (parent === document) {
52
- return null;
53
- }
54
- if (isScrollable(parent, { includeHidden })) {
55
- return parent;
56
- }
57
- parent = parent.parentNode;
58
- }
59
- return null;
60
- };
61
-
62
- export const getSelfAndAncestorScrolls = (element, startOnParent) => {
63
- let scrollX = 0;
64
- let scrollY = 0;
65
- const ancestorScrolls = [];
66
- const visitElement = (elementOrScrollContainer) => {
67
- const scrollContainer = getScrollContainer(elementOrScrollContainer);
68
- if (scrollContainer) {
69
- ancestorScrolls.push({
70
- element: elementOrScrollContainer,
71
- scrollContainer,
72
- });
73
- scrollX += scrollContainer.scrollLeft;
74
- scrollY += scrollContainer.scrollTop;
75
- if (scrollContainer === document.documentElement) {
76
- return;
77
- }
78
- visitElement(scrollContainer);
79
- }
80
- };
81
- if (startOnParent) {
82
- if (element === documentElement) {
83
- } else {
84
- visitElement(element.parentNode);
85
- }
86
- } else {
87
- visitElement(element);
88
- }
89
- ancestorScrolls.scrollX = scrollX;
90
- ancestorScrolls.scrollY = scrollY;
91
- return ancestorScrolls;
92
- };
93
-
94
- // https://github.com/shipshapecode/tether/blob/d6817f8c49a7a26b04c45e55589279dd1b5dd2bf/src/js/utils/parents.js#L1
95
- export const getScrollContainerSet = (element) => {
96
- const scrollContainerSet = new Set();
97
- let elementOrScrollContainer = element;
98
- while (true) {
99
- const scrollContainer = getScrollContainer(elementOrScrollContainer);
100
- if (!scrollContainer) {
101
- break;
102
- }
103
- scrollContainerSet.add(scrollContainer);
104
- if (scrollContainer === documentElement) {
105
- break;
106
- }
107
- elementOrScrollContainer = scrollContainer;
108
- }
109
- return scrollContainerSet;
110
- };
@@ -1,44 +0,0 @@
1
- import { getStyle, setStyles } from "../../style/dom_styles.js";
2
- import { isScrollable } from "./is_scrollable.js";
3
- import { getSelfAndAncestorScrolls } from "./scroll_container.js";
4
- import { measureScrollbar } from "./scrollbar_size.js";
5
-
6
- export const trapScrollInside = (element) => {
7
- const cleanupCallbackSet = new Set();
8
- const lockScroll = (el) => {
9
- const [scrollbarWidth, scrollbarHeight] = measureScrollbar(el);
10
- // scrollbar-gutter would work but would display an empty blank space
11
- const paddingRight = parseInt(getStyle(el, "padding-right"), 0);
12
- const paddingTop = parseInt(getStyle(el, "padding-top"), 0);
13
- const removeScrollLockStyles = setStyles(el, {
14
- "padding-right": `${paddingRight + scrollbarWidth}px`,
15
- "padding-top": `${paddingTop + scrollbarHeight}px`,
16
- "overflow": "hidden",
17
- });
18
- cleanupCallbackSet.add(() => {
19
- removeScrollLockStyles();
20
- });
21
- };
22
- let previous = element.previousSibling;
23
- while (previous) {
24
- if (previous.nodeType === 1) {
25
- if (isScrollable(previous)) {
26
- lockScroll(previous);
27
- }
28
- }
29
- previous = previous.previousSibling;
30
- }
31
-
32
- const selfAndAncestorScrolls = getSelfAndAncestorScrolls(element);
33
- for (const selfOrAncestorScroll of selfAndAncestorScrolls) {
34
- const elementToScrollLock = selfOrAncestorScroll.scrollContainer;
35
- lockScroll(elementToScrollLock);
36
- }
37
-
38
- return () => {
39
- for (const cleanupCallback of cleanupCallbackSet) {
40
- cleanupCallback();
41
- }
42
- cleanupCallbackSet.clear();
43
- };
44
- };
@@ -1,20 +0,0 @@
1
- // https://davidwalsh.name/detect-scrollbar-width
2
- export const measureScrollbar = (scrollableElement) => {
3
- const hasXScrollbar =
4
- scrollableElement.scrollHeight > scrollableElement.clientHeight;
5
- const hasYScrollbar =
6
- scrollableElement.scrollWidth > scrollableElement.clientWidth;
7
- if (!hasXScrollbar && !hasYScrollbar) {
8
- return [0, 0];
9
- }
10
- const scrollDiv = document.createElement("div");
11
- scrollDiv.style.cssText = `position: absolute; width: 100px; height: 100px; overflow: scroll; pointer-events: none; visibility: hidden;`;
12
- scrollableElement.appendChild(scrollDiv);
13
- const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
14
- const scrollbarHeight = scrollDiv.offsetHeight - scrollDiv.clientHeight;
15
- scrollableElement.removeChild(scrollDiv);
16
- return [
17
- hasXScrollbar ? scrollbarWidth : 0,
18
- hasYScrollbar ? scrollbarHeight : 0,
19
- ];
20
- };