@jbpark/use-hooks 2.1.0 → 2.2.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.
@@ -8,5 +8,6 @@ import { useLocalStorage } from "./useLocalStorage/index.mjs";
8
8
  import { useRecursiveTimeout } from "./useRecursiveTimeout/index.mjs";
9
9
  import { useScrollToElements } from "./useScrollToElements/index.mjs";
10
10
  import { useThrottle } from "./useThrottle/index.mjs";
11
+ import { useTimeline } from "./useTimeline/index.mjs";
11
12
  import { useWindowScroll } from "./useWindowScroll/index.mjs";
12
13
  import { useViewport } from "./useViewport/index.mjs";
@@ -8,6 +8,7 @@ import useLocalStorage from "./useLocalStorage/index.mjs";
8
8
  import useRecursiveTimeout from "./useRecursiveTimeout/index.mjs";
9
9
  import useScrollToElements from "./useScrollToElements/index.mjs";
10
10
  import useThrottle from "./useThrottle/index.mjs";
11
+ import useTimeline from "./useTimeline/index.mjs";
11
12
  import useWindowScroll from "./useWindowScroll/index.mjs";
12
13
  import useViewport from "./useViewport/index.mjs";
13
14
 
@@ -0,0 +1,50 @@
1
+ import * as react from "react";
2
+
3
+ //#region src/hooks/useTimeline/index.d.ts
4
+ interface TimelineStep {
5
+ scale?: number;
6
+ rotate?: {
7
+ x?: number;
8
+ y?: number;
9
+ z?: number;
10
+ };
11
+ position?: {
12
+ x?: number;
13
+ y?: number;
14
+ };
15
+ top?: number;
16
+ left?: number;
17
+ width?: number;
18
+ height?: number;
19
+ zIndex?: number;
20
+ filter?: string;
21
+ backgroundColor?: string;
22
+ opacity?: number;
23
+ selector: string;
24
+ transition: Transition;
25
+ }
26
+ interface Transition {
27
+ duration: number;
28
+ ease?: string;
29
+ delay?: number;
30
+ easeX?: string;
31
+ easeY?: string;
32
+ }
33
+ interface Options {
34
+ steps?: TimelineStep[];
35
+ loading?: boolean;
36
+ immediate?: boolean;
37
+ loop?: boolean;
38
+ }
39
+ declare const useTimeline: ({
40
+ steps,
41
+ loading,
42
+ immediate,
43
+ loop
44
+ }: Options) => {
45
+ ref: react.RefObject<HTMLDivElement | null>;
46
+ completed: boolean;
47
+ };
48
+ //#endregion
49
+ export { useTimeline };
50
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,175 @@
1
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
2
+
3
+ //#region src/hooks/useTimeline/index.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 && 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 useTimeline = ({ steps = [], loading, immediate, loop }) => {
87
+ const [slotIndex, setSlotIndex] = useState(-1);
88
+ const [completed, setCompleted] = useState(() => !!immediate);
89
+ const ref = useRef(null);
90
+ const timeline = useMemo(() => buildTimeline(steps), [steps]);
91
+ const hasCurveSteps = useMemo(() => steps.some((s) => s.transition.easeX || s.transition.easeY), [steps]);
92
+ useLayoutEffect(() => {
93
+ if (hasCurveSteps) {
94
+ registerCSSProperty("--tx");
95
+ registerCSSProperty("--ty");
96
+ registerCSSProperty("--s", "<number>", "1");
97
+ }
98
+ }, [hasCurveSteps]);
99
+ useLayoutEffect(() => {
100
+ if (!immediate || !ref.current || !steps.length) return;
101
+ const container = ref.current;
102
+ steps.forEach((step) => applyStep(container, step, true));
103
+ }, [immediate, steps]);
104
+ useEffect(() => {
105
+ if (immediate || loading || !timeline.length) return;
106
+ const firstDelay = timeline[0]?.[0]?.transition.delay ?? 0;
107
+ const timerId = window.setTimeout(() => {
108
+ requestAnimationFrame(() => setSlotIndex(0));
109
+ }, firstDelay);
110
+ return () => clearTimeout(timerId);
111
+ }, [
112
+ loading,
113
+ timeline,
114
+ immediate
115
+ ]);
116
+ useEffect(() => {
117
+ if (immediate || loading || slotIndex < 0) return;
118
+ const isLast = slotIndex >= timeline.length - 1;
119
+ if (isLast && !loop) return;
120
+ const currentSlot = timeline[slotIndex];
121
+ const maxDuration = Math.max(...currentSlot?.map((s) => s.transition.duration) ?? [0]);
122
+ const nextIndex = isLast ? 0 : slotIndex + 1;
123
+ const nextDelay = timeline[nextIndex]?.[0]?.transition.delay ?? 0;
124
+ const timerId = window.setTimeout(() => {
125
+ setSlotIndex(nextIndex);
126
+ }, maxDuration + nextDelay);
127
+ return () => clearTimeout(timerId);
128
+ }, [
129
+ loading,
130
+ slotIndex,
131
+ timeline,
132
+ immediate,
133
+ loop
134
+ ]);
135
+ useEffect(() => {
136
+ if (immediate || slotIndex < 0 || !ref.current) return;
137
+ const container = ref.current;
138
+ const timers = [];
139
+ requestAnimationFrame(() => {
140
+ timeline[slotIndex]?.forEach((step) => {
141
+ const stepTimers = applyStep(container, step);
142
+ if (stepTimers) timers.push(...stepTimers);
143
+ });
144
+ });
145
+ return () => {
146
+ timers.forEach(clearTimeout);
147
+ };
148
+ }, [
149
+ immediate,
150
+ slotIndex,
151
+ timeline
152
+ ]);
153
+ useEffect(() => {
154
+ if (immediate || loop || slotIndex < 0 || slotIndex < timeline.length - 1 || !timeline.length) return;
155
+ const lastSlot = timeline[slotIndex];
156
+ const maxDuration = Math.max(...lastSlot?.map((s) => s.transition.duration) ?? [0]);
157
+ const timerId = window.setTimeout(() => {
158
+ setCompleted(true);
159
+ }, maxDuration);
160
+ return () => clearTimeout(timerId);
161
+ }, [
162
+ slotIndex,
163
+ timeline,
164
+ immediate,
165
+ loop
166
+ ]);
167
+ return {
168
+ ref,
169
+ completed
170
+ };
171
+ };
172
+
173
+ //#endregion
174
+ export { useTimeline as default };
175
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/useTimeline/index.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\n\nconst registeredProperties = new Set<string>();\n\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 if (step.transition.delay === 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\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 timeline = useMemo(() => buildTimeline(steps), [steps]);\n const hasCurveSteps = useMemo(\n () => steps.some(s => s.transition.easeX || s.transition.easeY),\n [steps],\n );\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 }, [immediate, steps]);\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 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 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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"}
package/dist/index.d.mts CHANGED
@@ -8,7 +8,8 @@ import { useLocalStorage } from "./hooks/useLocalStorage/index.mjs";
8
8
  import { useRecursiveTimeout } from "./hooks/useRecursiveTimeout/index.mjs";
