@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,366 +0,0 @@
|
|
|
1
|
-
import { EASING } from "./easing.js";
|
|
2
|
-
import {
|
|
3
|
-
addOnTimeline,
|
|
4
|
-
getTimelineCurrentTime,
|
|
5
|
-
removeFromTimeline,
|
|
6
|
-
} from "./transition_timeline.js";
|
|
7
|
-
|
|
8
|
-
// Default lifecycle methods that do nothing
|
|
9
|
-
const LIFECYCLE_DEFAULT = {
|
|
10
|
-
setup: () => {},
|
|
11
|
-
pause: () => {},
|
|
12
|
-
cancel: () => {},
|
|
13
|
-
finish: () => {},
|
|
14
|
-
updateTarget: () => {},
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const createTransition = ({
|
|
18
|
-
constructor,
|
|
19
|
-
key,
|
|
20
|
-
from,
|
|
21
|
-
to,
|
|
22
|
-
lifecycle = LIFECYCLE_DEFAULT,
|
|
23
|
-
onUpdate,
|
|
24
|
-
minDiff,
|
|
25
|
-
...rest
|
|
26
|
-
} = {}) => {
|
|
27
|
-
const [updateCallbacks, executeUpdateCallbacks] = createCallbackController();
|
|
28
|
-
const [finishCallbacks, executeFinishCallbacks] = createCallbackController();
|
|
29
|
-
const channels = {
|
|
30
|
-
update: updateCallbacks,
|
|
31
|
-
finish: finishCallbacks,
|
|
32
|
-
};
|
|
33
|
-
if (onUpdate) {
|
|
34
|
-
updateCallbacks.add(onUpdate);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let playState = "idle"; // 'idle', 'running', 'paused', 'finished'
|
|
38
|
-
let isFirstUpdate = false;
|
|
39
|
-
let resume;
|
|
40
|
-
let executionLifecycle = null;
|
|
41
|
-
|
|
42
|
-
const start = () => {
|
|
43
|
-
isFirstUpdate = true;
|
|
44
|
-
playState = "running";
|
|
45
|
-
|
|
46
|
-
executionLifecycle = lifecycle.setup(transition);
|
|
47
|
-
|
|
48
|
-
// Allow setup to override from value if transition.from is undefined
|
|
49
|
-
if (
|
|
50
|
-
transition.from === undefined &&
|
|
51
|
-
executionLifecycle.from !== undefined
|
|
52
|
-
) {
|
|
53
|
-
transition.from = executionLifecycle.from;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const diff = Math.abs(transition.to - transition.from);
|
|
57
|
-
if (diff === 0) {
|
|
58
|
-
console.warn(
|
|
59
|
-
`${constructor.name} transition has identical from and to values (${transition.from}). This transition will have no effect.`,
|
|
60
|
-
);
|
|
61
|
-
} else if (typeof minDiff === "number" && diff < minDiff) {
|
|
62
|
-
console.warn(
|
|
63
|
-
`${constructor.name} transition difference is very small (${diff}). Consider if this transition is necessary (minimum threshold: ${minDiff}).`,
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
transition.update(transition.value);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const transition = {
|
|
70
|
-
constructor,
|
|
71
|
-
key,
|
|
72
|
-
from,
|
|
73
|
-
to,
|
|
74
|
-
value: from,
|
|
75
|
-
timing: "",
|
|
76
|
-
channels,
|
|
77
|
-
get playState() {
|
|
78
|
-
return playState;
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
play: () => {
|
|
82
|
-
if (playState === "idle") {
|
|
83
|
-
transition.value = transition.from;
|
|
84
|
-
transition.timing = "";
|
|
85
|
-
start();
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (playState === "running") {
|
|
89
|
-
console.warn("transition already running");
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
if (playState === "paused") {
|
|
93
|
-
playState = "running";
|
|
94
|
-
resume();
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
// "finished"
|
|
98
|
-
start();
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
update: (value, isLast) => {
|
|
102
|
-
if (playState === "idle") {
|
|
103
|
-
console.warn("Cannot update transition that is idle");
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (playState === "finished") {
|
|
107
|
-
console.warn("Cannot update a finished transition");
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
transition.value = value;
|
|
112
|
-
transition.timing = isLast ? "end" : isFirstUpdate ? "start" : "progress";
|
|
113
|
-
isFirstUpdate = false;
|
|
114
|
-
executionLifecycle.update(transition);
|
|
115
|
-
executeUpdateCallbacks(transition);
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
pause: () => {
|
|
119
|
-
if (playState === "paused") {
|
|
120
|
-
console.warn("transition already paused");
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
if (playState === "finished") {
|
|
124
|
-
console.warn("Cannot pause a finished transition");
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
playState = "paused";
|
|
128
|
-
|
|
129
|
-
// Let the transition handle its own pause logic
|
|
130
|
-
resume = lifecycle.pause(transition);
|
|
131
|
-
},
|
|
132
|
-
|
|
133
|
-
cancel: () => {
|
|
134
|
-
if (executionLifecycle) {
|
|
135
|
-
lifecycle.cancel(transition);
|
|
136
|
-
executionLifecycle.teardown();
|
|
137
|
-
executionLifecycle.restore();
|
|
138
|
-
}
|
|
139
|
-
resume = null;
|
|
140
|
-
playState = "idle";
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
finish: () => {
|
|
144
|
-
if (playState === "idle") {
|
|
145
|
-
console.warn("Cannot finish a transition that is idle");
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
if (playState === "finished") {
|
|
149
|
-
console.warn("transition already finished");
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
// "running" or "paused"
|
|
153
|
-
lifecycle.finish(transition);
|
|
154
|
-
executionLifecycle.teardown();
|
|
155
|
-
resume = null;
|
|
156
|
-
playState = "finished";
|
|
157
|
-
executeFinishCallbacks();
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
reverse: () => {
|
|
161
|
-
if (playState === "idle") {
|
|
162
|
-
console.warn("Cannot reverse a transition that is idle");
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
if (playState === "finished") {
|
|
166
|
-
console.warn("Cannot reverse a finished transition");
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Simply swap from and to values to reverse direction
|
|
171
|
-
const originalFrom = transition.from;
|
|
172
|
-
const originalTo = transition.to;
|
|
173
|
-
|
|
174
|
-
transition.from = originalTo;
|
|
175
|
-
transition.to = originalFrom;
|
|
176
|
-
|
|
177
|
-
// Let the transition handle its own reverse logic (if any)
|
|
178
|
-
if (lifecycle.reverse) {
|
|
179
|
-
lifecycle.reverse(transition);
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
updateTarget: (newTarget) => {
|
|
184
|
-
if (
|
|
185
|
-
typeof newTarget !== "number" ||
|
|
186
|
-
isNaN(newTarget) ||
|
|
187
|
-
!isFinite(newTarget)
|
|
188
|
-
) {
|
|
189
|
-
throw new Error(
|
|
190
|
-
`updateTarget: newTarget must be a finite number, got ${newTarget}`,
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
if (playState === "idle") {
|
|
194
|
-
console.warn("Cannot update target of idle transition");
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
if (playState === "finished") {
|
|
198
|
-
console.warn("Cannot update target of finished transition");
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
const currentValue = transition.value;
|
|
202
|
-
transition.from = currentValue;
|
|
203
|
-
transition.to = newTarget;
|
|
204
|
-
|
|
205
|
-
// Let the transition handle its own target update logic
|
|
206
|
-
lifecycle.updateTarget(transition);
|
|
207
|
-
},
|
|
208
|
-
|
|
209
|
-
...rest,
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
return transition;
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
// Timeline-managed transition that adds/removes itself from the animation timeline
|
|
216
|
-
export const createTimelineTransition = ({
|
|
217
|
-
isVisual,
|
|
218
|
-
duration,
|
|
219
|
-
fps = 60,
|
|
220
|
-
easing = EASING.EASE_OUT,
|
|
221
|
-
lifecycle,
|
|
222
|
-
startProgress = 0, // Progress to start from (0-1)
|
|
223
|
-
...options
|
|
224
|
-
}) => {
|
|
225
|
-
if (typeof duration !== "number" || duration <= 0) {
|
|
226
|
-
throw new Error(
|
|
227
|
-
`Invalid duration: ${duration}. Duration must be a positive number.`,
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
let lastUpdateTime = -1;
|
|
232
|
-
|
|
233
|
-
const timeChangeCallback = () => {
|
|
234
|
-
const timelineCurrentTime = getTimelineCurrentTime();
|
|
235
|
-
const msElapsedSinceStart = timelineCurrentTime - transition.startTime;
|
|
236
|
-
const msRemaining = transition.duration - msElapsedSinceStart;
|
|
237
|
-
|
|
238
|
-
if (
|
|
239
|
-
// we reach the end, round progress to 1
|
|
240
|
-
msRemaining < 0 ||
|
|
241
|
-
// we are very close from the end, round progress to 1
|
|
242
|
-
msRemaining <= transition.frameDuration
|
|
243
|
-
) {
|
|
244
|
-
transition.frameRemainingCount = 0;
|
|
245
|
-
transition.progress = 1;
|
|
246
|
-
transition.update(transition.to, true);
|
|
247
|
-
transition.finish();
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
if (lastUpdateTime === -1) {
|
|
251
|
-
// First frame - always allow
|
|
252
|
-
} else {
|
|
253
|
-
const timeSinceLastUpdate = timelineCurrentTime - lastUpdateTime;
|
|
254
|
-
// Allow rendering if we're within 3ms of the target frame duration
|
|
255
|
-
// This prevents choppy animations when browser timing is slightly off
|
|
256
|
-
const frameTimeTolerance = 3; // ms
|
|
257
|
-
const targetFrameTime = transition.frameDuration - frameTimeTolerance;
|
|
258
|
-
|
|
259
|
-
// Skip update only if we're significantly early
|
|
260
|
-
if (timeSinceLastUpdate < targetFrameTime) {
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
lastUpdateTime = timelineCurrentTime;
|
|
265
|
-
const rawProgress = Math.min(msElapsedSinceStart / transition.duration, 1);
|
|
266
|
-
// Apply start progress offset - transition runs from startProgress to 1
|
|
267
|
-
const progress = startProgress + rawProgress * (1 - startProgress);
|
|
268
|
-
transition.progress = progress;
|
|
269
|
-
const easedProgress = transition.easing(progress);
|
|
270
|
-
const value =
|
|
271
|
-
transition.from + (transition.to - transition.from) * easedProgress;
|
|
272
|
-
transition.frameRemainingCount = Math.ceil(
|
|
273
|
-
msRemaining / transition.frameDuration,
|
|
274
|
-
);
|
|
275
|
-
transition.update(value);
|
|
276
|
-
};
|
|
277
|
-
const onTimelineNeeded = () => {
|
|
278
|
-
addOnTimeline(timeChangeCallback, isVisual);
|
|
279
|
-
};
|
|
280
|
-
const onTimelineNotNeeded = () => {
|
|
281
|
-
removeFromTimeline(timeChangeCallback, isVisual);
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const { setup } = lifecycle;
|
|
285
|
-
const transition = createTransition({
|
|
286
|
-
...options,
|
|
287
|
-
startTime: null,
|
|
288
|
-
progress: startProgress, // Initialize with start progress
|
|
289
|
-
duration,
|
|
290
|
-
easing,
|
|
291
|
-
fps,
|
|
292
|
-
get frameDuration() {
|
|
293
|
-
return 1000 / fps;
|
|
294
|
-
},
|
|
295
|
-
frameRemainingCount: 0,
|
|
296
|
-
startProgress, // Store for calculations
|
|
297
|
-
lifecycle: {
|
|
298
|
-
...lifecycle,
|
|
299
|
-
setup: (transition) => {
|
|
300
|
-
// Handle timeline management
|
|
301
|
-
lastUpdateTime = -1;
|
|
302
|
-
transition.startTime = getTimelineCurrentTime();
|
|
303
|
-
// Calculate remaining frames based on remaining progress
|
|
304
|
-
const remainingProgress = 1 - startProgress;
|
|
305
|
-
const remainingDuration = transition.duration * remainingProgress;
|
|
306
|
-
transition.frameRemainingCount = Math.ceil(
|
|
307
|
-
remainingDuration / transition.frameDuration,
|
|
308
|
-
);
|
|
309
|
-
onTimelineNeeded();
|
|
310
|
-
// Call the original setup
|
|
311
|
-
return setup(transition);
|
|
312
|
-
},
|
|
313
|
-
pause: (transition) => {
|
|
314
|
-
const pauseTime = getTimelineCurrentTime();
|
|
315
|
-
onTimelineNotNeeded();
|
|
316
|
-
return () => {
|
|
317
|
-
const pausedDuration = getTimelineCurrentTime() - pauseTime;
|
|
318
|
-
transition.startTime += pausedDuration;
|
|
319
|
-
// Only adjust lastUpdateTime if it was set (not -1)
|
|
320
|
-
if (lastUpdateTime !== -1) {
|
|
321
|
-
lastUpdateTime += pausedDuration;
|
|
322
|
-
}
|
|
323
|
-
onTimelineNeeded();
|
|
324
|
-
};
|
|
325
|
-
},
|
|
326
|
-
updateTarget: (transition) => {
|
|
327
|
-
transition.startTime = getTimelineCurrentTime();
|
|
328
|
-
// Don't reset lastUpdateTime - we want visual continuity for smooth target updates
|
|
329
|
-
// Recalculate remaining frames from current progress
|
|
330
|
-
const remainingProgress = 1 - transition.progress;
|
|
331
|
-
const remainingDuration = transition.duration * remainingProgress;
|
|
332
|
-
transition.frameRemainingCount = Math.ceil(
|
|
333
|
-
remainingDuration / transition.frameDuration,
|
|
334
|
-
);
|
|
335
|
-
},
|
|
336
|
-
cancel: () => {
|
|
337
|
-
onTimelineNotNeeded();
|
|
338
|
-
},
|
|
339
|
-
finish: () => {
|
|
340
|
-
onTimelineNotNeeded();
|
|
341
|
-
},
|
|
342
|
-
},
|
|
343
|
-
});
|
|
344
|
-
return transition;
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
const createCallbackController = () => {
|
|
348
|
-
const callbackSet = new Set();
|
|
349
|
-
const execute = (...args) => {
|
|
350
|
-
for (const callback of callbackSet) {
|
|
351
|
-
callback(...args);
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
|
-
const callbacks = {
|
|
355
|
-
add: (callback) => {
|
|
356
|
-
if (typeof callback !== "function") {
|
|
357
|
-
throw new TypeError("Callback must be a function");
|
|
358
|
-
}
|
|
359
|
-
callbackSet.add(callback);
|
|
360
|
-
return () => {
|
|
361
|
-
callbackSet.delete(callback);
|
|
362
|
-
};
|
|
363
|
-
},
|
|
364
|
-
};
|
|
365
|
-
return [callbacks, execute];
|
|
366
|
-
};
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
export const getTimelineCurrentTime = () => {
|
|
2
|
-
return document.timeline.currentTime;
|
|
3
|
-
};
|
|
4
|
-
|
|
5
|
-
const visualCallbackSet = new Set();
|
|
6
|
-
const backgroundCallbackSet = new Set();
|
|
7
|
-
export const addOnTimeline = (callback, isVisual) => {
|
|
8
|
-
if (isVisual) {
|
|
9
|
-
visualCallbackSet.add(callback);
|
|
10
|
-
} else {
|
|
11
|
-
backgroundCallbackSet.add(callback);
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
export const removeFromTimeline = (callback, isVisual) => {
|
|
15
|
-
if (isVisual) {
|
|
16
|
-
visualCallbackSet.delete(callback);
|
|
17
|
-
} else {
|
|
18
|
-
backgroundCallbackSet.delete(callback);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// We need setTimeout to animate things like volume because requestAnimationFrame would be killed when tab is not visible
|
|
23
|
-
// while we might want to fadeout volumn when leaving the page for instance
|
|
24
|
-
const createBackgroundUpdateLoop = () => {
|
|
25
|
-
let timeout;
|
|
26
|
-
const update = () => {
|
|
27
|
-
for (const backgroundCallback of backgroundCallbackSet) {
|
|
28
|
-
backgroundCallback();
|
|
29
|
-
}
|
|
30
|
-
timeout = setTimeout(update, 16); // roughly 60fps
|
|
31
|
-
};
|
|
32
|
-
return {
|
|
33
|
-
start: () => {
|
|
34
|
-
timeout = setTimeout(update, 16);
|
|
35
|
-
},
|
|
36
|
-
stop: () => {
|
|
37
|
-
clearTimeout(timeout);
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
// For visual things we use animation frame which is more performant and made for this
|
|
42
|
-
const createAnimationFrameLoop = () => {
|
|
43
|
-
let animationFrame = null;
|
|
44
|
-
const update = () => {
|
|
45
|
-
for (const visualCallback of visualCallbackSet) {
|
|
46
|
-
visualCallback();
|
|
47
|
-
}
|
|
48
|
-
animationFrame = requestAnimationFrame(update);
|
|
49
|
-
};
|
|
50
|
-
return {
|
|
51
|
-
start: () => {
|
|
52
|
-
animationFrame = requestAnimationFrame(update);
|
|
53
|
-
},
|
|
54
|
-
stop: () => {
|
|
55
|
-
cancelAnimationFrame(animationFrame);
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
const backgroundUpdateLoop = createBackgroundUpdateLoop();
|
|
60
|
-
const animationUpdateLoop = createAnimationFrameLoop();
|
|
61
|
-
|
|
62
|
-
let timelineIsRunning = false;
|
|
63
|
-
export const stopTimeline = () => {
|
|
64
|
-
if (!timelineIsRunning) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
timelineIsRunning = false;
|
|
68
|
-
backgroundUpdateLoop.stop();
|
|
69
|
-
animationUpdateLoop.stop();
|
|
70
|
-
};
|
|
71
|
-
export const startTimeline = () => {
|
|
72
|
-
if (timelineIsRunning) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
timelineIsRunning = true;
|
|
76
|
-
backgroundUpdateLoop.start();
|
|
77
|
-
animationUpdateLoop.start();
|
|
78
|
-
};
|
|
79
|
-
startTimeline();
|
package/src/traversal.js
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
export const findAncestor = (node, predicate) => {
|
|
2
|
-
let ancestor = node.parentNode;
|
|
3
|
-
while (ancestor) {
|
|
4
|
-
if (predicate(ancestor)) {
|
|
5
|
-
return ancestor;
|
|
6
|
-
}
|
|
7
|
-
ancestor = ancestor.parentNode;
|
|
8
|
-
}
|
|
9
|
-
return null;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const findDescendant = (rootNode, fn, { skipRoot } = {}) => {
|
|
13
|
-
const iterator = createNextNodeIterator(rootNode, rootNode, skipRoot);
|
|
14
|
-
let { done, value: node } = iterator.next();
|
|
15
|
-
while (done === false) {
|
|
16
|
-
let skipChildren = false;
|
|
17
|
-
if (node === skipRoot) {
|
|
18
|
-
skipChildren = true;
|
|
19
|
-
} else {
|
|
20
|
-
const skip = () => {
|
|
21
|
-
skipChildren = true;
|
|
22
|
-
};
|
|
23
|
-
if (fn(node, skip)) {
|
|
24
|
-
return node;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
({ done, value: node } = iterator.next(skipChildren));
|
|
28
|
-
}
|
|
29
|
-
return null;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export const findLastDescendant = (rootNode, fn, { skipRoot } = {}) => {
|
|
33
|
-
const deepestNode = getDeepestNode(rootNode, skipRoot);
|
|
34
|
-
if (deepestNode) {
|
|
35
|
-
const iterator = createPreviousNodeIterator(
|
|
36
|
-
deepestNode,
|
|
37
|
-
rootNode,
|
|
38
|
-
skipRoot,
|
|
39
|
-
);
|
|
40
|
-
let { done, value: node } = iterator.next();
|
|
41
|
-
while (done === false) {
|
|
42
|
-
if (fn(node)) {
|
|
43
|
-
return node;
|
|
44
|
-
}
|
|
45
|
-
({ done, value: node } = iterator.next());
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return null;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export const findAfter = (
|
|
52
|
-
from,
|
|
53
|
-
predicate,
|
|
54
|
-
{ root = null, skipRoot = null, skipChildren = false } = {},
|
|
55
|
-
) => {
|
|
56
|
-
const iterator = createAfterNodeIterator(from, root, skipChildren, skipRoot);
|
|
57
|
-
let { done, value: node } = iterator.next();
|
|
58
|
-
while (done === false) {
|
|
59
|
-
if (predicate(node)) {
|
|
60
|
-
return node;
|
|
61
|
-
}
|
|
62
|
-
({ done, value: node } = iterator.next());
|
|
63
|
-
}
|
|
64
|
-
return null;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export const findBefore = (
|
|
68
|
-
from,
|
|
69
|
-
predicate,
|
|
70
|
-
{ root = null, skipRoot = null } = {},
|
|
71
|
-
) => {
|
|
72
|
-
const iterator = createPreviousNodeIterator(from, root, skipRoot);
|
|
73
|
-
let { done, value: node } = iterator.next();
|
|
74
|
-
while (done === false) {
|
|
75
|
-
if (predicate(node)) {
|
|
76
|
-
return node;
|
|
77
|
-
}
|
|
78
|
-
({ done, value: node } = iterator.next());
|
|
79
|
-
}
|
|
80
|
-
return null;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const getNextNode = (node, rootNode, skipChild = false, skipRoot = null) => {
|
|
84
|
-
if (!skipChild) {
|
|
85
|
-
const firstChild = node.firstChild;
|
|
86
|
-
if (firstChild) {
|
|
87
|
-
// If the first child is skipRoot or inside skipRoot, skip it
|
|
88
|
-
if (
|
|
89
|
-
skipRoot &&
|
|
90
|
-
(firstChild === skipRoot || skipRoot.contains(firstChild))
|
|
91
|
-
) {
|
|
92
|
-
// Skip this entire subtree by going to next sibling or up
|
|
93
|
-
return getNextNode(node, rootNode, true, skipRoot);
|
|
94
|
-
}
|
|
95
|
-
return firstChild;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const nextSibling = node.nextSibling;
|
|
100
|
-
if (nextSibling) {
|
|
101
|
-
// If next sibling is skipRoot, skip it entirely
|
|
102
|
-
if (skipRoot && nextSibling === skipRoot) {
|
|
103
|
-
return getNextNode(nextSibling, rootNode, true, skipRoot);
|
|
104
|
-
}
|
|
105
|
-
return nextSibling;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const parentNode = node.parentNode;
|
|
109
|
-
if (parentNode && parentNode !== rootNode) {
|
|
110
|
-
return getNextNode(parentNode, rootNode, true, skipRoot);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return null;
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const createNextNodeIterator = (node, rootNode, skipRoot = null) => {
|
|
117
|
-
let current = node;
|
|
118
|
-
const next = (innerSkipChildren = false) => {
|
|
119
|
-
const nextNode = getNextNode(
|
|
120
|
-
current,
|
|
121
|
-
rootNode,
|
|
122
|
-
innerSkipChildren,
|
|
123
|
-
skipRoot,
|
|
124
|
-
);
|
|
125
|
-
current = nextNode;
|
|
126
|
-
return {
|
|
127
|
-
done: Boolean(nextNode) === false,
|
|
128
|
-
value: nextNode,
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
return { next };
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const createAfterNodeIterator = (
|
|
135
|
-
fromNode,
|
|
136
|
-
rootNode,
|
|
137
|
-
skipChildren = false,
|
|
138
|
-
skipRoot = null,
|
|
139
|
-
) => {
|
|
140
|
-
let current = fromNode;
|
|
141
|
-
let childrenSkipped = false;
|
|
142
|
-
|
|
143
|
-
// If we're inside skipRoot, we need to start searching after skipRoot entirely
|
|
144
|
-
if (skipRoot && (fromNode === skipRoot || skipRoot.contains(fromNode))) {
|
|
145
|
-
current = skipRoot;
|
|
146
|
-
childrenSkipped = true; // Mark that we've already "processed" this node
|
|
147
|
-
skipChildren = true; // Force skip children to exit the skipRoot subtree
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const next = (innerSkipChildren = false) => {
|
|
151
|
-
const nextNode = getNextNode(
|
|
152
|
-
current,
|
|
153
|
-
rootNode,
|
|
154
|
-
(skipChildren && childrenSkipped === false) || innerSkipChildren,
|
|
155
|
-
skipRoot,
|
|
156
|
-
);
|
|
157
|
-
childrenSkipped = true;
|
|
158
|
-
current = nextNode;
|
|
159
|
-
return {
|
|
160
|
-
done: Boolean(nextNode) === false,
|
|
161
|
-
value: nextNode,
|
|
162
|
-
};
|
|
163
|
-
};
|
|
164
|
-
return { next };
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const getDeepestNode = (node, skipRoot = null) => {
|
|
168
|
-
let deepestNode = node.lastChild;
|
|
169
|
-
while (deepestNode) {
|
|
170
|
-
// If we hit skipRoot or enter its subtree, stop going deeper
|
|
171
|
-
if (
|
|
172
|
-
skipRoot &&
|
|
173
|
-
(deepestNode === skipRoot || skipRoot.contains(deepestNode))
|
|
174
|
-
) {
|
|
175
|
-
// Try the previous sibling instead
|
|
176
|
-
const previousSibling = deepestNode.previousSibling;
|
|
177
|
-
if (previousSibling) {
|
|
178
|
-
return getDeepestNode(previousSibling, skipRoot);
|
|
179
|
-
}
|
|
180
|
-
// If no previous sibling, return the parent (which should be safe)
|
|
181
|
-
return deepestNode.parentNode === node ? null : deepestNode.parentNode;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const lastChild = deepestNode.lastChild;
|
|
185
|
-
if (lastChild) {
|
|
186
|
-
deepestNode = lastChild;
|
|
187
|
-
} else {
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return deepestNode;
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
const getPreviousNode = (node, rootNode, skipRoot = null) => {
|
|
195
|
-
const previousSibling = node.previousSibling;
|
|
196
|
-
if (previousSibling) {
|
|
197
|
-
// If previous sibling is skipRoot, skip it entirely
|
|
198
|
-
if (skipRoot && previousSibling === skipRoot) {
|
|
199
|
-
return getPreviousNode(previousSibling, rootNode, skipRoot);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const deepestChild = getDeepestNode(previousSibling, skipRoot);
|
|
203
|
-
|
|
204
|
-
// Check if deepest child is inside skipRoot (shouldn't happen with updated getDeepestNode, but safe check)
|
|
205
|
-
if (
|
|
206
|
-
skipRoot &&
|
|
207
|
-
deepestChild &&
|
|
208
|
-
(deepestChild === skipRoot || skipRoot.contains(deepestChild))
|
|
209
|
-
) {
|
|
210
|
-
// Skip this sibling entirely and try the next one
|
|
211
|
-
return getPreviousNode(previousSibling, rootNode, skipRoot);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (deepestChild) {
|
|
215
|
-
return deepestChild;
|
|
216
|
-
}
|
|
217
|
-
return previousSibling;
|
|
218
|
-
}
|
|
219
|
-
if (node !== rootNode) {
|
|
220
|
-
const parentNode = node.parentNode;
|
|
221
|
-
if (parentNode && parentNode !== rootNode) {
|
|
222
|
-
return parentNode;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
return null;
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const createPreviousNodeIterator = (fromNode, rootNode, skipRoot = null) => {
|
|
229
|
-
let current = fromNode;
|
|
230
|
-
|
|
231
|
-
// If we're inside skipRoot, we need to start searching before skipRoot entirely
|
|
232
|
-
if (skipRoot && (fromNode === skipRoot || skipRoot.contains(fromNode))) {
|
|
233
|
-
current = skipRoot;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const next = () => {
|
|
237
|
-
const previousNode = getPreviousNode(current, rootNode, skipRoot);
|
|
238
|
-
current = previousNode;
|
|
239
|
-
return {
|
|
240
|
-
done: Boolean(previousNode) === false,
|
|
241
|
-
value: previousNode,
|
|
242
|
-
};
|
|
243
|
-
};
|
|
244
|
-
return {
|
|
245
|
-
next,
|
|
246
|
-
};
|
|
247
|
-
};
|