@jsenv/dom 0.6.1 → 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 +259 -314
- 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 -1470
- package/src/utils.js +0 -69
- package/src/value_effect.js +0 -35
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates intuitive scrolling behavior when scrolling over an element that needs to stay interactive
|
|
3
|
-
* (we can't use pointer-events: none). Instead of scrolling the document unexpectedly,
|
|
4
|
-
* finds and scrolls the appropriate scrollable container behind the overlay.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { isScrollable } from "./is_scrollable.js";
|
|
8
|
-
import { getScrollContainer } from "./scroll_container.js";
|
|
9
|
-
|
|
10
|
-
export const allowWheelThrough = (element, connectedElement) => {
|
|
11
|
-
const isElementOrDescendant = (possibleDescendant) => {
|
|
12
|
-
return (
|
|
13
|
-
possibleDescendant === element || element.contains(possibleDescendant)
|
|
14
|
-
);
|
|
15
|
-
};
|
|
16
|
-
const tryToScrollOne = (element, wheelEvent) => {
|
|
17
|
-
if (element === document.documentElement) {
|
|
18
|
-
// let browser handle document scrolling
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const { deltaX, deltaY } = wheelEvent;
|
|
23
|
-
// we found what we want: a scrollable container behind the element
|
|
24
|
-
// we try to scroll it.
|
|
25
|
-
const elementCanApplyScrollDeltaX =
|
|
26
|
-
deltaX && canApplyScrollDelta(element, deltaX, "x");
|
|
27
|
-
const elementCanApplyScrollDeltaY =
|
|
28
|
-
deltaY && canApplyScrollDelta(element, deltaY, "y");
|
|
29
|
-
if (!elementCanApplyScrollDeltaX && !elementCanApplyScrollDeltaY) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
if (!isScrollable(element)) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
const belongsToElement = isElementOrDescendant(element);
|
|
36
|
-
if (belongsToElement) {
|
|
37
|
-
// let browser handle the scroll on the element itself
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
wheelEvent.preventDefault();
|
|
41
|
-
applyWheelScrollThrough(element, wheelEvent);
|
|
42
|
-
return true;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
if (connectedElement) {
|
|
46
|
-
const onWheel = (wheelEvent) => {
|
|
47
|
-
const connectedScrollContainer = getScrollContainer(connectedElement);
|
|
48
|
-
if (connectedScrollContainer === document.documentElement) {
|
|
49
|
-
// the connected scrollable parent is the document
|
|
50
|
-
// there is nothing to do, browser native scroll will work as we want
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const elementsBehindMouse = document.elementsFromPoint(
|
|
55
|
-
wheelEvent.clientX,
|
|
56
|
-
wheelEvent.clientY,
|
|
57
|
-
);
|
|
58
|
-
for (const elementBehindMouse of elementsBehindMouse) {
|
|
59
|
-
// try to scroll element itself
|
|
60
|
-
if (tryToScrollOne(elementBehindMouse, wheelEvent)) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const belongsToElement = isElementOrDescendant(elementBehindMouse);
|
|
64
|
-
// try to scroll what is behind
|
|
65
|
-
if (!belongsToElement) {
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// At this stage the element has no scrollable parts
|
|
70
|
-
// we can try to scroll the connected scrollable parent
|
|
71
|
-
tryToScrollOne(connectedScrollContainer, wheelEvent);
|
|
72
|
-
};
|
|
73
|
-
element.addEventListener("wheel", onWheel);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const onWheel = (wheelEvent) => {
|
|
78
|
-
const elementsBehindMouse = document.elementsFromPoint(
|
|
79
|
-
wheelEvent.clientX,
|
|
80
|
-
wheelEvent.clientY,
|
|
81
|
-
);
|
|
82
|
-
for (const elementBehindMouse of elementsBehindMouse) {
|
|
83
|
-
// try to scroll element itself
|
|
84
|
-
if (tryToScrollOne(elementBehindMouse, wheelEvent)) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const belongsToElement = isElementOrDescendant(elementBehindMouse);
|
|
88
|
-
if (belongsToElement) {
|
|
89
|
-
// keep searching if something in our element is scrollable
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
// our element is not scrollable, try to scroll the container behind the mouse
|
|
93
|
-
const scrollContainer = getScrollContainer(elementBehindMouse);
|
|
94
|
-
if (tryToScrollOne(scrollContainer, wheelEvent)) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
element.addEventListener("wheel", onWheel);
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const canApplyScrollDelta = (element, delta, axis) => {
|
|
103
|
-
const {
|
|
104
|
-
clientWidth,
|
|
105
|
-
clientHeight,
|
|
106
|
-
scrollWidth,
|
|
107
|
-
scrollHeight,
|
|
108
|
-
scrollLeft,
|
|
109
|
-
scrollTop,
|
|
110
|
-
} = element;
|
|
111
|
-
|
|
112
|
-
let size = axis === "x" ? clientWidth : clientHeight;
|
|
113
|
-
let currentScroll = axis === "x" ? scrollLeft : scrollTop;
|
|
114
|
-
let scrollEnd = axis === "x" ? scrollWidth : scrollHeight;
|
|
115
|
-
|
|
116
|
-
if (size === scrollEnd) {
|
|
117
|
-
// when scrollWidth === clientWidth, there is no scroll to apply
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
if (delta < 0 && currentScroll <= 0) {
|
|
121
|
-
// when scrollLeft is 0, we can't scroll to the left
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
if (delta > 0 && currentScroll + size >= scrollEnd) {
|
|
125
|
-
// when scrollLeft + size >= scrollWidth, we can't scroll to the right
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
return true;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const applyWheelScrollThrough = (element, wheelEvent) => {
|
|
132
|
-
wheelEvent.preventDefault();
|
|
133
|
-
element.scrollBy({
|
|
134
|
-
top: wheelEvent.deltaY,
|
|
135
|
-
left: wheelEvent.deltaX,
|
|
136
|
-
behavior: wheelEvent.deltaMode === 0 ? "auto" : "smooth", // optional tweak
|
|
137
|
-
});
|
|
138
|
-
};
|
package/src/iterable_weak_set.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
export const createIterableWeakSet = () => {
|
|
2
|
-
const objectWeakRefSet = new Set();
|
|
3
|
-
|
|
4
|
-
return {
|
|
5
|
-
add: (object) => {
|
|
6
|
-
const objectWeakRef = new WeakRef(object);
|
|
7
|
-
objectWeakRefSet.add(objectWeakRef);
|
|
8
|
-
},
|
|
9
|
-
|
|
10
|
-
delete: (object) => {
|
|
11
|
-
for (const weakRef of objectWeakRefSet) {
|
|
12
|
-
if (weakRef.deref() === object) {
|
|
13
|
-
objectWeakRefSet.delete(weakRef);
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return false;
|
|
18
|
-
},
|
|
19
|
-
|
|
20
|
-
*[Symbol.iterator]() {
|
|
21
|
-
for (const objectWeakRef of objectWeakRefSet) {
|
|
22
|
-
const object = objectWeakRef.deref();
|
|
23
|
-
if (object === undefined) {
|
|
24
|
-
objectWeakRefSet.delete(objectWeakRef);
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
yield object;
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
has: (object) => {
|
|
32
|
-
for (const weakRef of objectWeakRefSet) {
|
|
33
|
-
const objectCandidate = weakRef.deref();
|
|
34
|
-
if (objectCandidate === undefined) {
|
|
35
|
-
objectWeakRefSet.delete(weakRef);
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (objectCandidate === object) {
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return false;
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
clear: () => {
|
|
46
|
-
objectWeakRefSet.clear();
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
get size() {
|
|
50
|
-
return objectWeakRefSet.size;
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
getStats: () => {
|
|
54
|
-
let alive = 0;
|
|
55
|
-
let dead = 0;
|
|
56
|
-
for (const weakRef of objectWeakRefSet) {
|
|
57
|
-
if (weakRef.deref() !== undefined) {
|
|
58
|
-
alive++;
|
|
59
|
-
} else {
|
|
60
|
-
dead++;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return { total: objectWeakRefSet.size, alive, dead };
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
};
|
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DOM Coordinate Systems: The Missing APIs Problem
|
|
3
|
-
*
|
|
4
|
-
* When positioning and moving DOM elements, we commonly need coordinate information.
|
|
5
|
-
* The web platform provides getBoundingClientRect() which gives viewport-relative coordinates,
|
|
6
|
-
* but this creates several challenges when working with scrollable containers:
|
|
7
|
-
*
|
|
8
|
-
* ## The Problem
|
|
9
|
-
*
|
|
10
|
-
* 1. **Basic positioning**: getBoundingClientRect() works great for viewport-relative positioning
|
|
11
|
-
* 2. **Document scrolling**: When document has scroll, we add document.scrollLeft/scrollTop
|
|
12
|
-
* 3. **Scroll containers**: When elements are inside scrollable containers, we need coordinates
|
|
13
|
-
* relative to that container, not the document
|
|
14
|
-
*
|
|
15
|
-
* ## Missing Browser APIs
|
|
16
|
-
*
|
|
17
|
-
* The web platform lacks essential APIs for scroll container workflows:
|
|
18
|
-
* - No equivalent of getBoundingClientRect() relative to scroll container
|
|
19
|
-
* - No built-in way to get element coordinates in scroll container space
|
|
20
|
-
* - Manual coordinate conversion is error-prone and inconsistent
|
|
21
|
-
*
|
|
22
|
-
* ## This Module's Solution
|
|
23
|
-
*
|
|
24
|
-
* This module provides the missing coordinate APIs that work seamlessly with scroll containers:
|
|
25
|
-
* - **getScrollRelativeRect()**: element rect relative to scroll container (PRIMARY API)
|
|
26
|
-
* - **getMouseEventScrollRelativeRect()**: Mouse coordinates in scroll container space
|
|
27
|
-
* - **convertScrollRelativeRectInto()**: Convert scroll-relative rect to element positioning coordinates
|
|
28
|
-
*
|
|
29
|
-
* These APIs abstract away the complexity of coordinate system conversion and provide
|
|
30
|
-
* a consistent interface for element positioning regardless of scroll container depth.
|
|
31
|
-
*
|
|
32
|
-
* ## Primary API: getScrollRelativeRect()
|
|
33
|
-
*
|
|
34
|
-
* This is the main API you want - element rectangle relative to scroll container:
|
|
35
|
-
*
|
|
36
|
-
* ```js
|
|
37
|
-
* const rect = element.getBoundingClientRect(); // viewport-relative
|
|
38
|
-
* const scrollRect = getScrollRelativeRect(element, scrollContainer); // scroll-relative
|
|
39
|
-
* ```
|
|
40
|
-
*
|
|
41
|
-
* Returns: { left, top, right, bottom, width, height, scrollLeft, scrollTop, scrollContainer, ...metadata }
|
|
42
|
-
*
|
|
43
|
-
* The scroll values are included so you can calculate scroll-absolute coordinates yourself:
|
|
44
|
-
* ```js
|
|
45
|
-
* const { left, top, scrollLeft, scrollTop } = getScrollRelativeRect(element);
|
|
46
|
-
* const scrollAbsoluteLeft = left + scrollLeft;
|
|
47
|
-
* const scrollAbsoluteTop = top + scrollTop;
|
|
48
|
-
* ```
|
|
49
|
-
*
|
|
50
|
-
* ## Secondary APIs:
|
|
51
|
-
*
|
|
52
|
-
* - **getMouseEventScrollRelativeRect()**: Get mouse coordinates as a rect in scroll container space
|
|
53
|
-
* - **convertScrollRelativeRectInto()**: Convert from scroll-relative coordinates to element positioning coordinates (for setting element.style.left/top)
|
|
54
|
-
*
|
|
55
|
-
* ## Coordinate System Terminology:
|
|
56
|
-
*
|
|
57
|
-
* - **Viewport-relative**: getBoundingClientRect() coordinates - relative to browser viewport
|
|
58
|
-
* - **Scroll-relative**: Coordinates relative to scroll container (ignoring current scroll position)
|
|
59
|
-
* - **Scroll-absolute**: Scroll-relative + scroll position (element's position in full scrollable content)
|
|
60
|
-
* - **Element coordinates**: Coordinates for positioning elements (via element.style.left/top)
|
|
61
|
-
*
|
|
62
|
-
* ## Legacy Coordinate System Diagrams
|
|
63
|
-
*
|
|
64
|
-
* X-Axis Coordinate Systems in Web Development
|
|
65
|
-
*
|
|
66
|
-
* Diagram showing horizontal positioning and scrollbars:
|
|
67
|
-
*
|
|
68
|
-
* VIEWPORT (visible part of the document)
|
|
69
|
-
* ┌───────────────────────────────────────────────┐
|
|
70
|
-
* │ │
|
|
71
|
-
* │ │
|
|
72
|
-
* │ container.offsetLeft: 20px │
|
|
73
|
-
* │ ┼─────────────────────────────┐ │
|
|
74
|
-
* │ │ │ │
|
|
75
|
-
* │ │ │ │
|
|
76
|
-
* │ │ el.offsetLeft: 100px │ │
|
|
77
|
-
* │ │ ┼─────┐ │ │
|
|
78
|
-
* │ │ │ │ │ │
|
|
79
|
-
* │ │ └─────┘ │ │
|
|
80
|
-
* │ │ │ │
|
|
81
|
-
* │ │ ░░░███░░░░░░░░░░░░░░░░░░░░░ │ │
|
|
82
|
-
* │ └─────│───────────────────────┘ │
|
|
83
|
-
* │ container.scrollLeft: 50px │
|
|
84
|
-
* │ │
|
|
85
|
-
* │ │
|
|
86
|
-
* │ ░░░░░░░███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
|
|
87
|
-
* └─────────│─────────────────────────────────────┘
|
|
88
|
-
* document.scrollLeft: 200px
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* Left coordinate for the element:
|
|
92
|
-
*
|
|
93
|
-
* Document coordinates (absolute position in full document)
|
|
94
|
-
* • Result: 320px
|
|
95
|
-
* • Detail: container.offsetLeft + element.offsetLeft + document.scrollLeft
|
|
96
|
-
* 20 + 100 + 200 = 320px
|
|
97
|
-
*
|
|
98
|
-
* Viewport coordinates (getBoundingClientRect().left):
|
|
99
|
-
* • Result: 120px
|
|
100
|
-
* • Detail: container.offsetLeft + element.offsetLeft
|
|
101
|
-
* 20 + 100 = 120px
|
|
102
|
-
*
|
|
103
|
-
* Scroll coordinates (position within scroll container):
|
|
104
|
-
* • Result: 50px
|
|
105
|
-
* • Detail: element.offsetLeft - container.scrollLeft
|
|
106
|
-
* 100 - 50 = 50px
|
|
107
|
-
*
|
|
108
|
-
* Scroll behavior examples:
|
|
109
|
-
*
|
|
110
|
-
* When document scrolls (scrollLeft: 200px → 300px):
|
|
111
|
-
* • Document coordinates: 320px → 420px
|
|
112
|
-
* • Viewport coordinates: 120px → 120px (unchanged)
|
|
113
|
-
* • Scroll coordinates: 50px → 50px (unchanged)
|
|
114
|
-
*
|
|
115
|
-
* When container scrolls (scrollLeft: 50px → 100px):
|
|
116
|
-
* • Document coordinates: 320px → 270px
|
|
117
|
-
* • Viewport coordinates: 120px → 70px
|
|
118
|
-
* • Scroll coordinates: 50px → 0px
|
|
119
|
-
*/
|
|
120
|
-
|
|
121
|
-
import { getScrollContainer } from "../interaction/scroll/scroll_container.js";
|
|
122
|
-
import { getBorderSizes } from "../size/get_border_sizes.js";
|
|
123
|
-
|
|
124
|
-
const { documentElement } = document;
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Get element rectangle relative to its scroll container
|
|
128
|
-
*
|
|
129
|
-
* @param {Element} element - The element to get coordinates for
|
|
130
|
-
* @param {Element} [scrollContainer] - Optional scroll container (auto-detected if not provided)
|
|
131
|
-
* @param {object} [options] - Configuration options
|
|
132
|
-
* @returns {object} { left, top, right, bottom, width, height, scrollLeft, scrollTop, scrollContainer, ...metadata }
|
|
133
|
-
*/
|
|
134
|
-
export const getScrollRelativeRect = (
|
|
135
|
-
element,
|
|
136
|
-
scrollContainer = getScrollContainer(element),
|
|
137
|
-
{ useOriginalPositionEvenIfSticky = false } = {},
|
|
138
|
-
) => {
|
|
139
|
-
const {
|
|
140
|
-
left: leftViewport,
|
|
141
|
-
top: topViewport,
|
|
142
|
-
width,
|
|
143
|
-
height,
|
|
144
|
-
} = element.getBoundingClientRect();
|
|
145
|
-
|
|
146
|
-
let fromFixed = false;
|
|
147
|
-
let fromStickyLeft;
|
|
148
|
-
let fromStickyTop;
|
|
149
|
-
let fromStickyLeftAttr;
|
|
150
|
-
let fromStickyTopAttr;
|
|
151
|
-
const scrollLeft = scrollContainer.scrollLeft;
|
|
152
|
-
const scrollTop = scrollContainer.scrollTop;
|
|
153
|
-
const scrollContainerIsDocument = scrollContainer === documentElement;
|
|
154
|
-
const createScrollRelativeRect = (leftScrollRelative, topScrollRelative) => {
|
|
155
|
-
const isStickyLeftOrHasStickyLeftAttr = Boolean(
|
|
156
|
-
fromStickyLeft || fromStickyLeftAttr,
|
|
157
|
-
);
|
|
158
|
-
const isStickyTopOrHasStickyTopAttr = Boolean(
|
|
159
|
-
fromStickyTop || fromStickyTopAttr,
|
|
160
|
-
);
|
|
161
|
-
return {
|
|
162
|
-
left: leftScrollRelative,
|
|
163
|
-
top: topScrollRelative,
|
|
164
|
-
right: leftScrollRelative + width,
|
|
165
|
-
bottom: topScrollRelative + height,
|
|
166
|
-
|
|
167
|
-
// metadata
|
|
168
|
-
width,
|
|
169
|
-
height,
|
|
170
|
-
scrollContainer,
|
|
171
|
-
scrollContainerIsDocument,
|
|
172
|
-
scrollLeft,
|
|
173
|
-
scrollTop,
|
|
174
|
-
fromFixed,
|
|
175
|
-
fromStickyLeft,
|
|
176
|
-
fromStickyTop,
|
|
177
|
-
fromStickyLeftAttr,
|
|
178
|
-
fromStickyTopAttr,
|
|
179
|
-
isStickyLeftOrHasStickyLeftAttr,
|
|
180
|
-
isStickyTopOrHasStickyTopAttr,
|
|
181
|
-
isSticky:
|
|
182
|
-
isStickyLeftOrHasStickyLeftAttr || isStickyTopOrHasStickyTopAttr,
|
|
183
|
-
};
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
sticky: {
|
|
187
|
-
const computedStyle = getComputedStyle(element);
|
|
188
|
-
sticky_position: {
|
|
189
|
-
const usePositionSticky = computedStyle.position === "sticky";
|
|
190
|
-
if (usePositionSticky) {
|
|
191
|
-
// For CSS position:sticky elements, use scrollable-relative coordinates
|
|
192
|
-
const [leftScrollRelative, topScrollRelative] =
|
|
193
|
-
viewportPosToScrollRelativePos(
|
|
194
|
-
leftViewport,
|
|
195
|
-
topViewport,
|
|
196
|
-
scrollContainer,
|
|
197
|
-
);
|
|
198
|
-
const isStickyLeft = computedStyle.left !== "auto";
|
|
199
|
-
const isStickyTop = computedStyle.top !== "auto";
|
|
200
|
-
fromStickyLeft = isStickyLeft
|
|
201
|
-
? { value: parseFloat(computedStyle.left) || 0 }
|
|
202
|
-
: undefined;
|
|
203
|
-
fromStickyTop = isStickyTop
|
|
204
|
-
? { value: parseFloat(computedStyle.top) || 0 }
|
|
205
|
-
: undefined;
|
|
206
|
-
return createScrollRelativeRect(leftScrollRelative, topScrollRelative);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
sticky_attribute: {
|
|
210
|
-
const hasStickyLeftAttribute = element.hasAttribute("data-sticky-left");
|
|
211
|
-
const hasStickyTopAttribute = element.hasAttribute("data-sticky-top");
|
|
212
|
-
const useStickyAttribute =
|
|
213
|
-
hasStickyLeftAttribute || hasStickyTopAttribute;
|
|
214
|
-
if (useStickyAttribute) {
|
|
215
|
-
// Handle virtually sticky obstacles (<col> or <tr>) - elements with data-sticky attributes
|
|
216
|
-
// but not CSS position:sticky. Calculate their position based on scroll and sticky behavior
|
|
217
|
-
let [leftScrollRelative, topScrollRelative] =
|
|
218
|
-
viewportPosToScrollRelativePos(
|
|
219
|
-
leftViewport,
|
|
220
|
-
topViewport,
|
|
221
|
-
scrollContainer,
|
|
222
|
-
);
|
|
223
|
-
if (hasStickyLeftAttribute) {
|
|
224
|
-
const leftCssValue = parseFloat(computedStyle.left) || 0;
|
|
225
|
-
fromStickyLeftAttr = { value: leftCssValue };
|
|
226
|
-
if (useOriginalPositionEvenIfSticky) {
|
|
227
|
-
// For obstacles: use original position only (ignore sticky behavior)
|
|
228
|
-
} else {
|
|
229
|
-
const scrollLeft = scrollContainer.scrollLeft;
|
|
230
|
-
const stickyPosition = scrollLeft + leftCssValue;
|
|
231
|
-
const leftWithScroll = leftScrollRelative + scrollLeft;
|
|
232
|
-
if (stickyPosition > leftWithScroll) {
|
|
233
|
-
leftScrollRelative = leftCssValue; // Element is stuck
|
|
234
|
-
} else {
|
|
235
|
-
// Element in natural position
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (hasStickyTopAttribute) {
|
|
240
|
-
const topCssValue = parseFloat(computedStyle.top) || 0;
|
|
241
|
-
fromStickyTopAttr = { value: topCssValue };
|
|
242
|
-
if (useOriginalPositionEvenIfSticky) {
|
|
243
|
-
// For obstacles: use original position only (ignore sticky behavior)
|
|
244
|
-
} else {
|
|
245
|
-
const scrollTop = scrollContainer.scrollTop;
|
|
246
|
-
const stickyPosition = scrollTop + topCssValue;
|
|
247
|
-
const topWithScroll = topScrollRelative + scrollTop;
|
|
248
|
-
if (stickyPosition > topWithScroll) {
|
|
249
|
-
topScrollRelative = topCssValue; // Element is stuck
|
|
250
|
-
} else {
|
|
251
|
-
// Element in natural position
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return createScrollRelativeRect(leftScrollRelative, topScrollRelative);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// For normal elements, use scrollable-relative coordinates
|
|
261
|
-
const [leftScrollRelative, topScrollRelative] =
|
|
262
|
-
viewportPosToScrollRelativePos(leftViewport, topViewport, scrollContainer);
|
|
263
|
-
return createScrollRelativeRect(leftScrollRelative, topScrollRelative);
|
|
264
|
-
};
|
|
265
|
-
const viewportPosToScrollRelativePos = (
|
|
266
|
-
leftViewport,
|
|
267
|
-
topViewport,
|
|
268
|
-
scrollContainer,
|
|
269
|
-
) => {
|
|
270
|
-
const scrollContainerIsDocument = scrollContainer === documentElement;
|
|
271
|
-
if (scrollContainerIsDocument) {
|
|
272
|
-
return [leftViewport, topViewport];
|
|
273
|
-
}
|
|
274
|
-
const { left: scrollContainerLeftViewport, top: scrollContainerTopViewport } =
|
|
275
|
-
scrollContainer.getBoundingClientRect();
|
|
276
|
-
return [
|
|
277
|
-
leftViewport - scrollContainerLeftViewport,
|
|
278
|
-
topViewport - scrollContainerTopViewport,
|
|
279
|
-
];
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
export const addScrollToRect = (scrollRelativeRect) => {
|
|
283
|
-
const { left, top, width, height, scrollLeft, scrollTop } =
|
|
284
|
-
scrollRelativeRect;
|
|
285
|
-
const leftWithScroll = left + scrollLeft;
|
|
286
|
-
const topWithScroll = top + scrollTop;
|
|
287
|
-
return {
|
|
288
|
-
...scrollRelativeRect,
|
|
289
|
-
left: leftWithScroll,
|
|
290
|
-
top: topWithScroll,
|
|
291
|
-
right: leftWithScroll + width,
|
|
292
|
-
bottom: topWithScroll + height,
|
|
293
|
-
};
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
// https://github.com/w3c/csswg-drafts/issues/3329
|
|
297
|
-
// Return the portion of the element that is visible for this scoll container
|
|
298
|
-
export const getScrollBox = (scrollContainer) => {
|
|
299
|
-
if (scrollContainer === documentElement) {
|
|
300
|
-
const { clientWidth, clientHeight } = documentElement;
|
|
301
|
-
|
|
302
|
-
return {
|
|
303
|
-
left: 0,
|
|
304
|
-
top: 0,
|
|
305
|
-
right: clientWidth,
|
|
306
|
-
bottom: clientHeight,
|
|
307
|
-
width: clientWidth,
|
|
308
|
-
height: clientHeight,
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const { clientWidth, clientHeight } = scrollContainer;
|
|
313
|
-
const scrollContainerBorderSizes = getBorderSizes(scrollContainer);
|
|
314
|
-
const left = scrollContainerBorderSizes.left;
|
|
315
|
-
const top = scrollContainerBorderSizes.top;
|
|
316
|
-
const right = left + clientWidth;
|
|
317
|
-
const bottom = top + clientHeight;
|
|
318
|
-
return {
|
|
319
|
-
left,
|
|
320
|
-
top,
|
|
321
|
-
right,
|
|
322
|
-
bottom,
|
|
323
|
-
width: clientWidth,
|
|
324
|
-
height: clientHeight,
|
|
325
|
-
};
|
|
326
|
-
};
|
|
327
|
-
// https://developer.mozilla.org/en-US/docs/Glossary/Scroll_container#scrollport
|
|
328
|
-
export const getScrollport = (scrollBox, scrollContainer) => {
|
|
329
|
-
const { left, top, width, height } = scrollBox;
|
|
330
|
-
const leftWithScroll = left + scrollContainer.scrollLeft;
|
|
331
|
-
const topWithScroll = top + scrollContainer.scrollTop;
|
|
332
|
-
const rightWithScroll = leftWithScroll + width;
|
|
333
|
-
const bottomWithScroll = topWithScroll + height;
|
|
334
|
-
return {
|
|
335
|
-
left: leftWithScroll,
|
|
336
|
-
top: topWithScroll,
|
|
337
|
-
right: rightWithScroll,
|
|
338
|
-
bottom: bottomWithScroll,
|
|
339
|
-
};
|
|
340
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export const getPositionedParent = (element) => {
|
|
2
|
-
let parent = element.parentElement;
|
|
3
|
-
while (parent && parent !== document.body) {
|
|
4
|
-
const position = window.getComputedStyle(parent).position;
|
|
5
|
-
if (
|
|
6
|
-
position === "relative" ||
|
|
7
|
-
position === "absolute" ||
|
|
8
|
-
position === "fixed"
|
|
9
|
-
) {
|
|
10
|
-
return parent;
|
|
11
|
-
}
|
|
12
|
-
parent = parent.parentElement;
|
|
13
|
-
}
|
|
14
|
-
return document.body;
|
|
15
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export const findSelfOrAncestorFixedPosition = (element) => {
|
|
2
|
-
let current = element;
|
|
3
|
-
while (true) {
|
|
4
|
-
const computedStyle = window.getComputedStyle(current);
|
|
5
|
-
if (computedStyle.position === "fixed") {
|
|
6
|
-
const { left, top } = current.getBoundingClientRect();
|
|
7
|
-
return [left, top];
|
|
8
|
-
}
|
|
9
|
-
current = current.parentElement;
|
|
10
|
-
if (!current || current === document.documentElement) {
|
|
11
|
-
break;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
return null;
|
|
15
|
-
};
|