@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,486 +0,0 @@
|
|
|
1
|
-
import { getScrollContainer } from "../interaction/scroll/scroll_container.js";
|
|
2
|
-
import { createPubSub } from "../pub_sub.js";
|
|
3
|
-
|
|
4
|
-
const DEBUG = false;
|
|
5
|
-
|
|
6
|
-
// Creates a visible rect effect that tracks how much of an element is visible within its scrollable parent
|
|
7
|
-
// and within the document viewport. This is useful for implementing overlays, lazy loading, or any UI
|
|
8
|
-
// that needs to react to element visibility changes.
|
|
9
|
-
//
|
|
10
|
-
// The function returns two visibility ratios:
|
|
11
|
-
// - scrollVisibilityRatio: Visibility ratio relative to the scrollable parent (0-1)
|
|
12
|
-
// - visibilityRatio: Visibility ratio relative to the document viewport (0-1)
|
|
13
|
-
//
|
|
14
|
-
// When scrollable parent is the document, both ratios will be the same.
|
|
15
|
-
// When scrollable parent is a custom container, scrollVisibilityRatio might be 1.0 (fully visible
|
|
16
|
-
// within the container) while visibilityRatio could be 0.0 (container is scrolled out of viewport).
|
|
17
|
-
// A bit like https://tetherjs.dev/ but different
|
|
18
|
-
export const visibleRectEffect = (element, update) => {
|
|
19
|
-
const [teardown, addTeardown] = createPubSub();
|
|
20
|
-
const scrollContainer = getScrollContainer(element);
|
|
21
|
-
const scrollContainerIsDocument =
|
|
22
|
-
scrollContainer === document.documentElement;
|
|
23
|
-
let lastMeasuredWidth;
|
|
24
|
-
let lastMeasuredHeight;
|
|
25
|
-
const check = (reason) => {
|
|
26
|
-
if (DEBUG) {
|
|
27
|
-
console.group(`visibleRect.check("${reason}")`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// 1. Calculate element position relative to scrollable parent
|
|
31
|
-
const { scrollLeft, scrollTop } = scrollContainer;
|
|
32
|
-
const visibleAreaLeft = scrollLeft;
|
|
33
|
-
const visibleAreaTop = scrollTop;
|
|
34
|
-
|
|
35
|
-
// Get element position relative to its scrollable parent
|
|
36
|
-
let elementAbsoluteLeft;
|
|
37
|
-
let elementAbsoluteTop;
|
|
38
|
-
if (scrollContainerIsDocument) {
|
|
39
|
-
// For document scrolling, use offsetLeft/offsetTop relative to document
|
|
40
|
-
const rect = element.getBoundingClientRect();
|
|
41
|
-
elementAbsoluteLeft = rect.left + scrollLeft;
|
|
42
|
-
elementAbsoluteTop = rect.top + scrollTop;
|
|
43
|
-
} else {
|
|
44
|
-
// For custom container, get position relative to the container
|
|
45
|
-
const elementRect = element.getBoundingClientRect();
|
|
46
|
-
const scrollContainerRect = scrollContainer.getBoundingClientRect();
|
|
47
|
-
elementAbsoluteLeft =
|
|
48
|
-
elementRect.left - scrollContainerRect.left + scrollLeft;
|
|
49
|
-
elementAbsoluteTop =
|
|
50
|
-
elementRect.top - scrollContainerRect.top + scrollTop;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const leftVisible =
|
|
54
|
-
visibleAreaLeft < elementAbsoluteLeft
|
|
55
|
-
? elementAbsoluteLeft - visibleAreaLeft
|
|
56
|
-
: 0;
|
|
57
|
-
const topVisible =
|
|
58
|
-
visibleAreaTop < elementAbsoluteTop
|
|
59
|
-
? elementAbsoluteTop - visibleAreaTop
|
|
60
|
-
: 0;
|
|
61
|
-
// Convert to overlay coordinates (adjust for custom scrollable container)
|
|
62
|
-
let overlayLeft = leftVisible;
|
|
63
|
-
let overlayTop = topVisible;
|
|
64
|
-
if (!scrollContainerIsDocument) {
|
|
65
|
-
const { left: scrollableLeft, top: scrollableTop } =
|
|
66
|
-
scrollContainer.getBoundingClientRect();
|
|
67
|
-
overlayLeft += scrollableLeft;
|
|
68
|
-
overlayTop += scrollableTop;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// 2. Calculate element visible width/height
|
|
72
|
-
const { width, height } = element.getBoundingClientRect();
|
|
73
|
-
lastMeasuredWidth = width;
|
|
74
|
-
lastMeasuredHeight = height;
|
|
75
|
-
const visibleAreaWidth = scrollContainer.clientWidth;
|
|
76
|
-
const visibleAreaHeight = scrollContainer.clientHeight;
|
|
77
|
-
const visibleAreaRight = visibleAreaLeft + visibleAreaWidth;
|
|
78
|
-
const visibleAreaBottom = visibleAreaTop + visibleAreaHeight;
|
|
79
|
-
// 2.1 Calculate visible width
|
|
80
|
-
let widthVisible;
|
|
81
|
-
{
|
|
82
|
-
const maxVisibleWidth = visibleAreaWidth - leftVisible;
|
|
83
|
-
const elementAbsoluteRight = elementAbsoluteLeft + width;
|
|
84
|
-
const elementLeftIsVisible = elementAbsoluteLeft >= visibleAreaLeft;
|
|
85
|
-
const elementRightIsVisible = elementAbsoluteRight <= visibleAreaRight;
|
|
86
|
-
if (elementLeftIsVisible && elementRightIsVisible) {
|
|
87
|
-
// Element fully visible horizontally
|
|
88
|
-
widthVisible = width;
|
|
89
|
-
} else if (elementLeftIsVisible && !elementRightIsVisible) {
|
|
90
|
-
// Element left is visible, right is cut off
|
|
91
|
-
widthVisible = visibleAreaRight - elementAbsoluteLeft;
|
|
92
|
-
} else if (!elementLeftIsVisible && elementRightIsVisible) {
|
|
93
|
-
// Element left is cut off, right is visible
|
|
94
|
-
widthVisible = elementAbsoluteRight - visibleAreaLeft;
|
|
95
|
-
} else {
|
|
96
|
-
// Element spans beyond both sides, show only visible area portion
|
|
97
|
-
widthVisible = maxVisibleWidth;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// 2.2 Calculate visible height
|
|
101
|
-
let heightVisible;
|
|
102
|
-
{
|
|
103
|
-
const maxVisibleHeight = visibleAreaHeight - topVisible;
|
|
104
|
-
const elementAbsoluteBottom = elementAbsoluteTop + height;
|
|
105
|
-
const elementTopIsVisible = elementAbsoluteTop >= visibleAreaTop;
|
|
106
|
-
const elementBottomIsVisible = elementAbsoluteBottom <= visibleAreaBottom;
|
|
107
|
-
if (elementTopIsVisible && elementBottomIsVisible) {
|
|
108
|
-
// Element fully visible vertically
|
|
109
|
-
heightVisible = height;
|
|
110
|
-
} else if (elementTopIsVisible && !elementBottomIsVisible) {
|
|
111
|
-
// Element top is visible, bottom is cut off
|
|
112
|
-
heightVisible = visibleAreaBottom - elementAbsoluteTop;
|
|
113
|
-
} else if (!elementTopIsVisible && elementBottomIsVisible) {
|
|
114
|
-
// Element top is cut off, bottom is visible
|
|
115
|
-
heightVisible = elementAbsoluteBottom - visibleAreaTop;
|
|
116
|
-
} else {
|
|
117
|
-
// Element spans beyond both sides, show only visible area portion
|
|
118
|
-
heightVisible = maxVisibleHeight;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Calculate visibility ratios
|
|
123
|
-
const scrollVisibilityRatio =
|
|
124
|
-
(widthVisible * heightVisible) / (width * height);
|
|
125
|
-
// Calculate visibility ratio relative to document viewport
|
|
126
|
-
let documentVisibilityRatio;
|
|
127
|
-
if (scrollContainerIsDocument) {
|
|
128
|
-
documentVisibilityRatio = scrollVisibilityRatio;
|
|
129
|
-
} else {
|
|
130
|
-
// For custom containers, calculate visibility relative to document viewport
|
|
131
|
-
const elementRect = element.getBoundingClientRect();
|
|
132
|
-
const viewportWidth = window.innerWidth;
|
|
133
|
-
const viewportHeight = window.innerHeight;
|
|
134
|
-
// Calculate how much of the element is visible in the document viewport
|
|
135
|
-
const elementLeft = Math.max(0, elementRect.left);
|
|
136
|
-
const elementTop = Math.max(0, elementRect.top);
|
|
137
|
-
const elementRight = Math.min(viewportWidth, elementRect.right);
|
|
138
|
-
const elementBottom = Math.min(viewportHeight, elementRect.bottom);
|
|
139
|
-
const documentVisibleWidth = Math.max(0, elementRight - elementLeft);
|
|
140
|
-
const documentVisibleHeight = Math.max(0, elementBottom - elementTop);
|
|
141
|
-
documentVisibilityRatio =
|
|
142
|
-
(documentVisibleWidth * documentVisibleHeight) / (width * height);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const visibleRect = {
|
|
146
|
-
left: overlayLeft,
|
|
147
|
-
top: overlayTop,
|
|
148
|
-
right: overlayLeft + widthVisible,
|
|
149
|
-
bottom: overlayTop + heightVisible,
|
|
150
|
-
width: widthVisible,
|
|
151
|
-
height: heightVisible,
|
|
152
|
-
visibilityRatio: documentVisibilityRatio,
|
|
153
|
-
scrollVisibilityRatio,
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
if (DEBUG) {
|
|
157
|
-
console.log(`update(${JSON.stringify(visibleRect, null, " ")})`);
|
|
158
|
-
console.groupEnd();
|
|
159
|
-
}
|
|
160
|
-
update(visibleRect, {
|
|
161
|
-
width,
|
|
162
|
-
height,
|
|
163
|
-
});
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
check("initialization");
|
|
167
|
-
|
|
168
|
-
const [publishBeforeAutoCheck, onBeforeAutoCheck] = createPubSub();
|
|
169
|
-
auto_check: {
|
|
170
|
-
const autoCheck = (reason) => {
|
|
171
|
-
const beforeCheckResults = publishBeforeAutoCheck(reason);
|
|
172
|
-
check(reason);
|
|
173
|
-
for (const beforeCheckResult of beforeCheckResults) {
|
|
174
|
-
if (typeof beforeCheckResult === "function") {
|
|
175
|
-
beforeCheckResult();
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
// let rafId = null;
|
|
180
|
-
// const scheduleCheck = (reason) => {
|
|
181
|
-
// cancelAnimationFrame(rafId);
|
|
182
|
-
// rafId = requestAnimationFrame(() => {
|
|
183
|
-
// autoCheck(reason);
|
|
184
|
-
// });
|
|
185
|
-
// };
|
|
186
|
-
// addTeardown(() => {
|
|
187
|
-
// cancelAnimationFrame(rafId);
|
|
188
|
-
// });
|
|
189
|
-
|
|
190
|
-
on_scroll: {
|
|
191
|
-
// If scrollable parent is not document, also listen to document scroll
|
|
192
|
-
// to update UI position when the scrollable parent moves in viewport
|
|
193
|
-
const onDocumentScroll = () => {
|
|
194
|
-
autoCheck("document_scroll");
|
|
195
|
-
};
|
|
196
|
-
document.addEventListener("scroll", onDocumentScroll, {
|
|
197
|
-
passive: true,
|
|
198
|
-
});
|
|
199
|
-
addTeardown(() => {
|
|
200
|
-
document.removeEventListener("scroll", onDocumentScroll, {
|
|
201
|
-
passive: true,
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
if (!scrollContainerIsDocument) {
|
|
205
|
-
const onScroll = () => {
|
|
206
|
-
autoCheck("scrollable_parent_scroll");
|
|
207
|
-
};
|
|
208
|
-
scrollContainer.addEventListener("scroll", onScroll, {
|
|
209
|
-
passive: true,
|
|
210
|
-
});
|
|
211
|
-
addTeardown(() => {
|
|
212
|
-
scrollContainer.removeEventListener("scroll", onScroll, {
|
|
213
|
-
passive: true,
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
on_window_resize: {
|
|
219
|
-
const onWindowResize = () => {
|
|
220
|
-
autoCheck("window_size_change");
|
|
221
|
-
};
|
|
222
|
-
window.addEventListener("resize", onWindowResize);
|
|
223
|
-
addTeardown(() => {
|
|
224
|
-
window.removeEventListener("resize", onWindowResize);
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
on_element_resize: {
|
|
228
|
-
let handlingResize = true;
|
|
229
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
230
|
-
if (handlingResize) {
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
// we use directly the result of getBoundingClientRect() instead of the resizeEntry.contentRect or resizeEntry.borderBoxSize
|
|
234
|
-
// so that:
|
|
235
|
-
// - We can compare the dimensions measure in the last check and the current one
|
|
236
|
-
// - We don't have to check element boz-sizing to know what to compare
|
|
237
|
-
// - resizeEntry.borderBoxSize browser support is not that great
|
|
238
|
-
const { width, height } = element.getBoundingClientRect();
|
|
239
|
-
const widthDiff = Math.abs(width - lastMeasuredWidth);
|
|
240
|
-
const heightDiff = Math.abs(height - lastMeasuredHeight);
|
|
241
|
-
if (widthDiff === 0 && heightDiff === 0) {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
handlingResize = true;
|
|
245
|
-
autoCheck(`element_size_change (${width}x${height})`);
|
|
246
|
-
handlingResize = false;
|
|
247
|
-
});
|
|
248
|
-
resizeObserver.observe(element);
|
|
249
|
-
// Temporarily disconnect ResizeObserver to prevent feedback loops eventually caused by update function
|
|
250
|
-
onBeforeAutoCheck(() => {
|
|
251
|
-
resizeObserver.unobserve(element);
|
|
252
|
-
return () => {
|
|
253
|
-
// This triggers a new call to the resive observer that will be ignored thanks to
|
|
254
|
-
// the widthDiff/heightDiff early return
|
|
255
|
-
resizeObserver.observe(element);
|
|
256
|
-
};
|
|
257
|
-
});
|
|
258
|
-
addTeardown(() => {
|
|
259
|
-
resizeObserver.disconnect();
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
on_intersection_change: {
|
|
263
|
-
const documentIntersectionObserver = new IntersectionObserver(
|
|
264
|
-
() => {
|
|
265
|
-
autoCheck("element_intersection_with_document_change");
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
root: null,
|
|
269
|
-
rootMargin: "0px",
|
|
270
|
-
threshold: [0, 0.1, 0.9, 1],
|
|
271
|
-
},
|
|
272
|
-
);
|
|
273
|
-
documentIntersectionObserver.observe(element);
|
|
274
|
-
addTeardown(() => {
|
|
275
|
-
documentIntersectionObserver.disconnect();
|
|
276
|
-
});
|
|
277
|
-
if (!scrollContainerIsDocument) {
|
|
278
|
-
const scrollIntersectionObserver = new IntersectionObserver(
|
|
279
|
-
() => {
|
|
280
|
-
autoCheck("element_intersection_with_scroll_change");
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
root: scrollContainer,
|
|
284
|
-
rootMargin: "0px",
|
|
285
|
-
threshold: [0, 0, 1, 0.9, 1],
|
|
286
|
-
},
|
|
287
|
-
);
|
|
288
|
-
scrollIntersectionObserver.observe(element);
|
|
289
|
-
addTeardown(() => {
|
|
290
|
-
scrollIntersectionObserver.disconnect();
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
on_window_touchmove: {
|
|
295
|
-
const onWindowTouchMove = () => {
|
|
296
|
-
autoCheck("window_touchmove");
|
|
297
|
-
};
|
|
298
|
-
window.addEventListener("touchmove", onWindowTouchMove, {
|
|
299
|
-
passive: true,
|
|
300
|
-
});
|
|
301
|
-
addTeardown(() => {
|
|
302
|
-
window.removeEventListener("touchmove", onWindowTouchMove, {
|
|
303
|
-
passive: true,
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return {
|
|
310
|
-
check,
|
|
311
|
-
onBeforeAutoCheck,
|
|
312
|
-
disconnect: () => {
|
|
313
|
-
teardown();
|
|
314
|
-
},
|
|
315
|
-
};
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
export const pickPositionRelativeTo = (
|
|
319
|
-
element,
|
|
320
|
-
target,
|
|
321
|
-
{
|
|
322
|
-
alignToViewportEdgeWhenTargetNearEdge = 0,
|
|
323
|
-
minLeft = 0,
|
|
324
|
-
forcePosition,
|
|
325
|
-
} = {},
|
|
326
|
-
) => {
|
|
327
|
-
if (
|
|
328
|
-
import.meta.dev &&
|
|
329
|
-
getScrollContainer(element) !== document.documentElement
|
|
330
|
-
) {
|
|
331
|
-
// The idea behind this warning is that pickPositionRelativeTo is meant to position a tooltip/dropdown etc
|
|
332
|
-
// And for this use case the element to position should be document-relative
|
|
333
|
-
// (position: absolute with first scrollable parent being the document)
|
|
334
|
-
// Because this is how you achieve the best results:
|
|
335
|
-
// 1. The element naturally follow document scroll
|
|
336
|
-
// Which gives the best experience when user scrolls the page or the container
|
|
337
|
-
// 2. The element can take more visible size in case target is within a scrollable container
|
|
338
|
-
// or uses overflow: hidden somewhere in its ancestor chain
|
|
339
|
-
console.warn(
|
|
340
|
-
"pickPositionRelativeTo should be used only for document-relative element",
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const viewportWidth = document.documentElement.clientWidth;
|
|
345
|
-
const viewportHeight = document.documentElement.clientHeight;
|
|
346
|
-
// Get viewport-relative positions
|
|
347
|
-
const elementRect = element.getBoundingClientRect();
|
|
348
|
-
const targetRect = target.getBoundingClientRect();
|
|
349
|
-
const {
|
|
350
|
-
left: elementLeft,
|
|
351
|
-
right: elementRight,
|
|
352
|
-
top: elementTop,
|
|
353
|
-
bottom: elementBottom,
|
|
354
|
-
} = elementRect;
|
|
355
|
-
const {
|
|
356
|
-
left: targetLeft,
|
|
357
|
-
right: targetRight,
|
|
358
|
-
top: targetTop,
|
|
359
|
-
bottom: targetBottom,
|
|
360
|
-
} = targetRect;
|
|
361
|
-
const elementWidth = elementRight - elementLeft;
|
|
362
|
-
const elementHeight = elementBottom - elementTop;
|
|
363
|
-
const targetWidth = targetRight - targetLeft;
|
|
364
|
-
|
|
365
|
-
// Calculate horizontal position (viewport-relative)
|
|
366
|
-
let elementPositionLeft;
|
|
367
|
-
{
|
|
368
|
-
// Check if target element is wider than viewport
|
|
369
|
-
const targetIsWiderThanViewport = targetWidth > viewportWidth;
|
|
370
|
-
if (targetIsWiderThanViewport) {
|
|
371
|
-
const targetLeftIsVisible = targetLeft >= 0;
|
|
372
|
-
const targetRightIsVisible = targetRight <= viewportWidth;
|
|
373
|
-
|
|
374
|
-
if (!targetLeftIsVisible && targetRightIsVisible) {
|
|
375
|
-
// Target extends beyond left edge but right side is visible
|
|
376
|
-
const viewportCenter = viewportWidth / 2;
|
|
377
|
-
const distanceFromRightEdge = viewportWidth - targetRight;
|
|
378
|
-
elementPositionLeft =
|
|
379
|
-
viewportCenter - distanceFromRightEdge / 2 - elementWidth / 2;
|
|
380
|
-
} else if (targetLeftIsVisible && !targetRightIsVisible) {
|
|
381
|
-
// Target extends beyond right edge but left side is visible
|
|
382
|
-
const viewportCenter = viewportWidth / 2;
|
|
383
|
-
const distanceFromLeftEdge = -targetLeft;
|
|
384
|
-
elementPositionLeft =
|
|
385
|
-
viewportCenter - distanceFromLeftEdge / 2 - elementWidth / 2;
|
|
386
|
-
} else {
|
|
387
|
-
// Target extends beyond both edges or is fully visible (center in viewport)
|
|
388
|
-
elementPositionLeft = viewportWidth / 2 - elementWidth / 2;
|
|
389
|
-
}
|
|
390
|
-
} else {
|
|
391
|
-
// Target fits within viewport width - center element relative to target
|
|
392
|
-
elementPositionLeft = targetLeft + targetWidth / 2 - elementWidth / 2;
|
|
393
|
-
// Special handling when element is wider than target
|
|
394
|
-
if (alignToViewportEdgeWhenTargetNearEdge) {
|
|
395
|
-
const elementIsWiderThanTarget = elementWidth > targetWidth;
|
|
396
|
-
const targetIsNearLeftEdge =
|
|
397
|
-
targetLeft < alignToViewportEdgeWhenTargetNearEdge;
|
|
398
|
-
if (elementIsWiderThanTarget && targetIsNearLeftEdge) {
|
|
399
|
-
elementPositionLeft = minLeft; // Left edge of viewport
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
// Constrain horizontal position to viewport boundaries
|
|
404
|
-
if (elementPositionLeft < 0) {
|
|
405
|
-
elementPositionLeft = 0;
|
|
406
|
-
} else if (elementPositionLeft + elementWidth > viewportWidth) {
|
|
407
|
-
elementPositionLeft = viewportWidth - elementWidth;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Calculate vertical position (viewport-relative)
|
|
412
|
-
let position;
|
|
413
|
-
const spaceAboveTarget = targetTop;
|
|
414
|
-
const spaceBelowTarget = viewportHeight - targetBottom;
|
|
415
|
-
determine_position: {
|
|
416
|
-
if (forcePosition) {
|
|
417
|
-
position = forcePosition;
|
|
418
|
-
break determine_position;
|
|
419
|
-
}
|
|
420
|
-
const preferredPosition = element.getAttribute("data-position");
|
|
421
|
-
const minContentVisibilityRatio = 0.6; // 60% minimum visibility to keep position
|
|
422
|
-
if (preferredPosition) {
|
|
423
|
-
// Element has a preferred position - try to keep it unless we really struggle
|
|
424
|
-
const visibleRatio =
|
|
425
|
-
preferredPosition === "above"
|
|
426
|
-
? spaceAboveTarget / elementHeight
|
|
427
|
-
: spaceBelowTarget / elementHeight;
|
|
428
|
-
const canShowMinimumContent = visibleRatio >= minContentVisibilityRatio;
|
|
429
|
-
if (canShowMinimumContent) {
|
|
430
|
-
position = preferredPosition;
|
|
431
|
-
break determine_position;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
// No preferred position - use original logic (prefer below, fallback to above if more space)
|
|
435
|
-
const elementFitsBelow = spaceBelowTarget >= elementHeight;
|
|
436
|
-
if (elementFitsBelow) {
|
|
437
|
-
position = "below";
|
|
438
|
-
break determine_position;
|
|
439
|
-
}
|
|
440
|
-
const hasMoreSpaceBelow = spaceBelowTarget >= spaceAboveTarget;
|
|
441
|
-
position = hasMoreSpaceBelow ? "below" : "above";
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
let elementPositionTop;
|
|
445
|
-
{
|
|
446
|
-
if (position === "below") {
|
|
447
|
-
// Calculate top position when placing below target (ensure whole pixels)
|
|
448
|
-
const idealTopWhenBelow = targetBottom;
|
|
449
|
-
elementPositionTop =
|
|
450
|
-
idealTopWhenBelow % 1 === 0
|
|
451
|
-
? idealTopWhenBelow
|
|
452
|
-
: Math.floor(idealTopWhenBelow) + 1;
|
|
453
|
-
} else {
|
|
454
|
-
// Calculate top position when placing above target
|
|
455
|
-
const idealTopWhenAbove = targetTop - elementHeight;
|
|
456
|
-
const minimumTopInViewport = 0;
|
|
457
|
-
elementPositionTop =
|
|
458
|
-
idealTopWhenAbove < minimumTopInViewport
|
|
459
|
-
? minimumTopInViewport
|
|
460
|
-
: idealTopWhenAbove;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// Get document scroll for final coordinate conversion
|
|
465
|
-
const { scrollLeft, scrollTop } = document.documentElement;
|
|
466
|
-
const elementDocumentLeft = elementPositionLeft + scrollLeft;
|
|
467
|
-
const elementDocumentTop = elementPositionTop + scrollTop;
|
|
468
|
-
const targetDocumentLeft = targetLeft + scrollLeft;
|
|
469
|
-
const targetDocumentTop = targetTop + scrollTop;
|
|
470
|
-
const targetDocumentRight = targetRight + scrollLeft;
|
|
471
|
-
const targetDocumentBottom = targetBottom + scrollTop;
|
|
472
|
-
|
|
473
|
-
return {
|
|
474
|
-
position,
|
|
475
|
-
left: elementDocumentLeft,
|
|
476
|
-
top: elementDocumentTop,
|
|
477
|
-
width: elementWidth,
|
|
478
|
-
height: elementHeight,
|
|
479
|
-
targetLeft: targetDocumentLeft,
|
|
480
|
-
targetTop: targetDocumentTop,
|
|
481
|
-
targetRight: targetDocumentRight,
|
|
482
|
-
targetBottom: targetDocumentBottom,
|
|
483
|
-
spaceAboveTarget,
|
|
484
|
-
spaceBelowTarget,
|
|
485
|
-
};
|
|
486
|
-
};
|
package/src/pub_sub.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export const createPubSub = (clearOnPublish = false) => {
|
|
2
|
-
const callbackSet = new Set();
|
|
3
|
-
|
|
4
|
-
const publish = (...args) => {
|
|
5
|
-
const results = [];
|
|
6
|
-
for (const callback of callbackSet) {
|
|
7
|
-
const result = callback(...args);
|
|
8
|
-
results.push(result);
|
|
9
|
-
}
|
|
10
|
-
if (clearOnPublish) {
|
|
11
|
-
callbackSet.clear();
|
|
12
|
-
}
|
|
13
|
-
return results;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const subscribe = (callback) => {
|
|
17
|
-
if (typeof callback !== "function") {
|
|
18
|
-
throw new TypeError("callback must be a function");
|
|
19
|
-
}
|
|
20
|
-
callbackSet.add(callback);
|
|
21
|
-
return () => {
|
|
22
|
-
callbackSet.delete(callback);
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const clear = () => {
|
|
27
|
-
callbackSet.clear();
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return [publish, subscribe, clear];
|
|
31
|
-
};
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { addAttributeEffect } from "../attr/add_attribute_effect.js";
|
|
2
|
-
import { getHeight } from "./get_height.js";
|
|
3
|
-
|
|
4
|
-
export const ensureDetailsContentFullHeight = (details) => {
|
|
5
|
-
const updateHeight = () => {
|
|
6
|
-
if (!details.open) {
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
let summary = details.querySelector("summary");
|
|
10
|
-
const summaryNextSiblingSet = new Set();
|
|
11
|
-
{
|
|
12
|
-
let child = summary;
|
|
13
|
-
let nextElementSibling;
|
|
14
|
-
while ((nextElementSibling = child.nextElementSibling)) {
|
|
15
|
-
nextElementSibling.style.height = "auto";
|
|
16
|
-
summaryNextSiblingSet.add(nextElementSibling);
|
|
17
|
-
child = nextElementSibling;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const detailsHeight = getHeight(details);
|
|
22
|
-
let summaryHeight = getHeight(summary);
|
|
23
|
-
let heightBefore = summaryHeight;
|
|
24
|
-
for (const nextElementSibling of summaryNextSiblingSet) {
|
|
25
|
-
const contentHeight = detailsHeight - heightBefore;
|
|
26
|
-
nextElementSibling.style.height = `${contentHeight}px`;
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
updateHeight();
|
|
31
|
-
|
|
32
|
-
const cleanupCallbackSet = new Set();
|
|
33
|
-
update_on_size_change: {
|
|
34
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
35
|
-
requestAnimationFrame(() => {
|
|
36
|
-
updateHeight();
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
resizeObserver.observe(details);
|
|
40
|
-
cleanupCallbackSet.add(() => {
|
|
41
|
-
resizeObserver.disconnect();
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
update_on_toggle: {
|
|
45
|
-
const ontoggle = () => {
|
|
46
|
-
updateHeight();
|
|
47
|
-
};
|
|
48
|
-
details.addEventListener("toggle", ontoggle);
|
|
49
|
-
cleanupCallbackSet.add(() => {
|
|
50
|
-
details.removeEventListener("toggle", ontoggle);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
return () => {
|
|
54
|
-
for (const cleanupCallback of cleanupCallbackSet) {
|
|
55
|
-
cleanupCallback();
|
|
56
|
-
}
|
|
57
|
-
cleanupCallbackSet.clear();
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
addAttributeEffect("data-details-content-full-height", (details) => {
|
|
62
|
-
return ensureDetailsContentFullHeight(details);
|
|
63
|
-
});
|