9
9
  import { useScrollToElements } from "./hooks/useScrollToElements/index.mjs";
10
10
  import { useThrottle } from "./hooks/useThrottle/index.mjs";
11
+ import { useTimeline } from "./hooks/useTimeline/index.mjs";
11
12
  import { useWindowScroll } from "./hooks/useWindowScroll/index.mjs";
12
13
  import { useViewport } from "./hooks/useViewport/index.mjs";
13
14
  import "./hooks/index.mjs";
14
- export { useBodyScrollLock, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useViewport, useWindowScroll };
15
+ export { useBodyScrollLock, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
package/dist/index.mjs CHANGED
@@ -8,8 +8,9 @@ import useLocalStorage from "./hooks/useLocalStorage/index.mjs";
8
8
  import useRecursiveTimeout from "./hooks/useRecursiveTimeout/index.mjs";
9
9
  import useScrollToElements from "./hooks/useScrollToElements/index.mjs";
10
10
  import useThrottle from "./hooks/useThrottle/index.mjs";
11
+ import useTimeline from "./hooks/useTimeline/index.mjs";
11
12
  import useWindowScroll from "./hooks/useWindowScroll/index.mjs";
12
13
  import useViewport from "./hooks/useViewport/index.mjs";
13
14
  import "./hooks/index.mjs";
14
15
 
15
- export { useBodyScrollLock, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useViewport, useWindowScroll };
16
+ export { useBodyScrollLock, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbpark/use-hooks",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "A collection of reusable React 19 hooks for common UI and interaction patterns",
5
5
  "keywords": [
6
6
  "react",