@jbpark/use-hooks 2.11.0 → 4.0.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/README.ko.md +40 -22
- package/README.md +40 -22
- package/dist/hooks/index.d.mts +14 -3
- package/dist/hooks/index.mjs +14 -3
- package/dist/hooks/use-click-outside.d.mts +9 -2
- package/dist/hooks/use-click-outside.mjs +27 -11
- package/dist/hooks/use-click-outside.mjs.map +1 -1
- package/dist/hooks/use-debounced-callback.d.mts +14 -0
- package/dist/hooks/{use-debounce.mjs → use-debounced-callback.mjs} +4 -4
- package/dist/hooks/use-debounced-callback.mjs.map +1 -0
- package/dist/hooks/use-debounced-value.d.mts +5 -0
- package/dist/hooks/use-debounced-value.mjs +20 -0
- package/dist/hooks/use-debounced-value.mjs.map +1 -0
- package/dist/hooks/use-event-listener.d.mts +21 -0
- package/dist/hooks/use-event-listener.mjs +50 -0
- package/dist/hooks/use-event-listener.mjs.map +1 -0
- package/dist/hooks/use-file-drop.d.mts +14 -0
- package/dist/hooks/use-file-drop.mjs +85 -0
- package/dist/hooks/use-file-drop.mjs.map +1 -0
- package/dist/hooks/use-image.d.mts +2 -1
- package/dist/hooks/use-image.mjs +4 -3
- package/dist/hooks/use-image.mjs.map +1 -1
- package/dist/hooks/use-intersection-observer.d.mts +8 -1
- package/dist/hooks/use-intersection-observer.mjs +37 -5
- package/dist/hooks/use-intersection-observer.mjs.map +1 -1
- package/dist/hooks/use-interval.d.mts +5 -0
- package/dist/hooks/use-interval.mjs +20 -0
- package/dist/hooks/use-interval.mjs.map +1 -0
- package/dist/hooks/use-key-press.d.mts +14 -0
- package/dist/hooks/use-key-press.mjs +85 -0
- package/dist/hooks/use-key-press.mjs.map +1 -0
- package/dist/hooks/use-merged-ref.d.mts +8 -0
- package/dist/hooks/use-merged-ref.mjs +31 -0
- package/dist/hooks/use-merged-ref.mjs.map +1 -0
- package/dist/hooks/use-mutation-observer.d.mts +11 -0
- package/dist/hooks/use-mutation-observer.mjs +47 -0
- package/dist/hooks/use-mutation-observer.mjs.map +1 -0
- package/dist/hooks/use-previous.d.mts +5 -0
- package/dist/hooks/use-previous.mjs +14 -0
- package/dist/hooks/use-previous.mjs.map +1 -0
- package/dist/hooks/use-resize-observer.d.mts +12 -0
- package/dist/hooks/use-resize-observer.mjs +53 -0
- package/dist/hooks/use-resize-observer.mjs.map +1 -0
- package/dist/hooks/use-responsive-size.mjs +2 -2
- package/dist/hooks/use-responsive-size.mjs.map +1 -1
- package/dist/hooks/use-scroll-to-elements.d.mts +7 -5
- package/dist/hooks/use-scroll-to-elements.mjs +52 -27
- package/dist/hooks/use-scroll-to-elements.mjs.map +1 -1
- package/dist/hooks/use-throttled-callback.d.mts +12 -0
- package/dist/hooks/use-throttled-callback.mjs +54 -0
- package/dist/hooks/use-throttled-callback.mjs.map +1 -0
- package/dist/hooks/use-throttled-value.d.mts +9 -0
- package/dist/hooks/use-throttled-value.mjs +16 -0
- package/dist/hooks/use-throttled-value.mjs.map +1 -0
- package/dist/hooks/use-timeout.d.mts +8 -0
- package/dist/hooks/use-timeout.mjs +38 -0
- package/dist/hooks/use-timeout.mjs.map +1 -0
- package/dist/hooks/use-toggle.d.mts +5 -0
- package/dist/hooks/use-toggle.mjs +17 -0
- package/dist/hooks/use-toggle.mjs.map +1 -0
- package/dist/index.d.mts +15 -4
- package/dist/index.mjs +15 -4
- package/package.json +1 -1
- package/dist/hooks/use-debounce.d.mts +0 -14
- package/dist/hooks/use-debounce.mjs.map +0 -1
- package/dist/hooks/use-throttle.d.mts +0 -5
- package/dist/hooks/use-throttle.mjs +0 -42
- package/dist/hooks/use-throttle.mjs.map +0 -1
- package/dist/hooks/use-timeline.d.mts +0 -50
- package/dist/hooks/use-timeline.mjs +0 -211
- package/dist/hooks/use-timeline.mjs.map +0 -1
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
|
|
3
|
-
//#region src/hooks/use-timeline.ts
|
|
4
|
-
const registeredProperties = /* @__PURE__ */ new Set();
|
|
5
|
-
const registerCSSProperty = (name, syntax = "<length>", initialValue = "0px") => {
|
|
6
|
-
if (registeredProperties.has(name) || typeof CSS === "undefined" || !CSS.registerProperty) return;
|
|
7
|
-
try {
|
|
8
|
-
CSS.registerProperty({
|
|
9
|
-
name,
|
|
10
|
-
syntax,
|
|
11
|
-
inherits: false,
|
|
12
|
-
initialValue
|
|
13
|
-
});
|
|
14
|
-
registeredProperties.add(name);
|
|
15
|
-
} catch {}
|
|
16
|
-
};
|
|
17
|
-
const buildTimeline = (steps) => {
|
|
18
|
-
const groups = [];
|
|
19
|
-
for (const step of steps) if ((step.transition.delay ?? 0) === 0 && groups.length > 0) groups[groups.length - 1]?.push(step);
|
|
20
|
-
else groups.push([step]);
|
|
21
|
-
return groups;
|
|
22
|
-
};
|
|
23
|
-
const buildTransform = (step) => {
|
|
24
|
-
const parts = [];
|
|
25
|
-
if (step.position?.x != null || step.position?.y != null) parts.push(`translate(${step.position.x ?? 0}px, ${step.position.y ?? 0}px)`);
|
|
26
|
-
if (step.scale != null) parts.push(`scale(${step.scale})`);
|
|
27
|
-
if (step.rotate?.x != null) parts.push(`rotateX(${step.rotate.x}deg)`);
|
|
28
|
-
if (step.rotate?.y != null) parts.push(`rotateY(${step.rotate.y}deg)`);
|
|
29
|
-
if (step.rotate?.z != null) parts.push(`rotateZ(${step.rotate.z}deg)`);
|
|
30
|
-
return parts.join(" ") || void 0;
|
|
31
|
-
};
|
|
32
|
-
const applyStep = (container, step, skipTransition) => {
|
|
33
|
-
const elements = container.matches(step.selector) ? [container] : Array.from(container.querySelectorAll(step.selector));
|
|
34
|
-
if (!elements.length) return;
|
|
35
|
-
const dur = `${step.transition.duration}ms`;
|
|
36
|
-
const eas = step.transition.ease ?? "ease-out";
|
|
37
|
-
const easX = step.transition.easeX;
|
|
38
|
-
const easY = step.transition.easeY;
|
|
39
|
-
const hasCurve = !!(easX || easY);
|
|
40
|
-
const timers = [];
|
|
41
|
-
elements.forEach((el) => {
|
|
42
|
-
if (skipTransition) el.style.transition = "none";
|
|
43
|
-
else if (hasCurve) {
|
|
44
|
-
const xEase = easX ?? eas;
|
|
45
|
-
const yEase = easY ?? eas;
|
|
46
|
-
el.style.transition = [
|
|
47
|
-
`--tx ${dur} ${xEase}`,
|
|
48
|
-
`--ty ${dur} ${yEase}`,
|
|
49
|
-
`--s ${dur} ${eas}`,
|
|
50
|
-
`rotate ${dur} ${eas}`,
|
|
51
|
-
`width ${dur} ${eas}`,
|
|
52
|
-
`height ${dur} ${eas}`,
|
|
53
|
-
`filter ${dur} ${eas}`,
|
|
54
|
-
`opacity ${dur} ${eas}`
|
|
55
|
-
].join(", ");
|
|
56
|
-
} else el.style.transition = `all ${dur} ${eas}`;
|
|
57
|
-
if (step.backgroundColor) el.style.backgroundColor = step.backgroundColor;
|
|
58
|
-
if (step.opacity !== void 0) el.style.opacity = String(step.opacity);
|
|
59
|
-
if (step.top !== void 0) el.style.top = `${step.top}px`;
|
|
60
|
-
if (step.left !== void 0) el.style.left = `${step.left}px`;
|
|
61
|
-
if (step.width !== void 0) el.style.width = `${step.width}px`;
|
|
62
|
-
if (step.height !== void 0) el.style.height = `${step.height}px`;
|
|
63
|
-
if (step.zIndex !== void 0) if (skipTransition) el.style.zIndex = String(step.zIndex);
|
|
64
|
-
else {
|
|
65
|
-
const timerId = setTimeout(() => {
|
|
66
|
-
el.style.zIndex = String(step.zIndex);
|
|
67
|
-
}, step.transition.duration / 2);
|
|
68
|
-
timers.push(timerId);
|
|
69
|
-
}
|
|
70
|
-
if (step.filter !== void 0) el.style.filter = step.filter;
|
|
71
|
-
if (step.rotate?.z !== void 0) el.style.rotate = `${step.rotate.z}deg`;
|
|
72
|
-
if (hasCurve) {
|
|
73
|
-
if (step.position?.x != null || step.position?.y != null) {
|
|
74
|
-
el.style.setProperty("--tx", `${step.position.x ?? 0}px`);
|
|
75
|
-
el.style.setProperty("--ty", `${step.position.y ?? 0}px`);
|
|
76
|
-
}
|
|
77
|
-
if (step.scale != null) el.style.setProperty("--s", String(step.scale));
|
|
78
|
-
el.style.transform = `translate(var(--tx), var(--ty)) scale(var(--s, 1))`;
|
|
79
|
-
} else {
|
|
80
|
-
const transform = buildTransform(step);
|
|
81
|
-
if (transform) el.style.transform = transform;
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
return timers;
|
|
85
|
-
};
|
|
86
|
-
const resetAppliedStyles = (container, steps) => {
|
|
87
|
-
new Set(steps.map((step) => step.selector)).forEach((selector) => {
|
|
88
|
-
(container.matches(selector) ? [container] : Array.from(container.querySelectorAll(selector))).forEach((el) => {
|
|
89
|
-
el.style.transition = "";
|
|
90
|
-
el.style.backgroundColor = "";
|
|
91
|
-
el.style.opacity = "";
|
|
92
|
-
el.style.top = "";
|
|
93
|
-
el.style.left = "";
|
|
94
|
-
el.style.width = "";
|
|
95
|
-
el.style.height = "";
|
|
96
|
-
el.style.zIndex = "";
|
|
97
|
-
el.style.filter = "";
|
|
98
|
-
el.style.rotate = "";
|
|
99
|
-
el.style.transform = "";
|
|
100
|
-
el.style.removeProperty("--tx");
|
|
101
|
-
el.style.removeProperty("--ty");
|
|
102
|
-
el.style.removeProperty("--s");
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
};
|
|
106
|
-
const useTimeline = ({ steps = [], loading, immediate, loop }) => {
|
|
107
|
-
const [slotIndex, setSlotIndex] = useState(-1);
|
|
108
|
-
const [completed, setCompleted] = useState(() => !!immediate);
|
|
109
|
-
const ref = useRef(null);
|
|
110
|
-
const stepsRef = useRef(steps);
|
|
111
|
-
useEffect(() => {
|
|
112
|
-
stepsRef.current = steps;
|
|
113
|
-
});
|
|
114
|
-
const stepsKey = useMemo(() => JSON.stringify(steps), [steps]);
|
|
115
|
-
const timeline = useMemo(() => buildTimeline(steps), [stepsKey]);
|
|
116
|
-
const hasCurveSteps = useMemo(() => steps.some((s) => s.transition.easeX || s.transition.easeY), [stepsKey]);
|
|
117
|
-
useEffect(() => {
|
|
118
|
-
setSlotIndex(-1);
|
|
119
|
-
setCompleted(!!immediate);
|
|
120
|
-
}, [stepsKey, loading]);
|
|
121
|
-
useLayoutEffect(() => {
|
|
122
|
-
if (hasCurveSteps) {
|
|
123
|
-
registerCSSProperty("--tx");
|
|
124
|
-
registerCSSProperty("--ty");
|
|
125
|
-
registerCSSProperty("--s", "<number>", "1");
|
|
126
|
-
}
|
|
127
|
-
}, [hasCurveSteps]);
|
|
128
|
-
useLayoutEffect(() => {
|
|
129
|
-
if (!immediate || !ref.current || !steps.length) return;
|
|
130
|
-
const container = ref.current;
|
|
131
|
-
steps.forEach((step) => applyStep(container, step, true));
|
|
132
|
-
}, [immediate, stepsKey]);
|
|
133
|
-
useEffect(() => {
|
|
134
|
-
const container = ref.current;
|
|
135
|
-
return () => {
|
|
136
|
-
if (container) resetAppliedStyles(container, stepsRef.current);
|
|
137
|
-
};
|
|
138
|
-
}, []);
|
|
139
|
-
useEffect(() => {
|
|
140
|
-
if (immediate || loading || !timeline.length) return;
|
|
141
|
-
const firstDelay = timeline[0]?.[0]?.transition.delay ?? 0;
|
|
142
|
-
const timerId = window.setTimeout(() => {
|
|
143
|
-
requestAnimationFrame(() => setSlotIndex(0));
|
|
144
|
-
}, firstDelay);
|
|
145
|
-
return () => clearTimeout(timerId);
|
|
146
|
-
}, [
|
|
147
|
-
loading,
|
|
148
|
-
timeline,
|
|
149
|
-
immediate
|
|
150
|
-
]);
|
|
151
|
-
useEffect(() => {
|
|
152
|
-
if (immediate || loading || slotIndex < 0) return;
|
|
153
|
-
const isLast = slotIndex >= timeline.length - 1;
|
|
154
|
-
if (isLast && !loop) return;
|
|
155
|
-
const currentSlot = timeline[slotIndex];
|
|
156
|
-
const maxDuration = Math.max(...currentSlot?.map((s) => s.transition.duration) ?? [0]);
|
|
157
|
-
const nextIndex = isLast ? 0 : slotIndex + 1;
|
|
158
|
-
const nextDelay = timeline[nextIndex]?.[0]?.transition.delay ?? 0;
|
|
159
|
-
const timerId = window.setTimeout(() => {
|
|
160
|
-
setSlotIndex(nextIndex);
|
|
161
|
-
}, maxDuration + nextDelay);
|
|
162
|
-
return () => clearTimeout(timerId);
|
|
163
|
-
}, [
|
|
164
|
-
loading,
|
|
165
|
-
slotIndex,
|
|
166
|
-
timeline,
|
|
167
|
-
immediate,
|
|
168
|
-
loop
|
|
169
|
-
]);
|
|
170
|
-
useEffect(() => {
|
|
171
|
-
if (immediate || slotIndex < 0 || !ref.current) return;
|
|
172
|
-
const container = ref.current;
|
|
173
|
-
const timers = [];
|
|
174
|
-
const rafId = requestAnimationFrame(() => {
|
|
175
|
-
timeline[slotIndex]?.forEach((step) => {
|
|
176
|
-
const stepTimers = applyStep(container, step);
|
|
177
|
-
if (stepTimers) timers.push(...stepTimers);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
return () => {
|
|
181
|
-
cancelAnimationFrame(rafId);
|
|
182
|
-
timers.forEach(clearTimeout);
|
|
183
|
-
};
|
|
184
|
-
}, [
|
|
185
|
-
immediate,
|
|
186
|
-
slotIndex,
|
|
187
|
-
timeline
|
|
188
|
-
]);
|
|
189
|
-
useEffect(() => {
|
|
190
|
-
if (immediate || loop || slotIndex < 0 || slotIndex < timeline.length - 1 || !timeline.length) return;
|
|
191
|
-
const lastSlot = timeline[slotIndex];
|
|
192
|
-
const maxDuration = Math.max(...lastSlot?.map((s) => s.transition.duration) ?? [0]);
|
|
193
|
-
const timerId = window.setTimeout(() => {
|
|
194
|
-
setCompleted(true);
|
|
195
|
-
}, maxDuration);
|
|
196
|
-
return () => clearTimeout(timerId);
|
|
197
|
-
}, [
|
|
198
|
-
slotIndex,
|
|
199
|
-
timeline,
|
|
200
|
-
immediate,
|
|
201
|
-
loop
|
|
202
|
-
]);
|
|
203
|
-
return {
|
|
204
|
-
ref,
|
|
205
|
-
completed
|
|
206
|
-
};
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
//#endregion
|
|
210
|
-
export { useTimeline as default };
|
|
211
|
-
//# sourceMappingURL=use-timeline.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-timeline.mjs","names":[],"sources":["../../src/hooks/use-timeline.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\n\nconst registeredProperties = new Set<string>();\n\n// `CSS.registerProperty` is permanent for the page's lifetime — there is\n// no unregisterProperty API, so once a curved-easing step registers\n// `--tx`/`--ty`/`--s`, they stay registered globally even after every\n// `useTimeline` instance using them has unmounted.\nconst registerCSSProperty = (\n name: string,\n syntax = '<length>',\n initialValue = '0px',\n) => {\n if (\n registeredProperties.has(name) ||\n typeof CSS === 'undefined' ||\n !CSS.registerProperty\n ) {\n return;\n }\n\n try {\n CSS.registerProperty({\n name,\n syntax,\n inherits: false,\n initialValue,\n });\n registeredProperties.add(name);\n } catch {\n // 이미 등록된 경우 무시\n }\n};\n\nexport interface TimelineStep {\n scale?: number;\n rotate?: { x?: number; y?: number; z?: number };\n position?: { x?: number; y?: number };\n top?: number;\n left?: number;\n width?: number;\n height?: number;\n zIndex?: number;\n filter?: string;\n backgroundColor?: string;\n opacity?: number;\n selector: string;\n transition: Transition;\n}\n\ninterface Transition {\n duration: number;\n ease?: string;\n delay?: number;\n easeX?: string;\n easeY?: string;\n}\n\ninterface Options {\n steps?: TimelineStep[];\n loading?: boolean;\n immediate?: boolean;\n loop?: boolean;\n}\n\nconst buildTimeline = (steps: TimelineStep[]): TimelineStep[][] => {\n const groups: TimelineStep[][] = [];\n\n for (const step of steps) {\n // An unspecified delay means \"no delay\", same as an explicit 0 — both\n // merge into the previous group instead of only the explicit case.\n if ((step.transition.delay ?? 0) === 0 && groups.length > 0) {\n groups[groups.length - 1]?.push(step);\n } else {\n groups.push([step]);\n }\n }\n\n return groups;\n};\n\nconst buildTransform = (step: TimelineStep) => {\n const parts: string[] = [];\n\n if (step.position?.x != null || step.position?.y != null) {\n parts.push(\n `translate(${step.position.x ?? 0}px, ${step.position.y ?? 0}px)`,\n );\n }\n\n if (step.scale != null) {\n parts.push(`scale(${step.scale})`);\n }\n\n if (step.rotate?.x != null) {\n parts.push(`rotateX(${step.rotate.x}deg)`);\n }\n\n if (step.rotate?.y != null) {\n parts.push(`rotateY(${step.rotate.y}deg)`);\n }\n\n if (step.rotate?.z != null) {\n parts.push(`rotateZ(${step.rotate.z}deg)`);\n }\n\n return parts.join(' ') || undefined;\n};\n\nconst applyStep = (\n container: HTMLElement,\n step: TimelineStep,\n skipTransition?: boolean,\n) => {\n const elements = container.matches(step.selector)\n ? [container]\n : Array.from(container.querySelectorAll<HTMLElement>(step.selector));\n\n if (!elements.length) {\n return;\n }\n\n const dur = `${step.transition.duration}ms`;\n const eas = step.transition.ease ?? 'ease-out';\n const easX = step.transition.easeX;\n const easY = step.transition.easeY;\n const hasCurve = !!(easX || easY);\n const timers: ReturnType<typeof setTimeout>[] = [];\n\n elements.forEach(el => {\n if (skipTransition) {\n el.style.transition = 'none';\n } else if (hasCurve) {\n const xEase = easX ?? eas;\n const yEase = easY ?? eas;\n el.style.transition = [\n `--tx ${dur} ${xEase}`,\n `--ty ${dur} ${yEase}`,\n `--s ${dur} ${eas}`,\n `rotate ${dur} ${eas}`,\n `width ${dur} ${eas}`,\n `height ${dur} ${eas}`,\n `filter ${dur} ${eas}`,\n `opacity ${dur} ${eas}`,\n ].join(', ');\n } else {\n el.style.transition = `all ${dur} ${eas}`;\n }\n\n if (step.backgroundColor) {\n el.style.backgroundColor = step.backgroundColor;\n }\n\n if (step.opacity !== undefined) {\n el.style.opacity = String(step.opacity);\n }\n\n if (step.top !== undefined) {\n el.style.top = `${step.top}px`;\n }\n\n if (step.left !== undefined) {\n el.style.left = `${step.left}px`;\n }\n\n if (step.width !== undefined) {\n el.style.width = `${step.width}px`;\n }\n\n if (step.height !== undefined) {\n el.style.height = `${step.height}px`;\n }\n\n if (step.zIndex !== undefined) {\n if (skipTransition) {\n el.style.zIndex = String(step.zIndex);\n } else {\n const timerId = setTimeout(() => {\n el.style.zIndex = String(step.zIndex);\n }, step.transition.duration / 2);\n timers.push(timerId);\n }\n }\n\n if (step.filter !== undefined) {\n el.style.filter = step.filter;\n }\n\n if (step.rotate?.z !== undefined) {\n el.style.rotate = `${step.rotate.z}deg`;\n }\n\n if (hasCurve) {\n if (step.position?.x != null || step.position?.y != null) {\n el.style.setProperty('--tx', `${step.position.x ?? 0}px`);\n el.style.setProperty('--ty', `${step.position.y ?? 0}px`);\n }\n if (step.scale != null) {\n el.style.setProperty('--s', String(step.scale));\n }\n el.style.transform = `translate(var(--tx), var(--ty)) scale(var(--s, 1))`;\n } else {\n const transform = buildTransform(step);\n\n if (transform) {\n el.style.transform = transform;\n }\n }\n });\n\n return timers;\n};\n\n// Clears every inline style property `applyStep` can set, on every element\n// matching any of `steps`' selectors, so nothing this hook applied lingers\n// on elements that outlive it (e.g. kept mounted by a parent).\nconst resetAppliedStyles = (container: HTMLElement, steps: TimelineStep[]) => {\n const selectors = new Set(steps.map(step => step.selector));\n\n selectors.forEach(selector => {\n const elements = container.matches(selector)\n ? [container]\n : Array.from(container.querySelectorAll<HTMLElement>(selector));\n\n elements.forEach(el => {\n el.style.transition = '';\n el.style.backgroundColor = '';\n el.style.opacity = '';\n el.style.top = '';\n el.style.left = '';\n el.style.width = '';\n el.style.height = '';\n el.style.zIndex = '';\n el.style.filter = '';\n el.style.rotate = '';\n el.style.transform = '';\n el.style.removeProperty('--tx');\n el.style.removeProperty('--ty');\n el.style.removeProperty('--s');\n });\n });\n};\n\nconst useTimeline = ({ steps = [], loading, immediate, loop }: Options) => {\n const [slotIndex, setSlotIndex] = useState(-1);\n const [completed, setCompleted] = useState(() => !!immediate);\n\n const ref = useRef<HTMLDivElement>(null);\n const stepsRef = useRef(steps);\n\n useEffect(() => {\n stepsRef.current = steps;\n });\n\n // `steps` is naturally passed as an inline array literal at most call\n // sites, so a new reference every render would otherwise rebuild\n // `timeline` — clearing every running timer — on every unrelated\n // re-render. Key derived values off a serialized snapshot instead,\n // which only changes when the content actually does.\n const stepsKey = useMemo(() => JSON.stringify(steps), [steps]);\n const timeline = useMemo(\n () => buildTimeline(steps),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [stepsKey],\n );\n const hasCurveSteps = useMemo(\n () => steps.some(s => s.transition.easeX || s.transition.easeY),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [stepsKey],\n );\n\n // Restart playback from the top whenever the steps' content (not just\n // their reference) or `loading` changes — otherwise slotIndex/completed\n // stay wherever they were left on the previous timeline.\n useEffect(() => {\n setSlotIndex(-1);\n setCompleted(!!immediate);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [stepsKey, loading]);\n\n useLayoutEffect(() => {\n if (hasCurveSteps) {\n registerCSSProperty('--tx');\n registerCSSProperty('--ty');\n registerCSSProperty('--s', '<number>', '1');\n }\n }, [hasCurveSteps]);\n\n useLayoutEffect(() => {\n if (!immediate || !ref.current || !steps.length) {\n return;\n }\n\n const container = ref.current;\n steps.forEach(step => applyStep(container, step, true));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [immediate, stepsKey]);\n\n useEffect(() => {\n const container = ref.current;\n\n return () => {\n if (container) {\n resetAppliedStyles(container, stepsRef.current);\n }\n };\n }, []);\n\n useEffect(() => {\n if (immediate || loading || !timeline.length) {\n return;\n }\n\n const firstDelay = timeline[0]?.[0]?.transition.delay ?? 0;\n const timerId = window.setTimeout(() => {\n requestAnimationFrame(() => setSlotIndex(0));\n }, firstDelay);\n\n return () => clearTimeout(timerId);\n }, [loading, timeline, immediate]);\n\n useEffect(() => {\n if (immediate || loading || slotIndex < 0) {\n return;\n }\n\n const isLast = slotIndex >= timeline.length - 1;\n\n if (isLast && !loop) {\n return;\n }\n\n const currentSlot = timeline[slotIndex];\n const maxDuration = Math.max(\n ...(currentSlot?.map(s => s.transition.duration) ?? [0]),\n );\n\n const nextIndex = isLast ? 0 : slotIndex + 1;\n const nextDelay = timeline[nextIndex]?.[0]?.transition.delay ?? 0;\n\n const timerId = window.setTimeout(() => {\n setSlotIndex(nextIndex);\n }, maxDuration + nextDelay);\n\n return () => clearTimeout(timerId);\n }, [loading, slotIndex, timeline, immediate, loop]);\n\n useEffect(() => {\n if (immediate || slotIndex < 0 || !ref.current) {\n return;\n }\n\n const container = ref.current;\n const timers: ReturnType<typeof setTimeout>[] = [];\n\n const rafId = requestAnimationFrame(() => {\n timeline[slotIndex]?.forEach(step => {\n const stepTimers = applyStep(container, step);\n\n if (stepTimers) {\n timers.push(...stepTimers);\n }\n });\n });\n\n return () => {\n cancelAnimationFrame(rafId);\n timers.forEach(clearTimeout);\n };\n }, [immediate, slotIndex, timeline]);\n\n useEffect(() => {\n if (\n immediate ||\n loop ||\n slotIndex < 0 ||\n slotIndex < timeline.length - 1 ||\n !timeline.length\n ) {\n return;\n }\n\n const lastSlot = timeline[slotIndex];\n const maxDuration = Math.max(\n ...(lastSlot?.map(s => s.transition.duration) ?? [0]),\n );\n\n const timerId = window.setTimeout(() => {\n setCompleted(true);\n }, maxDuration);\n\n return () => clearTimeout(timerId);\n }, [slotIndex, timeline, immediate, loop]);\n\n return { ref, completed };\n};\n\nexport default useTimeline;\n"],"mappings":"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"}
|