@jsenv/dom 0.6.0 → 0.7.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.
- package/dist/jsenv_dom.js +262 -330
- package/package.json +2 -4
- package/index.js +0 -124
- package/src/attr/add_attribute_effect.js +0 -93
- package/src/attr/attributes.js +0 -32
- package/src/color/color_constrast.js +0 -69
- package/src/color/color_parsing.js +0 -319
- package/src/color/color_scheme.js +0 -28
- package/src/color/pick_light_or_dark.js +0 -34
- package/src/color/resolve_css_color.js +0 -60
- package/src/demos/3_columns_resize_demo.html +0 -84
- package/src/demos/3_rows_resize_demo.html +0 -89
- package/src/demos/aside_and_main_demo.html +0 -93
- package/src/demos/coordinates_demo.html +0 -450
- package/src/demos/document_autoscroll_demo.html +0 -517
- package/src/demos/drag_gesture_constraints_demo.html +0 -701
- package/src/demos/drag_gesture_demo.html +0 -1047
- package/src/demos/drag_gesture_element_to_impact_demo.html +0 -445
- package/src/demos/drag_reference_element_demo.html +0 -480
- package/src/demos/flex_details_set_demo.html +0 -302
- package/src/demos/flex_details_set_demo_2.html +0 -315
- package/src/demos/visible_rect_demo.html +0 -525
- package/src/element_signature.js +0 -100
- package/src/interaction/drag/constraint_feedback_line.js +0 -92
- package/src/interaction/drag/drag_constraint.js +0 -659
- package/src/interaction/drag/drag_debug_markers.js +0 -635
- package/src/interaction/drag/drag_element_positioner.js +0 -382
- package/src/interaction/drag/drag_gesture.js +0 -566
- package/src/interaction/drag/drag_resize_demo.html +0 -571
- package/src/interaction/drag/drag_to_move.js +0 -301
- package/src/interaction/drag/drag_to_resize_gesture.js +0 -68
- package/src/interaction/drag/drop_target_detection.js +0 -148
- package/src/interaction/drag/sticky_frontiers.js +0 -160
- package/src/interaction/event_marker.js +0 -14
- package/src/interaction/focus/active_element.js +0 -33
- package/src/interaction/focus/arrow_navigation.js +0 -599
- package/src/interaction/focus/element_is_focusable.js +0 -57
- package/src/interaction/focus/element_visibility.js +0 -111
- package/src/interaction/focus/find_focusable.js +0 -21
- package/src/interaction/focus/focus_group.js +0 -91
- package/src/interaction/focus/focus_group_registry.js +0 -12
- package/src/interaction/focus/focus_nav.js +0 -12
- package/src/interaction/focus/focus_nav_event_marker.js +0 -14
- package/src/interaction/focus/focus_trap.js +0 -105
- package/src/interaction/focus/tab_navigation.js +0 -128
- package/src/interaction/focus/tests/focus_group_skip_tab_test.html +0 -206
- package/src/interaction/focus/tests/tree_focus_test.html +0 -304
- package/src/interaction/focus/tests/tree_focus_test.jsx +0 -261
- package/src/interaction/focus/tests/tree_focus_test_preact.html +0 -13
- package/src/interaction/isolate_interactions.js +0 -161
- package/src/interaction/keyboard.js +0 -26
- package/src/interaction/scroll/capture_scroll.js +0 -47
- package/src/interaction/scroll/is_scrollable.js +0 -159
- package/src/interaction/scroll/scroll_container.js +0 -110
- package/src/interaction/scroll/scroll_trap.js +0 -44
- package/src/interaction/scroll/scrollbar_size.js +0 -20
- package/src/interaction/scroll/wheel_through.js +0 -138
- package/src/iterable_weak_set.js +0 -66
- package/src/position/dom_coords.js +0 -340
- package/src/position/offset_parent.js +0 -15
- package/src/position/position_fixed.js +0 -15
- package/src/position/position_sticky.js +0 -213
- package/src/position/sticky_rect.js +0 -79
- package/src/position/visible_rect.js +0 -486
- package/src/pub_sub.js +0 -31
- package/src/size/can_take_size.js +0 -11
- package/src/size/details_content_full_height.js +0 -63
- package/src/size/flex_details_set.js +0 -974
- package/src/size/get_available_height.js +0 -22
- package/src/size/get_available_width.js +0 -22
- package/src/size/get_border_sizes.js +0 -14
- package/src/size/get_height.js +0 -4
- package/src/size/get_inner_height.js +0 -15
- package/src/size/get_inner_width.js +0 -15
- package/src/size/get_margin_sizes.js +0 -10
- package/src/size/get_max_height.js +0 -57
- package/src/size/get_max_width.js +0 -47
- package/src/size/get_min_height.js +0 -14
- package/src/size/get_min_width.js +0 -14
- package/src/size/get_padding_sizes.js +0 -10
- package/src/size/get_width.js +0 -4
- package/src/size/hooks/use_available_height.js +0 -27
- package/src/size/hooks/use_available_width.js +0 -27
- package/src/size/hooks/use_max_height.js +0 -10
- package/src/size/hooks/use_max_width.js +0 -10
- package/src/size/hooks/use_resize_status.js +0 -62
- package/src/size/resize.js +0 -695
- package/src/size/resolve_css_size.js +0 -32
- package/src/style/dom_styles.js +0 -97
- package/src/style/style_composition.js +0 -121
- package/src/style/style_controller.js +0 -345
- package/src/style/style_default.js +0 -153
- package/src/style/style_default_demo.html +0 -128
- package/src/style/style_parsing.js +0 -375
- package/src/transition/demos/animation_resumption_test.xhtml +0 -500
- package/src/transition/demos/height_toggle_test.xhtml +0 -515
- package/src/transition/dom_transition.js +0 -254
- package/src/transition/easing.js +0 -48
- package/src/transition/group_transition.js +0 -261
- package/src/transition/transform_style_parser.js +0 -32
- package/src/transition/transition_playback.js +0 -366
- package/src/transition/transition_timeline.js +0 -79
- package/src/traversal.js +0 -247
- package/src/ui_transition/demos/content_states_transition_demo.html +0 -628
- package/src/ui_transition/demos/smooth_height_transition_demo.html +0 -149
- package/src/ui_transition/demos/transition_testing.html +0 -354
- package/src/ui_transition/ui_transition.js +0 -1491
- package/src/utils.js +0 -69
- 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
|
-
};
|