@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,974 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { startDragToResizeGesture } from "../interaction/drag/drag_to_resize_gesture.js";
|
|
7
|
-
import { captureScrollState } from "../interaction/scroll/capture_scroll.js";
|
|
8
|
-
import { forceStyles } from "../style/dom_styles.js";
|
|
9
|
-
import { createHeightTransition } from "../transition/dom_transition.js";
|
|
10
|
-
import { createGroupTransitionController } from "../transition/group_transition.js";
|
|
11
|
-
import { getHeight } from "./get_height.js";
|
|
12
|
-
import { getInnerHeight } from "./get_inner_height.js";
|
|
13
|
-
import { getMarginSizes } from "./get_margin_sizes.js";
|
|
14
|
-
import { getMinHeight } from "./get_min_height.js";
|
|
15
|
-
import { resolveCSSSize } from "./resolve_css_size.js";
|
|
16
|
-
|
|
17
|
-
const HEIGHT_TRANSITION_DURATION = 300;
|
|
18
|
-
const ANIMATE_TOGGLE = true;
|
|
19
|
-
const ANIMATE_RESIZE_AFTER_MUTATION = true;
|
|
20
|
-
const ANIMATION_THRESHOLD_PX = 10; // Don't animate changes smaller than this
|
|
21
|
-
const DEBUG = false;
|
|
22
|
-
|
|
23
|
-
export const initFlexDetailsSet = (
|
|
24
|
-
container,
|
|
25
|
-
{
|
|
26
|
-
onSizeChange,
|
|
27
|
-
onResizableDetailsChange,
|
|
28
|
-
onMouseResizeEnd,
|
|
29
|
-
onRequestedSizeChange,
|
|
30
|
-
debug = DEBUG,
|
|
31
|
-
} = {},
|
|
32
|
-
) => {
|
|
33
|
-
const flexDetailsSet = {
|
|
34
|
-
cleanup: null,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Create animation controller for managing height animations
|
|
38
|
-
const transitionController = createGroupTransitionController();
|
|
39
|
-
|
|
40
|
-
const cleanupCallbackSet = new Set();
|
|
41
|
-
const cleanup = () => {
|
|
42
|
-
// Cancel any ongoing animations
|
|
43
|
-
transitionController.cancel();
|
|
44
|
-
|
|
45
|
-
for (const cleanupCallback of cleanupCallbackSet) {
|
|
46
|
-
cleanupCallback();
|
|
47
|
-
}
|
|
48
|
-
cleanupCallbackSet.clear();
|
|
49
|
-
};
|
|
50
|
-
flexDetailsSet.cleanup = cleanup;
|
|
51
|
-
|
|
52
|
-
const spaceMap = new Map();
|
|
53
|
-
const marginSizeMap = new Map();
|
|
54
|
-
const requestedSpaceMap = new Map();
|
|
55
|
-
const minSpaceMap = new Map();
|
|
56
|
-
let allocatedSpaceMap = new Map();
|
|
57
|
-
const canGrowSet = new Set();
|
|
58
|
-
const canShrinkSet = new Set();
|
|
59
|
-
let availableSpace;
|
|
60
|
-
let remainingSpace;
|
|
61
|
-
let lastChild;
|
|
62
|
-
const openedDetailsArray = [];
|
|
63
|
-
const spaceToSize = (space, element) => {
|
|
64
|
-
const marginSize = marginSizeMap.get(element);
|
|
65
|
-
return space - marginSize;
|
|
66
|
-
};
|
|
67
|
-
const sizeToSpace = (size, element) => {
|
|
68
|
-
const marginSize = marginSizeMap.get(element);
|
|
69
|
-
return size + marginSize;
|
|
70
|
-
};
|
|
71
|
-
const prepareSpaceDistribution = () => {
|
|
72
|
-
spaceMap.clear();
|
|
73
|
-
marginSizeMap.clear();
|
|
74
|
-
requestedSpaceMap.clear();
|
|
75
|
-
minSpaceMap.clear();
|
|
76
|
-
allocatedSpaceMap.clear();
|
|
77
|
-
canGrowSet.clear();
|
|
78
|
-
canShrinkSet.clear();
|
|
79
|
-
availableSpace = getInnerHeight(container);
|
|
80
|
-
remainingSpace = availableSpace;
|
|
81
|
-
openedDetailsArray.length = 0;
|
|
82
|
-
lastChild = null;
|
|
83
|
-
if (debug) {
|
|
84
|
-
console.debug(`📐 Container space: ${availableSpace}px`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
for (const child of container.children) {
|
|
88
|
-
lastChild = child;
|
|
89
|
-
const marginSizes = getMarginSizes(child);
|
|
90
|
-
const marginSize = marginSizes.top + marginSizes.bottom;
|
|
91
|
-
marginSizeMap.set(child, marginSize);
|
|
92
|
-
|
|
93
|
-
if (!isDetailsElement(child)) {
|
|
94
|
-
const size = getHeight(child);
|
|
95
|
-
spaceMap.set(child, size + marginSize);
|
|
96
|
-
requestedSpaceMap.set(child, size + marginSize);
|
|
97
|
-
minSpaceMap.set(child, size + marginSize);
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
const details = child;
|
|
101
|
-
let size;
|
|
102
|
-
let requestedSize;
|
|
103
|
-
let requestedSizeSource;
|
|
104
|
-
let minSize;
|
|
105
|
-
const summary = details.querySelector("summary");
|
|
106
|
-
const summaryHeight = getHeight(summary);
|
|
107
|
-
|
|
108
|
-
size = getHeight(details);
|
|
109
|
-
|
|
110
|
-
if (details.open) {
|
|
111
|
-
openedDetailsArray.push(details);
|
|
112
|
-
canGrowSet.add(details);
|
|
113
|
-
canShrinkSet.add(details);
|
|
114
|
-
const detailsContent = summary.nextElementSibling;
|
|
115
|
-
let detailsHeight;
|
|
116
|
-
if (detailsContent) {
|
|
117
|
-
const preserveScroll = captureScrollState(detailsContent);
|
|
118
|
-
const restoreSizeStyle = forceStyles(detailsContent, {
|
|
119
|
-
height: "auto",
|
|
120
|
-
});
|
|
121
|
-
const detailsContentHeight = getHeight(detailsContent);
|
|
122
|
-
restoreSizeStyle();
|
|
123
|
-
// Preserve scroll position after height manipulation
|
|
124
|
-
preserveScroll();
|
|
125
|
-
detailsHeight = summaryHeight + detailsContentHeight;
|
|
126
|
-
} else {
|
|
127
|
-
// empty details content like
|
|
128
|
-
// <details><summary>...</summary></details>
|
|
129
|
-
// or textual content like
|
|
130
|
-
// <details><summary>...</summary>textual content</details>
|
|
131
|
-
detailsHeight = size;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (details.hasAttribute("data-requested-height")) {
|
|
135
|
-
const requestedHeightAttribute = details.getAttribute(
|
|
136
|
-
"data-requested-height",
|
|
137
|
-
);
|
|
138
|
-
requestedSize = resolveCSSSize(requestedHeightAttribute);
|
|
139
|
-
if (isNaN(requestedSize) || !isFinite(requestedSize)) {
|
|
140
|
-
console.warn(
|
|
141
|
-
`details ${details.id} has invalid data-requested-height attribute: ${requestedHeightAttribute}`,
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
requestedSizeSource = "data-requested-height attribute";
|
|
145
|
-
} else {
|
|
146
|
-
requestedSize = detailsHeight;
|
|
147
|
-
requestedSizeSource = "summary and content height";
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const dataMinHeight = details.getAttribute("data-min-height");
|
|
151
|
-
if (dataMinHeight) {
|
|
152
|
-
minSize = parseFloat(dataMinHeight, 10);
|
|
153
|
-
} else {
|
|
154
|
-
minSize = getMinHeight(details, availableSpace);
|
|
155
|
-
}
|
|
156
|
-
} else {
|
|
157
|
-
requestedSize = summaryHeight;
|
|
158
|
-
requestedSizeSource = "summary height";
|
|
159
|
-
minSize = summaryHeight;
|
|
160
|
-
}
|
|
161
|
-
spaceMap.set(details, size + marginSize);
|
|
162
|
-
requestedSpaceMap.set(details, requestedSize + marginSize);
|
|
163
|
-
minSpaceMap.set(details, minSize + marginSize);
|
|
164
|
-
if (debug) {
|
|
165
|
-
const currentSizeFormatted = spaceToSize(size + marginSize, details);
|
|
166
|
-
const requestedSizeFormatted = spaceToSize(
|
|
167
|
-
requestedSize + marginSize,
|
|
168
|
-
details,
|
|
169
|
-
);
|
|
170
|
-
const minSizeFormatted = spaceToSize(minSize + marginSize, details);
|
|
171
|
-
console.debug(
|
|
172
|
-
` ${details.id}: ${currentSizeFormatted}px → wants ${requestedSizeFormatted}px (min: ${minSizeFormatted}px) [${requestedSizeSource}]`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
const applyAllocatedSpaces = (resizeDetails) => {
|
|
179
|
-
const changeSet = new Set();
|
|
180
|
-
let maxChange = 0;
|
|
181
|
-
|
|
182
|
-
for (const child of container.children) {
|
|
183
|
-
const allocatedSpace = allocatedSpaceMap.get(child);
|
|
184
|
-
const allocatedSize = spaceToSize(allocatedSpace, child);
|
|
185
|
-
const space = spaceMap.get(child);
|
|
186
|
-
const size = spaceToSize(space, child);
|
|
187
|
-
const sizeChange = Math.abs(size - allocatedSize);
|
|
188
|
-
|
|
189
|
-
if (size === allocatedSize) {
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Track the maximum change to decide if animation is worth it
|
|
194
|
-
maxChange = Math.max(maxChange, sizeChange);
|
|
195
|
-
|
|
196
|
-
if (isDetailsElement(child) && child.open) {
|
|
197
|
-
const syncDetailsContentHeight = prepareSyncDetailsContentHeight(child);
|
|
198
|
-
changeSet.add({
|
|
199
|
-
element: child,
|
|
200
|
-
target: allocatedSize,
|
|
201
|
-
sideEffect: (height, { isAnimationEnd } = {}) => {
|
|
202
|
-
syncDetailsContentHeight(height, {
|
|
203
|
-
isAnimation: true,
|
|
204
|
-
isAnimationEnd,
|
|
205
|
-
});
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
} else {
|
|
209
|
-
changeSet.add({
|
|
210
|
-
element: child,
|
|
211
|
-
target: allocatedSize,
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (changeSet.size === 0) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Don't animate if changes are too small (avoids imperceptible animations that hide scrollbars)
|
|
221
|
-
const shouldAnimate =
|
|
222
|
-
resizeDetails.animated && maxChange >= ANIMATION_THRESHOLD_PX;
|
|
223
|
-
|
|
224
|
-
if (debug && resizeDetails.animated && !shouldAnimate) {
|
|
225
|
-
console.debug(
|
|
226
|
-
`🚫 Skipping animation: max change ${maxChange.toFixed(2)}px < ${ANIMATION_THRESHOLD_PX}px threshold`,
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (!shouldAnimate) {
|
|
231
|
-
const sizeChangeEntries = [];
|
|
232
|
-
for (const { element, target, sideEffect } of changeSet) {
|
|
233
|
-
element.style.height = `${target}px`;
|
|
234
|
-
spaceMap.set(element, sizeToSpace(target, element));
|
|
235
|
-
if (sideEffect) {
|
|
236
|
-
sideEffect(target);
|
|
237
|
-
}
|
|
238
|
-
sizeChangeEntries.push({ element, value: target });
|
|
239
|
-
}
|
|
240
|
-
onSizeChange?.(sizeChangeEntries, resizeDetails);
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Create height animations for each element in changeSet
|
|
245
|
-
const transitions = Array.from(changeSet).map(({ element, target }) => {
|
|
246
|
-
const transition = createHeightTransition(element, target, {
|
|
247
|
-
duration: HEIGHT_TRANSITION_DURATION,
|
|
248
|
-
});
|
|
249
|
-
return transition;
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
const transition = transitionController.animate(transitions, {
|
|
253
|
-
onChange: (changeEntries, isLast) => {
|
|
254
|
-
// Apply side effects for each animated element
|
|
255
|
-
for (const { transition, value } of changeEntries) {
|
|
256
|
-
for (const change of changeSet) {
|
|
257
|
-
if (change.element === transition.key) {
|
|
258
|
-
if (change.sideEffect) {
|
|
259
|
-
change.sideEffect(value, { isAnimationEnd: isLast });
|
|
260
|
-
}
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (onSizeChange) {
|
|
267
|
-
// Convert animation entries to the expected format
|
|
268
|
-
const sizeChangeEntries = changeEntries.map(
|
|
269
|
-
({ transition, value }) => ({
|
|
270
|
-
element: transition.key, // targetKey is the element
|
|
271
|
-
value,
|
|
272
|
-
}),
|
|
273
|
-
);
|
|
274
|
-
onSizeChange(
|
|
275
|
-
sizeChangeEntries,
|
|
276
|
-
isLast ? { ...resizeDetails, animated: false } : resizeDetails,
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
},
|
|
280
|
-
});
|
|
281
|
-
transition.play();
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const allocateSpace = (child, spaceToAllocate, requestSource) => {
|
|
285
|
-
const requestedSpace = requestedSpaceMap.get(child);
|
|
286
|
-
const canShrink = canShrinkSet.has(child);
|
|
287
|
-
const canGrow = canGrowSet.has(child);
|
|
288
|
-
|
|
289
|
-
let allocatedSpace;
|
|
290
|
-
let allocatedSpaceSource;
|
|
291
|
-
allocate: {
|
|
292
|
-
const minSpace = minSpaceMap.get(child);
|
|
293
|
-
if (spaceToAllocate > remainingSpace) {
|
|
294
|
-
if (remainingSpace < minSpace) {
|
|
295
|
-
allocatedSpace = minSpace;
|
|
296
|
-
allocatedSpaceSource = "min space";
|
|
297
|
-
break allocate;
|
|
298
|
-
}
|
|
299
|
-
allocatedSpace = remainingSpace;
|
|
300
|
-
allocatedSpaceSource = "remaining space";
|
|
301
|
-
break allocate;
|
|
302
|
-
}
|
|
303
|
-
if (spaceToAllocate < minSpace) {
|
|
304
|
-
allocatedSpace = minSpace;
|
|
305
|
-
allocatedSpaceSource = "min space";
|
|
306
|
-
break allocate;
|
|
307
|
-
}
|
|
308
|
-
allocatedSpace = spaceToAllocate;
|
|
309
|
-
allocatedSpaceSource = requestSource;
|
|
310
|
-
break allocate;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (allocatedSpace < requestedSpace) {
|
|
314
|
-
if (!canShrink) {
|
|
315
|
-
allocatedSpace = requestedSpace;
|
|
316
|
-
allocatedSpaceSource = `${requestSource} + cannot shrink`;
|
|
317
|
-
}
|
|
318
|
-
} else if (allocatedSpace > requestedSpace) {
|
|
319
|
-
if (!canGrow) {
|
|
320
|
-
allocatedSpace = requestedSpace;
|
|
321
|
-
allocatedSpaceSource = `${requestSource} + cannot grow`;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
remainingSpace -= allocatedSpace;
|
|
326
|
-
if (debug) {
|
|
327
|
-
const allocatedSize = spaceToSize(allocatedSpace, child);
|
|
328
|
-
const sourceInfo =
|
|
329
|
-
allocatedSpaceSource === requestSource
|
|
330
|
-
? ""
|
|
331
|
-
: ` (${allocatedSpaceSource})`;
|
|
332
|
-
if (allocatedSpace === spaceToAllocate) {
|
|
333
|
-
console.debug(
|
|
334
|
-
` → ${allocatedSize}px to "${child.id}"${sourceInfo} | ${remainingSpace}px remaining`,
|
|
335
|
-
);
|
|
336
|
-
} else {
|
|
337
|
-
const requestedSize = spaceToSize(spaceToAllocate, child);
|
|
338
|
-
console.debug(
|
|
339
|
-
` → ${allocatedSize}px -out of ${requestedSize}px wanted- to "${child.id}"${sourceInfo} | ${remainingSpace}px remaining`,
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
allocatedSpaceMap.set(child, allocatedSpace);
|
|
344
|
-
|
|
345
|
-
const space = spaceMap.get(child);
|
|
346
|
-
return allocatedSpace - space;
|
|
347
|
-
};
|
|
348
|
-
const applyDiffOnAllocatedSpace = (child, diff, source) => {
|
|
349
|
-
if (diff === 0) {
|
|
350
|
-
return 0;
|
|
351
|
-
}
|
|
352
|
-
const allocatedSpace = allocatedSpaceMap.get(child);
|
|
353
|
-
remainingSpace += allocatedSpace;
|
|
354
|
-
const spaceToAllocate = allocatedSpace + diff;
|
|
355
|
-
if (debug) {
|
|
356
|
-
console.debug(
|
|
357
|
-
`🔄 ${child.id}: ${allocatedSpace}px + ${diff}px = ${spaceToAllocate}px (${source})`,
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
allocateSpace(child, spaceToAllocate, source);
|
|
361
|
-
const reallocatedSpace = allocatedSpaceMap.get(child);
|
|
362
|
-
return reallocatedSpace - allocatedSpace;
|
|
363
|
-
};
|
|
364
|
-
const distributeAvailableSpace = (source) => {
|
|
365
|
-
if (debug) {
|
|
366
|
-
console.debug(
|
|
367
|
-
`📦 Distributing ${availableSpace}px among ${container.children.length} children:`,
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
for (const child of container.children) {
|
|
371
|
-
allocateSpace(child, requestedSpaceMap.get(child), source);
|
|
372
|
-
}
|
|
373
|
-
if (debug) {
|
|
374
|
-
console.debug(`📦 After distribution: ${remainingSpace}px remaining`);
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
const distributeRemainingSpace = ({ childToGrow, childToShrinkFrom }) => {
|
|
378
|
-
if (!remainingSpace) {
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
if (remainingSpace < 0) {
|
|
382
|
-
const spaceToSteal = -remainingSpace;
|
|
383
|
-
if (debug) {
|
|
384
|
-
console.debug(
|
|
385
|
-
`⚠️ Deficit: ${remainingSpace}px, stealing ${spaceToSteal}px from elements before ${childToShrinkFrom.id}`,
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
updatePreviousSiblingsAllocatedSpace(
|
|
389
|
-
childToShrinkFrom,
|
|
390
|
-
-spaceToSteal,
|
|
391
|
-
`remaining space is negative: ${remainingSpace}px`,
|
|
392
|
-
);
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
if (childToGrow) {
|
|
396
|
-
if (debug) {
|
|
397
|
-
console.debug(
|
|
398
|
-
`✨ Bonus: giving ${remainingSpace}px to ${childToGrow.id}`,
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
applyDiffOnAllocatedSpace(
|
|
402
|
-
childToGrow,
|
|
403
|
-
remainingSpace,
|
|
404
|
-
`remaining space is positive: ${remainingSpace}px`,
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
const updatePreviousSiblingsAllocatedSpace = (
|
|
410
|
-
child,
|
|
411
|
-
diffToApply,
|
|
412
|
-
source,
|
|
413
|
-
mapRemainingDiffToApply,
|
|
414
|
-
) => {
|
|
415
|
-
let spaceDiffSum = 0;
|
|
416
|
-
let remainingDiffToApply = diffToApply;
|
|
417
|
-
let previousSibling = child.previousElementSibling;
|
|
418
|
-
while (previousSibling) {
|
|
419
|
-
if (mapRemainingDiffToApply) {
|
|
420
|
-
remainingDiffToApply = mapRemainingDiffToApply(
|
|
421
|
-
previousSibling,
|
|
422
|
-
remainingDiffToApply,
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
const spaceDiff = applyDiffOnAllocatedSpace(
|
|
426
|
-
previousSibling,
|
|
427
|
-
remainingDiffToApply,
|
|
428
|
-
source,
|
|
429
|
-
);
|
|
430
|
-
if (spaceDiff) {
|
|
431
|
-
spaceDiffSum += spaceDiff;
|
|
432
|
-
remainingDiffToApply -= spaceDiff;
|
|
433
|
-
if (!remainingDiffToApply) {
|
|
434
|
-
break;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
previousSibling = previousSibling.previousElementSibling;
|
|
438
|
-
}
|
|
439
|
-
return spaceDiffSum;
|
|
440
|
-
};
|
|
441
|
-
const updateNextSiblingsAllocatedSpace = (
|
|
442
|
-
child,
|
|
443
|
-
diffToApply,
|
|
444
|
-
reason,
|
|
445
|
-
mapRemainingDiffToApply,
|
|
446
|
-
) => {
|
|
447
|
-
let spaceDiffSum = 0;
|
|
448
|
-
let remainingDiffToApply = diffToApply;
|
|
449
|
-
let nextSibling = child.nextElementSibling;
|
|
450
|
-
while (nextSibling) {
|
|
451
|
-
if (mapRemainingDiffToApply) {
|
|
452
|
-
remainingDiffToApply = mapRemainingDiffToApply(
|
|
453
|
-
nextSibling,
|
|
454
|
-
remainingDiffToApply,
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
const spaceDiff = applyDiffOnAllocatedSpace(
|
|
458
|
-
nextSibling,
|
|
459
|
-
remainingDiffToApply,
|
|
460
|
-
reason,
|
|
461
|
-
);
|
|
462
|
-
if (spaceDiff) {
|
|
463
|
-
spaceDiffSum += spaceDiff;
|
|
464
|
-
remainingDiffToApply -= spaceDiff;
|
|
465
|
-
if (!remainingDiffToApply) {
|
|
466
|
-
break;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
nextSibling = nextSibling.nextElementSibling;
|
|
470
|
-
}
|
|
471
|
-
return spaceDiffSum;
|
|
472
|
-
};
|
|
473
|
-
const updateSiblingAllocatedSpace = (child, diff, reason) => {
|
|
474
|
-
let nextSibling = child.nextElementSibling;
|
|
475
|
-
while (nextSibling) {
|
|
476
|
-
if (!isDetailsElement(nextSibling)) {
|
|
477
|
-
nextSibling = nextSibling.nextElementSibling;
|
|
478
|
-
continue;
|
|
479
|
-
}
|
|
480
|
-
const spaceDiff = applyDiffOnAllocatedSpace(nextSibling, diff, reason);
|
|
481
|
-
if (spaceDiff) {
|
|
482
|
-
return spaceDiff;
|
|
483
|
-
}
|
|
484
|
-
nextSibling = nextSibling.nextElementSibling;
|
|
485
|
-
}
|
|
486
|
-
if (debug) {
|
|
487
|
-
console.debug(
|
|
488
|
-
"coult not update next sibling allocated space, try on previous siblings",
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
let previousSibling = child.previousElementSibling;
|
|
492
|
-
while (previousSibling) {
|
|
493
|
-
if (!isDetailsElement(previousSibling)) {
|
|
494
|
-
previousSibling = previousSibling.previousElementSibling;
|
|
495
|
-
continue;
|
|
496
|
-
}
|
|
497
|
-
const spaceDiff = applyDiffOnAllocatedSpace(
|
|
498
|
-
previousSibling,
|
|
499
|
-
diff,
|
|
500
|
-
reason,
|
|
501
|
-
);
|
|
502
|
-
if (spaceDiff) {
|
|
503
|
-
return spaceDiff;
|
|
504
|
-
}
|
|
505
|
-
previousSibling = previousSibling.previousElementSibling;
|
|
506
|
-
}
|
|
507
|
-
return 0;
|
|
508
|
-
};
|
|
509
|
-
|
|
510
|
-
const saveCurrentSizeAsRequestedSizes = ({
|
|
511
|
-
replaceExistingAttributes,
|
|
512
|
-
} = {}) => {
|
|
513
|
-
for (const child of container.children) {
|
|
514
|
-
if (canGrowSet.has(child) || canShrinkSet.has(child)) {
|
|
515
|
-
if (
|
|
516
|
-
child.hasAttribute("data-requested-height") &&
|
|
517
|
-
!replaceExistingAttributes
|
|
518
|
-
) {
|
|
519
|
-
continue;
|
|
520
|
-
}
|
|
521
|
-
const allocatedSpace = allocatedSpaceMap.get(child);
|
|
522
|
-
child.setAttribute("data-requested-height", allocatedSpace);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
const updateSpaceDistribution = (resizeDetails) => {
|
|
528
|
-
if (debug) {
|
|
529
|
-
console.group(`updateSpaceDistribution: ${resizeDetails.reason}`);
|
|
530
|
-
}
|
|
531
|
-
prepareSpaceDistribution();
|
|
532
|
-
distributeAvailableSpace(resizeDetails.reason);
|
|
533
|
-
distributeRemainingSpace({
|
|
534
|
-
childToGrow: openedDetailsArray[openedDetailsArray.length - 1],
|
|
535
|
-
childToShrinkFrom: lastChild,
|
|
536
|
-
});
|
|
537
|
-
if (
|
|
538
|
-
resizeDetails.reason === "initial_space_distribution" ||
|
|
539
|
-
resizeDetails.reason === "content_change"
|
|
540
|
-
) {
|
|
541
|
-
spaceMap.clear(); // force to set size at start
|
|
542
|
-
}
|
|
543
|
-
applyAllocatedSpaces(resizeDetails);
|
|
544
|
-
saveCurrentSizeAsRequestedSizes();
|
|
545
|
-
if (debug) {
|
|
546
|
-
console.groupEnd();
|
|
547
|
-
}
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
const resizableDetailsIdSet = new Set();
|
|
551
|
-
const updateResizableDetails = () => {
|
|
552
|
-
const currentResizableDetailsIdSet = new Set();
|
|
553
|
-
let hasPreviousOpen = false;
|
|
554
|
-
for (const child of container.children) {
|
|
555
|
-
if (!isDetailsElement(child)) {
|
|
556
|
-
continue;
|
|
557
|
-
}
|
|
558
|
-
if (!child.open) {
|
|
559
|
-
continue;
|
|
560
|
-
}
|
|
561
|
-
if (hasPreviousOpen) {
|
|
562
|
-
currentResizableDetailsIdSet.add(child.id);
|
|
563
|
-
}
|
|
564
|
-
if (!hasPreviousOpen && child.open) {
|
|
565
|
-
hasPreviousOpen = true;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
let someNew;
|
|
570
|
-
let someOld;
|
|
571
|
-
for (const currentId of currentResizableDetailsIdSet) {
|
|
572
|
-
if (!resizableDetailsIdSet.has(currentId)) {
|
|
573
|
-
resizableDetailsIdSet.add(currentId);
|
|
574
|
-
someNew = true;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
for (const id of resizableDetailsIdSet) {
|
|
578
|
-
if (!currentResizableDetailsIdSet.has(id)) {
|
|
579
|
-
resizableDetailsIdSet.delete(id);
|
|
580
|
-
someOld = true;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
if (someNew || someOld) {
|
|
584
|
-
onResizableDetailsChange?.(resizableDetailsIdSet);
|
|
585
|
-
}
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
initial_size: {
|
|
589
|
-
updateSpaceDistribution({
|
|
590
|
-
reason: "initial_space_distribution",
|
|
591
|
-
});
|
|
592
|
-
updateResizableDetails();
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
update_on_toggle: {
|
|
596
|
-
const distributeSpaceAfterToggle = (details) => {
|
|
597
|
-
const reason = details.open
|
|
598
|
-
? `${details.id} just opened`
|
|
599
|
-
: `${details.id} just closed`;
|
|
600
|
-
if (debug) {
|
|
601
|
-
console.group(`distributeSpaceAfterToggle: ${reason}`);
|
|
602
|
-
}
|
|
603
|
-
prepareSpaceDistribution();
|
|
604
|
-
distributeAvailableSpace(reason);
|
|
605
|
-
|
|
606
|
-
const requestedSpace = requestedSpaceMap.get(details);
|
|
607
|
-
const allocatedSpace = allocatedSpaceMap.get(details);
|
|
608
|
-
const spaceToSteal = requestedSpace - allocatedSpace - remainingSpace;
|
|
609
|
-
if (spaceToSteal === 0) {
|
|
610
|
-
distributeRemainingSpace({
|
|
611
|
-
childToGrow: openedDetailsArray[openedDetailsArray.length - 1],
|
|
612
|
-
childToShrinkFrom: lastChild,
|
|
613
|
-
});
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
if (debug) {
|
|
617
|
-
console.debug(
|
|
618
|
-
`${details.id} would like to take ${requestedSpace}px (${reason}). Trying to steal ${spaceToSteal}px from sibling, remaining space: ${remainingSpace}px`,
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
const spaceStolenFromSibling = -updateSiblingAllocatedSpace(
|
|
622
|
-
details,
|
|
623
|
-
-spaceToSteal,
|
|
624
|
-
reason,
|
|
625
|
-
);
|
|
626
|
-
if (spaceStolenFromSibling) {
|
|
627
|
-
if (debug) {
|
|
628
|
-
console.debug(
|
|
629
|
-
`${spaceStolenFromSibling}px space stolen from sibling`,
|
|
630
|
-
);
|
|
631
|
-
}
|
|
632
|
-
applyDiffOnAllocatedSpace(details, requestedSpace, reason);
|
|
633
|
-
} else {
|
|
634
|
-
if (debug) {
|
|
635
|
-
console.debug(
|
|
636
|
-
`no space could be stolen from sibling, remaining space: ${remainingSpace}px`,
|
|
637
|
-
);
|
|
638
|
-
}
|
|
639
|
-
distributeRemainingSpace({
|
|
640
|
-
childToGrow: openedDetailsArray[0],
|
|
641
|
-
childToShrinkFrom: lastChild,
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
if (debug) {
|
|
645
|
-
console.groupEnd();
|
|
646
|
-
}
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
for (const child of container.children) {
|
|
650
|
-
if (!isDetailsElement(child)) {
|
|
651
|
-
continue;
|
|
652
|
-
}
|
|
653
|
-
const details = child;
|
|
654
|
-
const ontoggle = () => {
|
|
655
|
-
distributeSpaceAfterToggle(details);
|
|
656
|
-
applyAllocatedSpaces({
|
|
657
|
-
reason: details.open ? "details_opened" : "details_closed",
|
|
658
|
-
animated: ANIMATE_TOGGLE,
|
|
659
|
-
});
|
|
660
|
-
updateResizableDetails();
|
|
661
|
-
};
|
|
662
|
-
if (details.open) {
|
|
663
|
-
setTimeout(() => {
|
|
664
|
-
details.addEventListener("toggle", ontoggle);
|
|
665
|
-
});
|
|
666
|
-
} else {
|
|
667
|
-
details.addEventListener("toggle", ontoggle);
|
|
668
|
-
}
|
|
669
|
-
cleanupCallbackSet.add(() => {
|
|
670
|
-
details.removeEventListener("toggle", ontoggle);
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
resize_with_mouse: {
|
|
676
|
-
const prepareResize = () => {
|
|
677
|
-
let resizedElement;
|
|
678
|
-
// let startSpaceMap;
|
|
679
|
-
let startAllocatedSpaceMap;
|
|
680
|
-
let currentAllocatedSpaceMap;
|
|
681
|
-
|
|
682
|
-
const start = (element) => {
|
|
683
|
-
updateSpaceDistribution({
|
|
684
|
-
reason: "mouse_resize_start",
|
|
685
|
-
});
|
|
686
|
-
resizedElement = element;
|
|
687
|
-
// startSpaceMap = new Map(spaceMap);
|
|
688
|
-
startAllocatedSpaceMap = new Map(allocatedSpaceMap);
|
|
689
|
-
};
|
|
690
|
-
|
|
691
|
-
const applyMoveDiffToSizes = (moveDiff, reason) => {
|
|
692
|
-
let spaceDiff = 0;
|
|
693
|
-
let remainingMoveToApply;
|
|
694
|
-
if (moveDiff > 0) {
|
|
695
|
-
remainingMoveToApply = moveDiff;
|
|
696
|
-
next_siblings_grow: {
|
|
697
|
-
// alors ici on veut grow pour tenter de restaurer la diff
|
|
698
|
-
// entre requestedMap et spaceMap
|
|
699
|
-
// s'il n'y en a pas alors on aura pas appliquer ce move
|
|
700
|
-
const spaceGivenToNextSiblings = updateNextSiblingsAllocatedSpace(
|
|
701
|
-
resizedElement,
|
|
702
|
-
remainingMoveToApply,
|
|
703
|
-
reason,
|
|
704
|
-
(nextSibling) => {
|
|
705
|
-
const requestedSpace = requestedSpaceMap.get(nextSibling);
|
|
706
|
-
const space = spaceMap.get(nextSibling);
|
|
707
|
-
return requestedSpace - space;
|
|
708
|
-
},
|
|
709
|
-
);
|
|
710
|
-
if (spaceGivenToNextSiblings) {
|
|
711
|
-
spaceDiff -= spaceGivenToNextSiblings;
|
|
712
|
-
remainingMoveToApply -= spaceGivenToNextSiblings;
|
|
713
|
-
if (debug) {
|
|
714
|
-
console.debug(
|
|
715
|
-
`${spaceGivenToNextSiblings}px given to previous siblings`,
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
previous_siblings_shrink: {
|
|
721
|
-
const spaceStolenFromPreviousSiblings =
|
|
722
|
-
-updatePreviousSiblingsAllocatedSpace(
|
|
723
|
-
resizedElement,
|
|
724
|
-
-remainingMoveToApply,
|
|
725
|
-
reason,
|
|
726
|
-
);
|
|
727
|
-
if (spaceStolenFromPreviousSiblings) {
|
|
728
|
-
spaceDiff += spaceStolenFromPreviousSiblings;
|
|
729
|
-
remainingMoveToApply -= spaceStolenFromPreviousSiblings;
|
|
730
|
-
if (debug) {
|
|
731
|
-
console.debug(
|
|
732
|
-
`${spaceStolenFromPreviousSiblings}px stolen from previous siblings`,
|
|
733
|
-
);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
self_grow: {
|
|
738
|
-
applyDiffOnAllocatedSpace(resizedElement, spaceDiff, reason);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
remainingMoveToApply = -moveDiff;
|
|
743
|
-
self_shrink: {
|
|
744
|
-
const selfShrink = -applyDiffOnAllocatedSpace(
|
|
745
|
-
resizedElement,
|
|
746
|
-
-remainingMoveToApply,
|
|
747
|
-
reason,
|
|
748
|
-
);
|
|
749
|
-
remainingMoveToApply -= selfShrink;
|
|
750
|
-
spaceDiff += selfShrink;
|
|
751
|
-
}
|
|
752
|
-
next_siblings_shrink: {
|
|
753
|
-
const nextSiblingsShrink = -updateNextSiblingsAllocatedSpace(
|
|
754
|
-
resizedElement,
|
|
755
|
-
-remainingMoveToApply,
|
|
756
|
-
reason,
|
|
757
|
-
);
|
|
758
|
-
if (nextSiblingsShrink) {
|
|
759
|
-
remainingMoveToApply -= nextSiblingsShrink;
|
|
760
|
-
spaceDiff += nextSiblingsShrink;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
previous_sibling_grow: {
|
|
764
|
-
updatePreviousSiblingsAllocatedSpace(
|
|
765
|
-
resizedElement,
|
|
766
|
-
spaceDiff,
|
|
767
|
-
reason,
|
|
768
|
-
);
|
|
769
|
-
}
|
|
770
|
-
};
|
|
771
|
-
|
|
772
|
-
const move = (yMove, gesture) => {
|
|
773
|
-
// if (isNaN(moveRequestedSize) || !isFinite(moveRequestedSize)) {
|
|
774
|
-
// console.warn(
|
|
775
|
-
// `requestResize called with invalid size: ${moveRequestedSize}`,
|
|
776
|
-
// );
|
|
777
|
-
// return;
|
|
778
|
-
// }
|
|
779
|
-
const reason = `applying ${yMove}px move on ${resizedElement.id}`;
|
|
780
|
-
if (debug) {
|
|
781
|
-
console.group(reason);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
const moveDiff = -yMove;
|
|
785
|
-
applyMoveDiffToSizes(moveDiff, reason);
|
|
786
|
-
applyAllocatedSpaces({
|
|
787
|
-
reason: gesture.isMouseUp ? "mouse_resize_end" : "mouse_resize",
|
|
788
|
-
});
|
|
789
|
-
currentAllocatedSpaceMap = new Map(allocatedSpaceMap);
|
|
790
|
-
allocatedSpaceMap = new Map(startAllocatedSpaceMap);
|
|
791
|
-
if (debug) {
|
|
792
|
-
console.groupEnd();
|
|
793
|
-
}
|
|
794
|
-
};
|
|
795
|
-
|
|
796
|
-
const end = () => {
|
|
797
|
-
if (currentAllocatedSpaceMap) {
|
|
798
|
-
allocatedSpaceMap = currentAllocatedSpaceMap;
|
|
799
|
-
saveCurrentSizeAsRequestedSizes({ replaceExistingAttributes: true });
|
|
800
|
-
if (onRequestedSizeChange) {
|
|
801
|
-
for (const [child, allocatedSpace] of allocatedSpaceMap) {
|
|
802
|
-
const size = spaceToSize(allocatedSpace, child);
|
|
803
|
-
onRequestedSizeChange(child, size);
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
onMouseResizeEnd?.();
|
|
807
|
-
}
|
|
808
|
-
};
|
|
809
|
-
|
|
810
|
-
return { start, move, end };
|
|
811
|
-
};
|
|
812
|
-
|
|
813
|
-
const onmousedown = (event) => {
|
|
814
|
-
const { start, move, end } = prepareResize();
|
|
815
|
-
|
|
816
|
-
startDragToResizeGesture(event, {
|
|
817
|
-
onDragStart: (gesture) => {
|
|
818
|
-
start(gesture.element);
|
|
819
|
-
},
|
|
820
|
-
onDrag: (gesture) => {
|
|
821
|
-
const yMove = gesture.yMove;
|
|
822
|
-
move(yMove, gesture);
|
|
823
|
-
},
|
|
824
|
-
onRelease: () => {
|
|
825
|
-
end();
|
|
826
|
-
},
|
|
827
|
-
constrainedFeedbackLine: false,
|
|
828
|
-
});
|
|
829
|
-
};
|
|
830
|
-
container.addEventListener("mousedown", onmousedown);
|
|
831
|
-
cleanupCallbackSet.add(() => {
|
|
832
|
-
container.removeEventListener("mousedown", onmousedown);
|
|
833
|
-
});
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
update_on_container_resize: {
|
|
837
|
-
/**
|
|
838
|
-
* In the following HTML browser will set `<div>` height as if it was "auto"
|
|
839
|
-
*
|
|
840
|
-
* ```html
|
|
841
|
-
* <details style="height: 100px;">
|
|
842
|
-
* <summary>...</summary>
|
|
843
|
-
* <div style="height: 100%"></div>
|
|
844
|
-
* </details>
|
|
845
|
-
* ```
|
|
846
|
-
*
|
|
847
|
-
* So we always maintain a precise px height for the details content to ensure
|
|
848
|
-
* it takes 100% of the details height (minus the summay)
|
|
849
|
-
*
|
|
850
|
-
* To achieve this we need to update these px heights when the container size changes
|
|
851
|
-
*/
|
|
852
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
853
|
-
updateSpaceDistribution({
|
|
854
|
-
reason: "container_resize",
|
|
855
|
-
});
|
|
856
|
-
});
|
|
857
|
-
resizeObserver.observe(container);
|
|
858
|
-
cleanupCallbackSet.add(() => {
|
|
859
|
-
resizeObserver.disconnect();
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
update_on_content_change: {
|
|
864
|
-
// Track when the DOM structure changes inside the container
|
|
865
|
-
// This detects when:
|
|
866
|
-
// - Details elements are added/removed
|
|
867
|
-
// - The content inside details elements changes
|
|
868
|
-
const mutationObserver = new MutationObserver((mutations) => {
|
|
869
|
-
for (const mutation of mutations) {
|
|
870
|
-
if (mutation.type === "childList") {
|
|
871
|
-
updateSpaceDistribution({
|
|
872
|
-
reason: "content_change",
|
|
873
|
-
animated: ANIMATE_RESIZE_AFTER_MUTATION,
|
|
874
|
-
});
|
|
875
|
-
return;
|
|
876
|
-
}
|
|
877
|
-
if (mutation.type === "characterData") {
|
|
878
|
-
updateSpaceDistribution({
|
|
879
|
-
reason: "content_change",
|
|
880
|
-
animated: ANIMATE_RESIZE_AFTER_MUTATION,
|
|
881
|
-
});
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
});
|
|
886
|
-
mutationObserver.observe(container, {
|
|
887
|
-
childList: true,
|
|
888
|
-
subtree: true,
|
|
889
|
-
characterData: true,
|
|
890
|
-
});
|
|
891
|
-
cleanupCallbackSet.add(() => {
|
|
892
|
-
mutationObserver.disconnect();
|
|
893
|
-
});
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
return flexDetailsSet;
|
|
897
|
-
};
|
|
898
|
-
|
|
899
|
-
const prepareSyncDetailsContentHeight = (details) => {
|
|
900
|
-
const getHeightCssValue = (height) => {
|
|
901
|
-
return `${height}px`;
|
|
902
|
-
};
|
|
903
|
-
|
|
904
|
-
const summary = details.querySelector("summary");
|
|
905
|
-
const summaryHeight = getHeight(summary);
|
|
906
|
-
details.style.setProperty(
|
|
907
|
-
"--summary-height",
|
|
908
|
-
getHeightCssValue(summaryHeight),
|
|
909
|
-
);
|
|
910
|
-
|
|
911
|
-
const content = summary.nextElementSibling;
|
|
912
|
-
if (!content) {
|
|
913
|
-
return (detailsHeight) => {
|
|
914
|
-
details.style.setProperty(
|
|
915
|
-
"--details-height",
|
|
916
|
-
getHeightCssValue(detailsHeight),
|
|
917
|
-
);
|
|
918
|
-
details.style.setProperty(
|
|
919
|
-
"--content-height",
|
|
920
|
-
getHeightCssValue(detailsHeight - summaryHeight),
|
|
921
|
-
);
|
|
922
|
-
};
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
// Capture scroll state at the beginning before any DOM manipulation
|
|
926
|
-
const preserveScroll = captureScrollState(content);
|
|
927
|
-
content.style.height = "var(--content-height)";
|
|
928
|
-
|
|
929
|
-
const contentComputedStyle = getComputedStyle(content);
|
|
930
|
-
const scrollbarMightTakeHorizontalSpace =
|
|
931
|
-
contentComputedStyle.overflowY === "auto" &&
|
|
932
|
-
contentComputedStyle.scrollbarGutter !== "stable";
|
|
933
|
-
|
|
934
|
-
return (detailsHeight, { isAnimation, isAnimationEnd } = {}) => {
|
|
935
|
-
const contentHeight = detailsHeight - summaryHeight;
|
|
936
|
-
details.style.setProperty(
|
|
937
|
-
"--details-height",
|
|
938
|
-
getHeightCssValue(detailsHeight),
|
|
939
|
-
);
|
|
940
|
-
details.style.setProperty(
|
|
941
|
-
"--content-height",
|
|
942
|
-
getHeightCssValue(contentHeight),
|
|
943
|
-
);
|
|
944
|
-
|
|
945
|
-
if (!isAnimation || isAnimationEnd) {
|
|
946
|
-
if (scrollbarMightTakeHorizontalSpace) {
|
|
947
|
-
// Fix scrollbar induced overflow:
|
|
948
|
-
//
|
|
949
|
-
// 1. browser displays a scrollbar because there is an overflow inside overflow: auto
|
|
950
|
-
// 2. we set height exactly to the natural height required to prevent overflow
|
|
951
|
-
//
|
|
952
|
-
// actual: browser keeps scrollbar displayed
|
|
953
|
-
// expected: scrollbar is hidden
|
|
954
|
-
//
|
|
955
|
-
// Solution: Temporarily prevent scrollbar to display
|
|
956
|
-
// force layout recalculation, then restore
|
|
957
|
-
const restoreOverflow = forceStyles(content, {
|
|
958
|
-
"overflow-y": "hidden",
|
|
959
|
-
});
|
|
960
|
-
// eslint-disable-next-line no-unused-expressions
|
|
961
|
-
content.offsetHeight;
|
|
962
|
-
restoreOverflow();
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
// Preserve scroll position at the end after all DOM manipulations
|
|
967
|
-
// The captureScrollState function is smart enough to handle new dimensions
|
|
968
|
-
preserveScroll();
|
|
969
|
-
};
|
|
970
|
-
};
|
|
971
|
-
|
|
972
|
-
const isDetailsElement = (element) => {
|
|
973
|
-
return element && element.tagName === "DETAILS";
|
|
974
|
-
};
|