@drivexstudio/animations 1.0.0 → 1.0.1
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.md +24 -0
- package/dist/index.cjs +1912 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +1856 -0
- package/dist/index.js.map +1 -0
- package/package.json +4 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,1856 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// packages/animations/ScrambleText.jsx
|
|
4
|
+
import React4, { useEffect as useEffect4, useRef as useRef3 } from "react";
|
|
5
|
+
|
|
6
|
+
// libs/vendor.js
|
|
7
|
+
import gsap from "gsap";
|
|
8
|
+
import { useGSAP } from "@gsap/react";
|
|
9
|
+
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
|
10
|
+
import { SplitText } from "gsap/SplitText";
|
|
11
|
+
import { ScrambleTextPlugin } from "gsap/ScrambleTextPlugin";
|
|
12
|
+
import { cx as classVarianceCx } from "class-variance-authority";
|
|
13
|
+
import { cva } from "class-variance-authority";
|
|
14
|
+
gsap.registerPlugin(
|
|
15
|
+
ScrollTrigger,
|
|
16
|
+
ScrambleTextPlugin,
|
|
17
|
+
SplitText
|
|
18
|
+
);
|
|
19
|
+
var cx = classVarianceCx;
|
|
20
|
+
|
|
21
|
+
// packages/animations/ScrambleGroup.jsx
|
|
22
|
+
import React3, { createContext as createContext2, useContext as useContext2, useRef as useRef2, useState as useState3, useCallback as useCallback3, useMemo as useMemo4 } from "react";
|
|
23
|
+
|
|
24
|
+
// libs/hooks/useIdleGSAP.js
|
|
25
|
+
import { useState as useState2, useEffect as useEffect3, useCallback as useCallback2, useMemo as useMemo3 } from "react";
|
|
26
|
+
import { useGSAP as useGSAP2 } from "@gsap/react";
|
|
27
|
+
|
|
28
|
+
// libs/hooks/usePageTransition.js
|
|
29
|
+
import { useMemo as useMemo2 } from "react";
|
|
30
|
+
|
|
31
|
+
// packages/providers/PageTransitionProvider.jsx
|
|
32
|
+
import React2, {
|
|
33
|
+
createContext,
|
|
34
|
+
useState,
|
|
35
|
+
useTransition,
|
|
36
|
+
useRef,
|
|
37
|
+
useCallback,
|
|
38
|
+
useEffect as useEffect2,
|
|
39
|
+
useMemo,
|
|
40
|
+
useContext
|
|
41
|
+
} from "react";
|
|
42
|
+
|
|
43
|
+
// packages/providers/LenisProvider.jsx
|
|
44
|
+
import React, { useEffect } from "react";
|
|
45
|
+
import { ReactLenis, useLenis } from "lenis/react";
|
|
46
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
47
|
+
var globalLenisInstance = null;
|
|
48
|
+
var isCssScrollLocked = false;
|
|
49
|
+
function getLenis() {
|
|
50
|
+
return globalLenisInstance;
|
|
51
|
+
}
|
|
52
|
+
function setCssScrollLocked(isLocked) {
|
|
53
|
+
if (isLocked !== isCssScrollLocked) {
|
|
54
|
+
isCssScrollLocked = isLocked;
|
|
55
|
+
if (isLocked) {
|
|
56
|
+
document.documentElement.classList.add("scroll-locked");
|
|
57
|
+
} else {
|
|
58
|
+
document.documentElement.classList.remove("scroll-locked");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function getCssScrollLocked() {
|
|
63
|
+
return isCssScrollLocked;
|
|
64
|
+
}
|
|
65
|
+
function scrollToTop(immediate = true) {
|
|
66
|
+
const lenis = globalLenisInstance;
|
|
67
|
+
if (lenis) {
|
|
68
|
+
lenis.scrollTo(0, { immediate });
|
|
69
|
+
} else {
|
|
70
|
+
window.scrollTo(0, 0);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function LenisTracker() {
|
|
74
|
+
const lenis = useLenis();
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
globalLenisInstance = lenis ?? null;
|
|
77
|
+
return () => {
|
|
78
|
+
globalLenisInstance = null;
|
|
79
|
+
};
|
|
80
|
+
}, [lenis]);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
function customEasing(t) {
|
|
84
|
+
return t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
|
|
85
|
+
}
|
|
86
|
+
function Lenis(props) {
|
|
87
|
+
return /* @__PURE__ */ jsxs(
|
|
88
|
+
ReactLenis,
|
|
89
|
+
{
|
|
90
|
+
root: true,
|
|
91
|
+
options: {
|
|
92
|
+
anchors: {
|
|
93
|
+
duration: 1.2,
|
|
94
|
+
easing: customEasing
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
...props,
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ jsx(LenisTracker, {}),
|
|
100
|
+
props.children
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// packages/providers/PageTransitionProvider.jsx
|
|
107
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
108
|
+
var PageTransitionContext = createContext(null);
|
|
109
|
+
var scrollTriggerModulePromise = null;
|
|
110
|
+
function getScrollTriggerModule() {
|
|
111
|
+
if (!scrollTriggerModulePromise) {
|
|
112
|
+
scrollTriggerModulePromise = import("gsap/ScrollTrigger");
|
|
113
|
+
}
|
|
114
|
+
return scrollTriggerModulePromise;
|
|
115
|
+
}
|
|
116
|
+
function clearScrollMemory() {
|
|
117
|
+
const modulePromise = getScrollTriggerModule();
|
|
118
|
+
if (modulePromise) {
|
|
119
|
+
modulePromise.then(({ ScrollTrigger: ScrollTrigger2 }) => {
|
|
120
|
+
ScrollTrigger2.clearScrollMemory();
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function refreshScrollTrigger() {
|
|
125
|
+
const modulePromise = getScrollTriggerModule();
|
|
126
|
+
if (modulePromise) {
|
|
127
|
+
modulePromise.then(({ ScrollTrigger: ScrollTrigger2 }) => {
|
|
128
|
+
ScrollTrigger2.refresh(true);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function PageTransitionProvider({ children }) {
|
|
133
|
+
const [phase, setPhase] = useState("idle");
|
|
134
|
+
const [isPending, startTransitionReact] = useTransition();
|
|
135
|
+
const timerRef = useRef(null);
|
|
136
|
+
const pendingCallbackRef = useRef(null);
|
|
137
|
+
const startTimeRef = useRef(0);
|
|
138
|
+
const clearTimer = useCallback(() => {
|
|
139
|
+
if (timerRef.current) {
|
|
140
|
+
clearTimeout(timerRef.current);
|
|
141
|
+
timerRef.current = null;
|
|
142
|
+
}
|
|
143
|
+
}, []);
|
|
144
|
+
const handlePopState = useCallback(() => {
|
|
145
|
+
clearTimer();
|
|
146
|
+
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
147
|
+
startTimeRef.current = 0;
|
|
148
|
+
pendingCallbackRef.current = null;
|
|
149
|
+
setPhase("holding");
|
|
150
|
+
clearScrollMemory();
|
|
151
|
+
timerRef.current = window.setTimeout(() => {
|
|
152
|
+
setPhase("exiting");
|
|
153
|
+
timerRef.current = window.setTimeout(() => {
|
|
154
|
+
setPhase("idle");
|
|
155
|
+
refreshScrollTrigger();
|
|
156
|
+
}, 1200);
|
|
157
|
+
}, 100);
|
|
158
|
+
}
|
|
159
|
+
}, [clearTimer]);
|
|
160
|
+
const startTransition = useCallback((callback) => {
|
|
161
|
+
clearTimer();
|
|
162
|
+
pendingCallbackRef.current = callback;
|
|
163
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
164
|
+
callback();
|
|
165
|
+
} else {
|
|
166
|
+
setPhase("entering");
|
|
167
|
+
timerRef.current = window.setTimeout(() => {
|
|
168
|
+
startTimeRef.current = Date.now();
|
|
169
|
+
setPhase("holding");
|
|
170
|
+
clearScrollMemory();
|
|
171
|
+
scrollToTop();
|
|
172
|
+
startTransitionReact(() => {
|
|
173
|
+
var _a;
|
|
174
|
+
(_a = pendingCallbackRef.current) == null ? void 0 : _a.call(pendingCallbackRef);
|
|
175
|
+
});
|
|
176
|
+
}, 1300);
|
|
177
|
+
}
|
|
178
|
+
}, [clearTimer, startTransitionReact]);
|
|
179
|
+
useEffect2(() => {
|
|
180
|
+
if (phase === "holding" && !isPending && startTimeRef.current > 0) {
|
|
181
|
+
const elapsed = Date.now() - startTimeRef.current;
|
|
182
|
+
const remainingDelay = Math.max(0, 100 - elapsed);
|
|
183
|
+
timerRef.current = window.setTimeout(() => {
|
|
184
|
+
setPhase("exiting");
|
|
185
|
+
startTimeRef.current = 0;
|
|
186
|
+
timerRef.current = window.setTimeout(() => {
|
|
187
|
+
setPhase("idle");
|
|
188
|
+
refreshScrollTrigger();
|
|
189
|
+
}, 1200);
|
|
190
|
+
}, remainingDelay);
|
|
191
|
+
}
|
|
192
|
+
}, [phase, isPending]);
|
|
193
|
+
useEffect2(() => {
|
|
194
|
+
const onPopState = () => {
|
|
195
|
+
handlePopState();
|
|
196
|
+
};
|
|
197
|
+
window.addEventListener("popstate", onPopState);
|
|
198
|
+
return () => {
|
|
199
|
+
window.removeEventListener("popstate", onPopState);
|
|
200
|
+
};
|
|
201
|
+
}, [handlePopState]);
|
|
202
|
+
useEffect2(() => {
|
|
203
|
+
return () => {
|
|
204
|
+
clearTimer();
|
|
205
|
+
};
|
|
206
|
+
}, [clearTimer]);
|
|
207
|
+
const contextValue = useMemo(() => ({
|
|
208
|
+
phase,
|
|
209
|
+
startTransition,
|
|
210
|
+
isPending
|
|
211
|
+
}), [phase, startTransition, isPending]);
|
|
212
|
+
return /* @__PURE__ */ jsx2(PageTransitionContext.Provider, { value: contextValue, children });
|
|
213
|
+
}
|
|
214
|
+
var defaultContextValue = {
|
|
215
|
+
phase: "idle",
|
|
216
|
+
startTransition: (t) => t(),
|
|
217
|
+
isPending: false
|
|
218
|
+
};
|
|
219
|
+
function usePageTransitionContext() {
|
|
220
|
+
return useContext(PageTransitionContext) ?? defaultContextValue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// libs/hooks/usePageTransition.js
|
|
224
|
+
function usePageTransition() {
|
|
225
|
+
const {
|
|
226
|
+
startTransition,
|
|
227
|
+
phase,
|
|
228
|
+
isPending
|
|
229
|
+
} = usePageTransitionContext();
|
|
230
|
+
const isTransitioning = phase !== "idle";
|
|
231
|
+
const transitionState = useMemo2(() => ({
|
|
232
|
+
startTransition,
|
|
233
|
+
isTransitioning,
|
|
234
|
+
phase,
|
|
235
|
+
isPending
|
|
236
|
+
}), [isPending, phase, startTransition, isTransitioning]);
|
|
237
|
+
return transitionState;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// libs/hooks/useIdleGSAP.js
|
|
241
|
+
function useIdleGSAP(callback, config) {
|
|
242
|
+
const { phase } = usePageTransition();
|
|
243
|
+
const [isIdle, setIsIdle] = useState2(phase === "idle");
|
|
244
|
+
useEffect3(() => {
|
|
245
|
+
if (phase === "idle") {
|
|
246
|
+
setIsIdle(true);
|
|
247
|
+
} else if (phase === "holding") {
|
|
248
|
+
setIsIdle(false);
|
|
249
|
+
}
|
|
250
|
+
}, [phase]);
|
|
251
|
+
const wrappedCallback = useCallback2((context, contextSafe) => {
|
|
252
|
+
if (isIdle) {
|
|
253
|
+
return callback(context, contextSafe);
|
|
254
|
+
}
|
|
255
|
+
}, [callback, isIdle]);
|
|
256
|
+
const gsapConfig = useMemo3(() => {
|
|
257
|
+
const configDependencies = (config == null ? void 0 : config.dependencies) ?? [];
|
|
258
|
+
const combinedDependencies = [...configDependencies, isIdle];
|
|
259
|
+
return {
|
|
260
|
+
...config,
|
|
261
|
+
dependencies: combinedDependencies
|
|
262
|
+
};
|
|
263
|
+
}, [config, isIdle]);
|
|
264
|
+
return useGSAP2(wrappedCallback, gsapConfig);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// packages/animations/ScrambleGroup.jsx
|
|
268
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
269
|
+
var ScrambleContext = createContext2(null);
|
|
270
|
+
function ScrambleGroup({
|
|
271
|
+
children,
|
|
272
|
+
stagger: stagger2 = 0.1,
|
|
273
|
+
start = "top 80%",
|
|
274
|
+
markers = false,
|
|
275
|
+
manual = false,
|
|
276
|
+
className
|
|
277
|
+
}) {
|
|
278
|
+
const childrenMapRef = useRef2(/* @__PURE__ */ new Map());
|
|
279
|
+
const [hasTriggered, setHasTriggered] = useState3(false);
|
|
280
|
+
const isTriggering = useRef2(false);
|
|
281
|
+
const scrollTriggerRef = useRef2(null);
|
|
282
|
+
const containerRef = useRef2(null);
|
|
283
|
+
const register = useCallback3((id, callback) => {
|
|
284
|
+
childrenMapRef.current.set(id, callback);
|
|
285
|
+
}, []);
|
|
286
|
+
const unregister = useCallback3((id) => {
|
|
287
|
+
childrenMapRef.current.delete(id);
|
|
288
|
+
}, []);
|
|
289
|
+
const triggerAll = useCallback3((customStagger) => {
|
|
290
|
+
if (isTriggering.current) return;
|
|
291
|
+
isTriggering.current = true;
|
|
292
|
+
const callbacks = Array.from(childrenMapRef.current.values());
|
|
293
|
+
const staggerValue = customStagger ?? stagger2;
|
|
294
|
+
callbacks.forEach((callbackFn, index) => {
|
|
295
|
+
gsap.delayedCall(index * staggerValue, () => {
|
|
296
|
+
callbackFn();
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
setHasTriggered(true);
|
|
300
|
+
}, [stagger2]);
|
|
301
|
+
const setupScrollTrigger = useCallback3(() => {
|
|
302
|
+
if (manual || !containerRef.current) return;
|
|
303
|
+
isTriggering.current = false;
|
|
304
|
+
const tween = gsap.to(containerRef.current, {
|
|
305
|
+
scrollTrigger: {
|
|
306
|
+
trigger: containerRef.current,
|
|
307
|
+
start,
|
|
308
|
+
markers,
|
|
309
|
+
toggleActions: "play none none none"
|
|
310
|
+
},
|
|
311
|
+
onStart: () => {
|
|
312
|
+
triggerAll();
|
|
313
|
+
},
|
|
314
|
+
duration: 1e-3
|
|
315
|
+
});
|
|
316
|
+
scrollTriggerRef.current = tween.scrollTrigger ?? null;
|
|
317
|
+
return () => {
|
|
318
|
+
var _a;
|
|
319
|
+
tween.kill();
|
|
320
|
+
(_a = scrollTriggerRef.current) == null ? void 0 : _a.kill();
|
|
321
|
+
};
|
|
322
|
+
}, [manual, start, markers, triggerAll]);
|
|
323
|
+
useIdleGSAP(setupScrollTrigger, {
|
|
324
|
+
dependencies: [manual, start, markers, triggerAll]
|
|
325
|
+
});
|
|
326
|
+
const contextValue = useMemo4(() => ({
|
|
327
|
+
register,
|
|
328
|
+
unregister,
|
|
329
|
+
triggerAll,
|
|
330
|
+
hasTriggered
|
|
331
|
+
}), [register, unregister, triggerAll, hasTriggered]);
|
|
332
|
+
return /* @__PURE__ */ jsx3(ScrambleContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx3("div", { ref: containerRef, className, children }) });
|
|
333
|
+
}
|
|
334
|
+
function useScrambleGroup() {
|
|
335
|
+
return useContext2(ScrambleContext);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// libs/constants/chars.js
|
|
339
|
+
var DEFAULT_CHARS = " .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$";
|
|
340
|
+
|
|
341
|
+
// packages/animations/ScrambleText.jsx
|
|
342
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
343
|
+
function ScrambleText({
|
|
344
|
+
children,
|
|
345
|
+
className,
|
|
346
|
+
duration = 0.6,
|
|
347
|
+
chars = DEFAULT_CHARS,
|
|
348
|
+
dualLayer = true,
|
|
349
|
+
triggerOnHover = false,
|
|
350
|
+
revealMode = false,
|
|
351
|
+
theme = "dark",
|
|
352
|
+
firstColorClass,
|
|
353
|
+
secondColorClass,
|
|
354
|
+
onComplete,
|
|
355
|
+
onReady,
|
|
356
|
+
multiLine = false
|
|
357
|
+
}) {
|
|
358
|
+
const containerRef = useRef3(null);
|
|
359
|
+
const textElementRef = useRef3(null);
|
|
360
|
+
const timelineRef = useRef3(null);
|
|
361
|
+
const internalTextRef = useRef3("");
|
|
362
|
+
const isCompleteRef = useRef3(false);
|
|
363
|
+
const instanceIdRef = useRef3(`scramble-${Math.random().toString(36).slice(2, 9)}`);
|
|
364
|
+
const themeColors = theme === "brand" ? { firstColorClass: "scramble-white", secondColorClass: "scramble-foreground" } : { firstColorClass: "scramble-brand", secondColorClass: "scramble-foreground" };
|
|
365
|
+
const color1 = firstColorClass ?? themeColors.firstColorClass;
|
|
366
|
+
const color2 = secondColorClass ?? themeColors.secondColorClass;
|
|
367
|
+
const scrambleGroup = useScrambleGroup();
|
|
368
|
+
let childText;
|
|
369
|
+
if (typeof children === "string") {
|
|
370
|
+
childText = children;
|
|
371
|
+
} else if (typeof children === "number") {
|
|
372
|
+
childText = String(children);
|
|
373
|
+
} else {
|
|
374
|
+
childText = "";
|
|
375
|
+
}
|
|
376
|
+
useEffect4(() => {
|
|
377
|
+
internalTextRef.current = childText;
|
|
378
|
+
}, [childText]);
|
|
379
|
+
const killTimeline = () => {
|
|
380
|
+
if (timelineRef.current) {
|
|
381
|
+
timelineRef.current.kill();
|
|
382
|
+
timelineRef.current = null;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
const triggerAnimation = () => {
|
|
386
|
+
if (!textElementRef.current) return null;
|
|
387
|
+
const targetEl = textElementRef.current;
|
|
388
|
+
const textToScramble = internalTextRef.current || childText;
|
|
389
|
+
if (!textToScramble || textToScramble.length === 0) return null;
|
|
390
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
391
|
+
targetEl.textContent = textToScramble;
|
|
392
|
+
targetEl.className = targetEl.className.replace(/\bscramble-\w+\b/g, "");
|
|
393
|
+
isCompleteRef.current = true;
|
|
394
|
+
onComplete == null ? void 0 : onComplete();
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
killTimeline();
|
|
398
|
+
timelineRef.current = gsap.timeline({
|
|
399
|
+
onComplete: () => {
|
|
400
|
+
timelineRef.current = null;
|
|
401
|
+
isCompleteRef.current = true;
|
|
402
|
+
onComplete == null ? void 0 : onComplete();
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
if (dualLayer) {
|
|
406
|
+
const generateScrambledString = (targetString, charset = DEFAULT_CHARS) => {
|
|
407
|
+
let result = "";
|
|
408
|
+
for (let t = 0; t < targetString.length; t++) {
|
|
409
|
+
let char = targetString[t];
|
|
410
|
+
if (char === " " || char === "\n" || char === "\r") {
|
|
411
|
+
result += char;
|
|
412
|
+
} else {
|
|
413
|
+
result += charset[Math.floor(Math.random() * charset.length)];
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return result;
|
|
417
|
+
};
|
|
418
|
+
const scrambledText = generateScrambledString(textToScramble, chars);
|
|
419
|
+
const nonWhitespaceLength = textToScramble.replace(/\s/g, "").length;
|
|
420
|
+
const stepDuration = nonWhitespaceLength > 0 ? duration / nonWhitespaceLength : 0;
|
|
421
|
+
if (revealMode && !isCompleteRef.current) {
|
|
422
|
+
targetEl.textContent = textToScramble.replace(/[^\s\n\r]/g, "\xA0");
|
|
423
|
+
timelineRef.current.to(targetEl, {
|
|
424
|
+
duration,
|
|
425
|
+
scrambleText: {
|
|
426
|
+
text: scrambledText,
|
|
427
|
+
chars,
|
|
428
|
+
speed: 1,
|
|
429
|
+
revealDelay: 0.1,
|
|
430
|
+
oldClass: color1,
|
|
431
|
+
newClass: color1
|
|
432
|
+
},
|
|
433
|
+
ease: "none"
|
|
434
|
+
});
|
|
435
|
+
} else {
|
|
436
|
+
timelineRef.current.to(targetEl, {
|
|
437
|
+
duration,
|
|
438
|
+
scrambleText: {
|
|
439
|
+
text: scrambledText,
|
|
440
|
+
chars,
|
|
441
|
+
speed: 1,
|
|
442
|
+
revealDelay: 0.1,
|
|
443
|
+
oldClass: color2,
|
|
444
|
+
newClass: color1
|
|
445
|
+
},
|
|
446
|
+
ease: "none"
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
timelineRef.current.to(targetEl, {
|
|
450
|
+
duration,
|
|
451
|
+
scrambleText: {
|
|
452
|
+
text: textToScramble,
|
|
453
|
+
chars,
|
|
454
|
+
speed: 1,
|
|
455
|
+
revealDelay: 0.1,
|
|
456
|
+
oldClass: color1,
|
|
457
|
+
newClass: color2
|
|
458
|
+
},
|
|
459
|
+
ease: "none"
|
|
460
|
+
}, stepDuration);
|
|
461
|
+
} else {
|
|
462
|
+
timelineRef.current.to(targetEl, {
|
|
463
|
+
duration,
|
|
464
|
+
scrambleText: {
|
|
465
|
+
text: textToScramble,
|
|
466
|
+
chars,
|
|
467
|
+
speed: 1,
|
|
468
|
+
revealDelay: 0.2
|
|
469
|
+
},
|
|
470
|
+
ease: "none"
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
return timelineRef.current;
|
|
474
|
+
};
|
|
475
|
+
useEffect4(() => {
|
|
476
|
+
if (scrambleGroup) {
|
|
477
|
+
scrambleGroup.register(instanceIdRef.current, triggerAnimation);
|
|
478
|
+
return () => {
|
|
479
|
+
scrambleGroup.unregister(instanceIdRef.current);
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
}, [scrambleGroup, triggerAnimation]);
|
|
483
|
+
useEffect4(() => {
|
|
484
|
+
onReady == null ? void 0 : onReady(triggerAnimation);
|
|
485
|
+
}, [onReady, triggerAnimation]);
|
|
486
|
+
const handleMouseEnter = () => {
|
|
487
|
+
if (triggerOnHover) {
|
|
488
|
+
triggerAnimation();
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
useEffect4(() => {
|
|
492
|
+
return () => {
|
|
493
|
+
killTimeline();
|
|
494
|
+
};
|
|
495
|
+
}, []);
|
|
496
|
+
const initialDisplayText = revealMode ? childText.replace(/[^\s\n\r]/g, "\xA0") : childText;
|
|
497
|
+
const whiteSpaceValue = multiLine ? "normal" : "nowrap";
|
|
498
|
+
const displayValue = multiLine ? "inline" : "inline-block";
|
|
499
|
+
const containerStyle = {
|
|
500
|
+
position: "relative",
|
|
501
|
+
display: displayValue,
|
|
502
|
+
whiteSpace: whiteSpaceValue
|
|
503
|
+
};
|
|
504
|
+
const hiddenSpanStyle = {
|
|
505
|
+
visibility: "hidden",
|
|
506
|
+
whiteSpace: whiteSpaceValue
|
|
507
|
+
};
|
|
508
|
+
const animatedSpanStyle = {
|
|
509
|
+
position: "absolute",
|
|
510
|
+
top: 0,
|
|
511
|
+
left: 0,
|
|
512
|
+
whiteSpace: whiteSpaceValue,
|
|
513
|
+
...multiLine ? { width: "100%" } : {}
|
|
514
|
+
};
|
|
515
|
+
return /* @__PURE__ */ jsxs2(
|
|
516
|
+
"span",
|
|
517
|
+
{
|
|
518
|
+
ref: containerRef,
|
|
519
|
+
className,
|
|
520
|
+
style: containerStyle,
|
|
521
|
+
onMouseEnter: triggerOnHover ? handleMouseEnter : void 0,
|
|
522
|
+
children: [
|
|
523
|
+
/* @__PURE__ */ jsx4("span", { className: "sr-only", children: childText }),
|
|
524
|
+
/* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: hiddenSpanStyle, children: childText }),
|
|
525
|
+
/* @__PURE__ */ jsx4("span", { ref: textElementRef, "aria-hidden": "true", style: animatedSpanStyle, children: initialDisplayText })
|
|
526
|
+
]
|
|
527
|
+
}
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// packages/animations/AnimatedHeadline.jsx
|
|
532
|
+
import React5, { forwardRef, useRef as useRef4, useState as useState4, useLayoutEffect, useCallback as useCallback4, useImperativeHandle, Fragment } from "react";
|
|
533
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
534
|
+
var typographyClasses = {
|
|
535
|
+
display: "text-display",
|
|
536
|
+
h1: "text-h1",
|
|
537
|
+
h2: "text-h2",
|
|
538
|
+
h3: "text-h3",
|
|
539
|
+
h4: "text-h4",
|
|
540
|
+
h5: "text-h5",
|
|
541
|
+
h6: "text-h6"
|
|
542
|
+
};
|
|
543
|
+
var AnimatedHeadline = forwardRef(({
|
|
544
|
+
children,
|
|
545
|
+
as: Component = "h1",
|
|
546
|
+
displayAs,
|
|
547
|
+
className,
|
|
548
|
+
skip,
|
|
549
|
+
trigger = "manual",
|
|
550
|
+
wrapperClassName
|
|
551
|
+
}, ref) => {
|
|
552
|
+
const containerRef = useRef4(null);
|
|
553
|
+
const wrapperRef = useRef4(null);
|
|
554
|
+
const hasRevealedRef = useRef4(false);
|
|
555
|
+
const [lines, setLines] = useState4(null);
|
|
556
|
+
const shouldSkip = skip ?? window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
557
|
+
useLayoutEffect(() => {
|
|
558
|
+
if (!containerRef.current) return;
|
|
559
|
+
const wordNodes = containerRef.current.querySelectorAll("[data-word]");
|
|
560
|
+
if (wordNodes.length === 0) return;
|
|
561
|
+
const computedLines = [];
|
|
562
|
+
let currentLine = [];
|
|
563
|
+
let lastY = -Infinity;
|
|
564
|
+
let lastExplicitLine = -1;
|
|
565
|
+
for (const node of wordNodes) {
|
|
566
|
+
const top = node.getBoundingClientRect().top;
|
|
567
|
+
const explicitLine = Number(node.dataset.explicitLine ?? -1);
|
|
568
|
+
const isNewExplicit = explicitLine !== lastExplicitLine && lastExplicitLine !== -1;
|
|
569
|
+
if (lastY > -Infinity && top - lastY > 2 || isNewExplicit) {
|
|
570
|
+
computedLines.push(currentLine.join(" "));
|
|
571
|
+
currentLine = [];
|
|
572
|
+
}
|
|
573
|
+
currentLine.push(node.textContent || "");
|
|
574
|
+
lastY = top;
|
|
575
|
+
lastExplicitLine = explicitLine;
|
|
576
|
+
}
|
|
577
|
+
if (currentLine.length > 0) {
|
|
578
|
+
computedLines.push(currentLine.join(" "));
|
|
579
|
+
}
|
|
580
|
+
setLines(computedLines);
|
|
581
|
+
}, []);
|
|
582
|
+
useGSAP(() => {
|
|
583
|
+
if (shouldSkip || !containerRef.current || !lines) return;
|
|
584
|
+
const container = containerRef.current;
|
|
585
|
+
gsap.set(container.querySelectorAll("[data-line-inner]"), { opacity: 0 });
|
|
586
|
+
gsap.set(container.querySelectorAll("[data-brand-rect], [data-fg-rect]"), { scaleX: 0, transformOrigin: "left" });
|
|
587
|
+
}, { dependencies: [shouldSkip, lines] });
|
|
588
|
+
useIdleGSAP(() => {
|
|
589
|
+
if (trigger !== "scroll" || shouldSkip || !lines) return;
|
|
590
|
+
const triggerEl = wrapperRef.current || containerRef.current;
|
|
591
|
+
if (triggerEl) {
|
|
592
|
+
ScrollTrigger.create({
|
|
593
|
+
trigger: triggerEl,
|
|
594
|
+
start: "top bottom",
|
|
595
|
+
once: true,
|
|
596
|
+
onEnter: () => reveal()
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
}, { dependencies: [trigger, shouldSkip, lines] });
|
|
600
|
+
const reveal = useCallback4((delayOffset = 0) => {
|
|
601
|
+
if (hasRevealedRef.current || shouldSkip || !containerRef.current) return;
|
|
602
|
+
hasRevealedRef.current = true;
|
|
603
|
+
const lineNodes = containerRef.current.querySelectorAll("[data-line]");
|
|
604
|
+
for (let r = 0; r < lineNodes.length; r++) {
|
|
605
|
+
const lineNode = lineNodes[r];
|
|
606
|
+
if (!lineNode) continue;
|
|
607
|
+
const delay = delayOffset + 0.15 * r;
|
|
608
|
+
const lineInner = lineNode.querySelector("[data-line-inner]");
|
|
609
|
+
const brandRect = lineNode.querySelector("[data-brand-rect]");
|
|
610
|
+
const fgRect = lineNode.querySelector("[data-fg-rect]");
|
|
611
|
+
if (!lineInner || !brandRect || !fgRect) continue;
|
|
612
|
+
const rects = [brandRect, fgRect];
|
|
613
|
+
const tl = gsap.timeline({ delay });
|
|
614
|
+
tl.to(brandRect, { scaleX: 1, duration: 0.45, ease: "power3.inOut" }, 0);
|
|
615
|
+
tl.to(fgRect, { scaleX: 1, duration: 0.45, ease: "power3.inOut" }, 0.1);
|
|
616
|
+
tl.set(lineInner, { opacity: 1 }, 0.5);
|
|
617
|
+
tl.set(rects, { transformOrigin: "right" }, 0.5);
|
|
618
|
+
tl.to(fgRect, { scaleX: 0, duration: 0.45, ease: "power3.inOut" }, 0.5);
|
|
619
|
+
tl.to(brandRect, { scaleX: 0, duration: 0.45, ease: "power3.inOut" }, 0.6);
|
|
620
|
+
}
|
|
621
|
+
}, [shouldSkip]);
|
|
622
|
+
const reset = useCallback4(() => {
|
|
623
|
+
hasRevealedRef.current = false;
|
|
624
|
+
if (!containerRef.current) return;
|
|
625
|
+
const container = containerRef.current;
|
|
626
|
+
gsap.set(container.querySelectorAll("[data-line-inner]"), { opacity: 0 });
|
|
627
|
+
gsap.set(container.querySelectorAll("[data-brand-rect], [data-fg-rect]"), { scaleX: 0, transformOrigin: "left" });
|
|
628
|
+
}, []);
|
|
629
|
+
useImperativeHandle(ref, () => ({ reveal, reset }), [reveal, reset]);
|
|
630
|
+
const resolvedClassName = cx(typographyClasses[displayAs ?? Component], className);
|
|
631
|
+
if (!lines) {
|
|
632
|
+
const manualLines = children.split("\n");
|
|
633
|
+
const preRenderedContent = /* @__PURE__ */ jsx5(Component, { ref: containerRef, className: resolvedClassName, children: manualLines.map((lineStr, lineIndex) => /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
634
|
+
lineIndex > 0 && /* @__PURE__ */ jsx5("br", {}),
|
|
635
|
+
lineStr.split(/\s+/).filter(Boolean).map((word, wordIndex) => /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
636
|
+
wordIndex > 0 && " ",
|
|
637
|
+
/* @__PURE__ */ jsx5("span", { "data-word": true, "data-explicit-line": lineIndex, children: word })
|
|
638
|
+
] }, wordIndex))
|
|
639
|
+
] }, lineIndex)) });
|
|
640
|
+
return trigger === "scroll" ? /* @__PURE__ */ jsx5("div", { ref: wrapperRef, className: wrapperClassName, children: preRenderedContent }) : preRenderedContent;
|
|
641
|
+
}
|
|
642
|
+
const animatedContent = /* @__PURE__ */ jsx5(Component, { ref: containerRef, className: resolvedClassName, children: lines.map((lineStr, lineIndex) => /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
643
|
+
lineIndex > 0 && /* @__PURE__ */ jsx5("br", {}),
|
|
644
|
+
/* @__PURE__ */ jsxs3("div", { "data-line": true, className: "relative inline-block", children: [
|
|
645
|
+
/* @__PURE__ */ jsx5("span", { "data-line-inner": true, className: "block whitespace-nowrap", children: lineStr }),
|
|
646
|
+
!shouldSkip && /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
647
|
+
/* @__PURE__ */ jsx5("div", { "data-brand-rect": true, className: "absolute -inset-x-[0.1em] -inset-y-[0.1em] bg-brand" }),
|
|
648
|
+
/* @__PURE__ */ jsx5("div", { "data-fg-rect": true, className: "absolute -inset-x-[0.1em] -inset-y-[0.1em] bg-foreground" })
|
|
649
|
+
] })
|
|
650
|
+
] })
|
|
651
|
+
] }, lineIndex)) });
|
|
652
|
+
return trigger === "scroll" ? /* @__PURE__ */ jsx5("div", { ref: wrapperRef, className: wrapperClassName, children: animatedContent }) : animatedContent;
|
|
653
|
+
});
|
|
654
|
+
var AnimatedHeadline_default = AnimatedHeadline;
|
|
655
|
+
|
|
656
|
+
// packages/animations/AnimatedButton.jsx
|
|
657
|
+
import React6, { useRef as useRef6 } from "react";
|
|
658
|
+
import { Slot, Slottable } from "@radix-ui/react-slot";
|
|
659
|
+
|
|
660
|
+
// libs/hooks/useDualLayerScramble.js
|
|
661
|
+
import { useRef as useRef5, useEffect as useEffect5, useCallback as useCallback5 } from "react";
|
|
662
|
+
var DEFAULT_CHARS2 = " .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$";
|
|
663
|
+
function useDualLayerScramble(options) {
|
|
664
|
+
const elementRef = useRef5(null);
|
|
665
|
+
const timelineRef = useRef5(null);
|
|
666
|
+
const originalTextRef = useRef5("");
|
|
667
|
+
const originalHTMLRef = useRef5("");
|
|
668
|
+
const dimensionsRef = useRef5(null);
|
|
669
|
+
const linesDataRef = useRef5([]);
|
|
670
|
+
const spansRef = useRef5([]);
|
|
671
|
+
const isAnimatingRef = useRef5(false);
|
|
672
|
+
const isPreparedRef = useRef5(false);
|
|
673
|
+
useEffect5(() => {
|
|
674
|
+
if (!elementRef.current) return;
|
|
675
|
+
const el = elementRef.current;
|
|
676
|
+
const innerText = el.innerText ?? "";
|
|
677
|
+
if (innerText.trim().length > 0) {
|
|
678
|
+
originalTextRef.current = innerText;
|
|
679
|
+
originalHTMLRef.current = el.innerHTML;
|
|
680
|
+
dimensionsRef.current = { width: el.offsetWidth, height: el.offsetHeight };
|
|
681
|
+
}
|
|
682
|
+
}, []);
|
|
683
|
+
const killTimeline = useCallback5(() => {
|
|
684
|
+
if (timelineRef.current) {
|
|
685
|
+
timelineRef.current.kill();
|
|
686
|
+
timelineRef.current = null;
|
|
687
|
+
isAnimatingRef.current = false;
|
|
688
|
+
}
|
|
689
|
+
}, []);
|
|
690
|
+
const prepareDOM = useCallback5(() => {
|
|
691
|
+
var _a, _b;
|
|
692
|
+
if (!elementRef.current || isPreparedRef.current) return;
|
|
693
|
+
const el = elementRef.current;
|
|
694
|
+
if ((originalTextRef.current || el.innerText || "").trim().length === 0) return;
|
|
695
|
+
if (!dimensionsRef.current) {
|
|
696
|
+
dimensionsRef.current = { width: el.offsetWidth, height: el.offsetHeight };
|
|
697
|
+
}
|
|
698
|
+
const splitTextIntoLines = (element) => {
|
|
699
|
+
const text = element.innerText || "";
|
|
700
|
+
if (text.trim().length === 0) return [];
|
|
701
|
+
if (text.includes("\n")) return text.split("\n").filter((line) => line.length > 0);
|
|
702
|
+
const firstChild = element.firstChild;
|
|
703
|
+
if (!firstChild || firstChild.nodeType !== Node.TEXT_NODE) return [text];
|
|
704
|
+
const range = document.createRange();
|
|
705
|
+
const lines2 = [];
|
|
706
|
+
let currentLine = "";
|
|
707
|
+
let lastTop = null;
|
|
708
|
+
const length = firstChild.length;
|
|
709
|
+
for (let i = 0; i < text.length && i < length; i++) {
|
|
710
|
+
range.setStart(firstChild, i);
|
|
711
|
+
range.setEnd(firstChild, i + 1);
|
|
712
|
+
const rect = range.getBoundingClientRect();
|
|
713
|
+
if (lastTop !== null && Math.abs(rect.top - lastTop) > 2) {
|
|
714
|
+
if (currentLine.length > 0) lines2.push(currentLine);
|
|
715
|
+
currentLine = "";
|
|
716
|
+
}
|
|
717
|
+
currentLine += text[i];
|
|
718
|
+
lastTop = rect.top;
|
|
719
|
+
}
|
|
720
|
+
if (currentLine.length > 0) lines2.push(currentLine);
|
|
721
|
+
return lines2.length > 0 ? lines2 : [text];
|
|
722
|
+
};
|
|
723
|
+
const lines = splitTextIntoLines(el);
|
|
724
|
+
const testDiv = document.createElement("div");
|
|
725
|
+
testDiv.style.cssText = `
|
|
726
|
+
position: absolute;
|
|
727
|
+
visibility: hidden;
|
|
728
|
+
pointer-events: none;
|
|
729
|
+
white-space: nowrap;
|
|
730
|
+
`;
|
|
731
|
+
const computedStyle = window.getComputedStyle(el);
|
|
732
|
+
testDiv.style.font = computedStyle.font;
|
|
733
|
+
testDiv.style.fontSize = computedStyle.fontSize;
|
|
734
|
+
testDiv.style.fontFamily = computedStyle.fontFamily;
|
|
735
|
+
testDiv.style.fontWeight = computedStyle.fontWeight;
|
|
736
|
+
testDiv.style.letterSpacing = computedStyle.letterSpacing;
|
|
737
|
+
testDiv.style.textTransform = computedStyle.textTransform;
|
|
738
|
+
document.body.appendChild(testDiv);
|
|
739
|
+
linesDataRef.current = lines.map((line) => {
|
|
740
|
+
testDiv.textContent = line;
|
|
741
|
+
return { text: line, width: testDiv.offsetWidth, height: testDiv.offsetHeight };
|
|
742
|
+
});
|
|
743
|
+
document.body.removeChild(testDiv);
|
|
744
|
+
const maxWidth = Math.max(...linesDataRef.current.map((lineData) => lineData.width));
|
|
745
|
+
const totalHeight = linesDataRef.current.reduce((sum, lineData) => sum + lineData.height, 0);
|
|
746
|
+
const finalWidth = Math.max(((_a = dimensionsRef.current) == null ? void 0 : _a.width) ?? 0, maxWidth);
|
|
747
|
+
const finalHeight = Math.max(((_b = dimensionsRef.current) == null ? void 0 : _b.height) ?? 0, totalHeight);
|
|
748
|
+
isPreparedRef.current = true;
|
|
749
|
+
gsap.set(el, { width: finalWidth, height: finalHeight, display: "inline-block", overflow: "hidden" });
|
|
750
|
+
el.innerHTML = "";
|
|
751
|
+
spansRef.current = [];
|
|
752
|
+
linesDataRef.current.forEach((lineData) => {
|
|
753
|
+
const span = document.createElement("span");
|
|
754
|
+
span.style.cssText = `
|
|
755
|
+
display: block;
|
|
756
|
+
opacity: 0;
|
|
757
|
+
width: ${lineData.width}px;
|
|
758
|
+
height: ${lineData.height}px;
|
|
759
|
+
overflow: hidden;
|
|
760
|
+
white-space: nowrap;
|
|
761
|
+
`;
|
|
762
|
+
span.innerText = lineData.text;
|
|
763
|
+
el.appendChild(span);
|
|
764
|
+
spansRef.current.push(span);
|
|
765
|
+
});
|
|
766
|
+
gsap.set(el, { opacity: 1 });
|
|
767
|
+
}, []);
|
|
768
|
+
const playScramble = useCallback5((overrides) => {
|
|
769
|
+
var _a;
|
|
770
|
+
if (!elementRef.current) return null;
|
|
771
|
+
if (isAnimatingRef.current) return timelineRef.current;
|
|
772
|
+
prepareDOM();
|
|
773
|
+
const config = { ...options, ...overrides };
|
|
774
|
+
const duration = config.duration ?? 1;
|
|
775
|
+
const speed = config.speed ?? 1;
|
|
776
|
+
const chars = config.chars ?? DEFAULT_CHARS2;
|
|
777
|
+
const firstColorClass = config.firstColorClass ?? "scramble-brand";
|
|
778
|
+
const secondColorClass = config.secondColorClass ?? "scramble-foreground";
|
|
779
|
+
const stagger2 = config.stagger ?? 0.08;
|
|
780
|
+
const linesData = linesDataRef.current;
|
|
781
|
+
const spans = spansRef.current;
|
|
782
|
+
if (linesData.length === 0 || spans.length === 0) return null;
|
|
783
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
784
|
+
spans.forEach((span, index) => {
|
|
785
|
+
const lineData = linesData[index];
|
|
786
|
+
if (lineData) {
|
|
787
|
+
span.innerText = lineData.text;
|
|
788
|
+
}
|
|
789
|
+
span.style.opacity = "1";
|
|
790
|
+
span.className = span.className.replace(/\bscramble-\w+\b/g, "");
|
|
791
|
+
});
|
|
792
|
+
(_a = config.onComplete) == null ? void 0 : _a.call(config);
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
killTimeline();
|
|
796
|
+
isAnimatingRef.current = true;
|
|
797
|
+
timelineRef.current = gsap.timeline({
|
|
798
|
+
onComplete: () => {
|
|
799
|
+
var _a2;
|
|
800
|
+
isAnimatingRef.current = false;
|
|
801
|
+
timelineRef.current = null;
|
|
802
|
+
(_a2 = config.onComplete) == null ? void 0 : _a2.call(config);
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
spans.forEach((span, index) => {
|
|
806
|
+
var _a2, _b, _c;
|
|
807
|
+
const lineData = linesData[index];
|
|
808
|
+
if (!lineData) return;
|
|
809
|
+
const originalText = lineData.text;
|
|
810
|
+
const delay = index * stagger2;
|
|
811
|
+
const generateScrambled = (text, charSet = DEFAULT_CHARS2) => {
|
|
812
|
+
let scrambled = "";
|
|
813
|
+
for (let i = 0; i < text.length; i++) {
|
|
814
|
+
const char = text[i];
|
|
815
|
+
if (char === " ") {
|
|
816
|
+
scrambled += char;
|
|
817
|
+
} else {
|
|
818
|
+
scrambled += charSet[Math.floor(Math.random() * charSet.length)];
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return scrambled;
|
|
822
|
+
};
|
|
823
|
+
const initialScrambledText = generateScrambled(originalText, chars);
|
|
824
|
+
const nonSpaceCount = originalText.replace(/\s/g, "").length;
|
|
825
|
+
const spacesOnly = originalText.replace(/[^\s]/g, " ");
|
|
826
|
+
(_a2 = timelineRef.current) == null ? void 0 : _a2.add(() => {
|
|
827
|
+
gsap.set(span, { opacity: 1 });
|
|
828
|
+
span.innerText = spacesOnly;
|
|
829
|
+
}, delay);
|
|
830
|
+
(_b = timelineRef.current) == null ? void 0 : _b.to(span, {
|
|
831
|
+
duration,
|
|
832
|
+
scrambleText: {
|
|
833
|
+
text: initialScrambledText,
|
|
834
|
+
chars,
|
|
835
|
+
speed,
|
|
836
|
+
revealDelay: 0.1,
|
|
837
|
+
oldClass: firstColorClass,
|
|
838
|
+
newClass: firstColorClass
|
|
839
|
+
},
|
|
840
|
+
ease: "none"
|
|
841
|
+
}, delay);
|
|
842
|
+
(_c = timelineRef.current) == null ? void 0 : _c.to(span, {
|
|
843
|
+
duration,
|
|
844
|
+
scrambleText: {
|
|
845
|
+
text: originalText,
|
|
846
|
+
chars,
|
|
847
|
+
speed,
|
|
848
|
+
revealDelay: 0.1,
|
|
849
|
+
oldClass: firstColorClass,
|
|
850
|
+
newClass: secondColorClass
|
|
851
|
+
},
|
|
852
|
+
ease: "none"
|
|
853
|
+
}, delay + (nonSpaceCount > 0 ? duration / nonSpaceCount : 0));
|
|
854
|
+
});
|
|
855
|
+
return timelineRef.current;
|
|
856
|
+
}, [options, killTimeline, prepareDOM]);
|
|
857
|
+
const resetDOM = useCallback5(() => {
|
|
858
|
+
killTimeline();
|
|
859
|
+
if (elementRef.current && isPreparedRef.current) {
|
|
860
|
+
elementRef.current.innerHTML = originalHTMLRef.current || originalTextRef.current;
|
|
861
|
+
gsap.set(elementRef.current, {
|
|
862
|
+
opacity: 0,
|
|
863
|
+
width: "auto",
|
|
864
|
+
height: "auto",
|
|
865
|
+
overflow: "visible"
|
|
866
|
+
});
|
|
867
|
+
spansRef.current = [];
|
|
868
|
+
linesDataRef.current = [];
|
|
869
|
+
isPreparedRef.current = false;
|
|
870
|
+
}
|
|
871
|
+
}, [killTimeline]);
|
|
872
|
+
useEffect5(() => {
|
|
873
|
+
return () => {
|
|
874
|
+
killTimeline();
|
|
875
|
+
spansRef.current = [];
|
|
876
|
+
linesDataRef.current = [];
|
|
877
|
+
isPreparedRef.current = false;
|
|
878
|
+
};
|
|
879
|
+
}, [killTimeline]);
|
|
880
|
+
return {
|
|
881
|
+
ref: elementRef,
|
|
882
|
+
scramble: playScramble,
|
|
883
|
+
kill: resetDOM,
|
|
884
|
+
isAnimating: isAnimatingRef.current
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// packages/animations/AnimatedButton.jsx
|
|
889
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
890
|
+
var buttonVariants = cva(
|
|
891
|
+
[
|
|
892
|
+
"group inline-flex min-w-0 shrink-0 cursor-pointer items-center justify-center whitespace-nowrap",
|
|
893
|
+
"font-medium font-mono uppercase",
|
|
894
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-brand/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
895
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
896
|
+
],
|
|
897
|
+
{
|
|
898
|
+
variants: {
|
|
899
|
+
variant: {
|
|
900
|
+
primary: []
|
|
901
|
+
},
|
|
902
|
+
size: {
|
|
903
|
+
xs: "text-body-sm",
|
|
904
|
+
sm: "text-body-sm",
|
|
905
|
+
default: "text-body-sm lg:text-body",
|
|
906
|
+
lg: "text-body lg:text-body-lg"
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
defaultVariants: {
|
|
910
|
+
variant: "primary",
|
|
911
|
+
size: "default"
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
);
|
|
915
|
+
var iconVariants = cva(
|
|
916
|
+
[
|
|
917
|
+
"flex items-center justify-center",
|
|
918
|
+
"transition-transform duration-700 [transition-timing-function:var(--ease-power4-in-out)]"
|
|
919
|
+
],
|
|
920
|
+
{
|
|
921
|
+
variants: {
|
|
922
|
+
size: {
|
|
923
|
+
xs: "size-32",
|
|
924
|
+
sm: "size-32 lg:size-40",
|
|
925
|
+
default: "size-40 lg:size-48",
|
|
926
|
+
lg: "size-48 lg:size-56"
|
|
927
|
+
},
|
|
928
|
+
position: {
|
|
929
|
+
left: "origin-left -rotate-45 scale-0",
|
|
930
|
+
right: "absolute right-0 z-10 origin-right rotate-0 scale-100"
|
|
931
|
+
},
|
|
932
|
+
theme: {
|
|
933
|
+
light: "bg-foreground text-background",
|
|
934
|
+
dark: "bg-foreground text-background",
|
|
935
|
+
brand: "bg-brand text-black"
|
|
936
|
+
}
|
|
937
|
+
},
|
|
938
|
+
defaultVariants: {
|
|
939
|
+
size: "default",
|
|
940
|
+
theme: "light"
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
);
|
|
944
|
+
var textWrapperVariants = cva(
|
|
945
|
+
[
|
|
946
|
+
"flex w-full flex-1 items-center justify-center",
|
|
947
|
+
"transition-transform duration-700 [transition-timing-function:var(--ease-power4-in-out)]"
|
|
948
|
+
],
|
|
949
|
+
{
|
|
950
|
+
variants: {
|
|
951
|
+
size: {
|
|
952
|
+
xs: "h-32 -translate-x-[calc(32px+6px)] px-8",
|
|
953
|
+
sm: "h-32 -translate-x-[calc(32px+6px)] px-8 lg:h-40 lg:-translate-x-[calc(40px+6px)] lg:px-12",
|
|
954
|
+
default: "h-40 -translate-x-[calc(40px+6px)] px-12 lg:h-48 lg:-translate-x-[calc(48px+6px)] lg:px-16",
|
|
955
|
+
lg: "h-48 -translate-x-[calc(48px+6px)] px-16 lg:h-56 lg:-translate-x-[calc(56px+6px)] lg:px-24"
|
|
956
|
+
},
|
|
957
|
+
theme: {
|
|
958
|
+
light: "bg-foreground text-background",
|
|
959
|
+
dark: "bg-foreground text-background",
|
|
960
|
+
brand: "bg-brand text-black"
|
|
961
|
+
}
|
|
962
|
+
},
|
|
963
|
+
defaultVariants: {
|
|
964
|
+
size: "default",
|
|
965
|
+
theme: "light"
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
);
|
|
969
|
+
function PlusIcon({ className }) {
|
|
970
|
+
return /* @__PURE__ */ jsx6(
|
|
971
|
+
"svg",
|
|
972
|
+
{
|
|
973
|
+
className: cx("size-[0.75em]", className),
|
|
974
|
+
viewBox: "0 0 12 12",
|
|
975
|
+
fill: "none",
|
|
976
|
+
"aria-hidden": "true",
|
|
977
|
+
children: /* @__PURE__ */ jsx6(
|
|
978
|
+
"path",
|
|
979
|
+
{
|
|
980
|
+
d: "M6 1v10M1 6h10",
|
|
981
|
+
stroke: "currentColor",
|
|
982
|
+
strokeWidth: "1.5",
|
|
983
|
+
strokeLinecap: "square"
|
|
984
|
+
}
|
|
985
|
+
)
|
|
986
|
+
}
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
function AnimatedButton(props) {
|
|
990
|
+
const {
|
|
991
|
+
children,
|
|
992
|
+
className,
|
|
993
|
+
asChild,
|
|
994
|
+
variant,
|
|
995
|
+
size,
|
|
996
|
+
theme,
|
|
997
|
+
type,
|
|
998
|
+
...rest
|
|
999
|
+
} = props;
|
|
1000
|
+
const resolvedType = type === void 0 ? asChild ? void 0 : "button" : type;
|
|
1001
|
+
const Comp = asChild ? Slot : "button";
|
|
1002
|
+
const themeColors = theme === "brand" ? { firstColorClass: "scramble-white", secondColorClass: "scramble-inherit" } : { firstColorClass: "scramble-brand", secondColorClass: "scramble-inherit" };
|
|
1003
|
+
const scrambleConfig = {
|
|
1004
|
+
duration: 0.5,
|
|
1005
|
+
initiallyVisible: true,
|
|
1006
|
+
firstColorClass: themeColors.firstColorClass,
|
|
1007
|
+
secondColorClass: themeColors.secondColorClass
|
|
1008
|
+
};
|
|
1009
|
+
const scrambleResult = useDualLayerScramble(scrambleConfig);
|
|
1010
|
+
const isScramblingRef = useRef6(false);
|
|
1011
|
+
const handleMouseEnter = () => {
|
|
1012
|
+
if (!isScramblingRef.current) {
|
|
1013
|
+
isScramblingRef.current = true;
|
|
1014
|
+
scrambleResult.scramble();
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
const handleMouseLeave = () => {
|
|
1018
|
+
isScramblingRef.current = false;
|
|
1019
|
+
};
|
|
1020
|
+
const buttonClasses = buttonVariants({ variant, size, className });
|
|
1021
|
+
const renderContent = (content) => /* @__PURE__ */ jsxs4("span", { className: "relative flex w-full items-center gap-6", children: [
|
|
1022
|
+
/* @__PURE__ */ jsx6(
|
|
1023
|
+
"span",
|
|
1024
|
+
{
|
|
1025
|
+
className: cx(
|
|
1026
|
+
iconVariants({ size, theme, position: "left" }),
|
|
1027
|
+
"group-hover:rotate-0 group-hover:scale-100"
|
|
1028
|
+
),
|
|
1029
|
+
children: /* @__PURE__ */ jsx6(PlusIcon, {})
|
|
1030
|
+
}
|
|
1031
|
+
),
|
|
1032
|
+
/* @__PURE__ */ jsx6(
|
|
1033
|
+
"span",
|
|
1034
|
+
{
|
|
1035
|
+
className: cx(
|
|
1036
|
+
textWrapperVariants({ size, theme }),
|
|
1037
|
+
"group-hover:translate-x-0"
|
|
1038
|
+
),
|
|
1039
|
+
children: /* @__PURE__ */ jsx6("span", { ref: scrambleResult.ref, children: content })
|
|
1040
|
+
}
|
|
1041
|
+
),
|
|
1042
|
+
/* @__PURE__ */ jsx6(
|
|
1043
|
+
"span",
|
|
1044
|
+
{
|
|
1045
|
+
className: cx(
|
|
1046
|
+
iconVariants({ size, theme, position: "right" }),
|
|
1047
|
+
"group-hover:-rotate-45 group-hover:scale-0"
|
|
1048
|
+
),
|
|
1049
|
+
children: /* @__PURE__ */ jsx6(PlusIcon, {})
|
|
1050
|
+
}
|
|
1051
|
+
)
|
|
1052
|
+
] });
|
|
1053
|
+
return /* @__PURE__ */ jsx6(
|
|
1054
|
+
Comp,
|
|
1055
|
+
{
|
|
1056
|
+
type: resolvedType,
|
|
1057
|
+
className: buttonClasses,
|
|
1058
|
+
onMouseEnter: handleMouseEnter,
|
|
1059
|
+
onMouseLeave: handleMouseLeave,
|
|
1060
|
+
...rest,
|
|
1061
|
+
children: /* @__PURE__ */ jsx6(Slottable, { asChild, child: children, children: renderContent })
|
|
1062
|
+
}
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// packages/animations/AnimatedLink.jsx
|
|
1067
|
+
import React7, { forwardRef as forwardRef2 } from "react";
|
|
1068
|
+
import { Slot as Slot2, Slottable as Slottable2 } from "@radix-ui/react-slot";
|
|
1069
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1070
|
+
var AnimatedLink = forwardRef2(function AnimatedLink2(props, ref) {
|
|
1071
|
+
const {
|
|
1072
|
+
children,
|
|
1073
|
+
className,
|
|
1074
|
+
asChild,
|
|
1075
|
+
indicator,
|
|
1076
|
+
...restProps
|
|
1077
|
+
} = props;
|
|
1078
|
+
const Component = asChild ? Slot2 : "a";
|
|
1079
|
+
const displayClass = indicator ? "inline-flex items-center" : "inline-block";
|
|
1080
|
+
const combinedClassName = cx(
|
|
1081
|
+
"group relative w-fit cursor-pointer",
|
|
1082
|
+
displayClass,
|
|
1083
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-brand/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
1084
|
+
className
|
|
1085
|
+
);
|
|
1086
|
+
const renderChildren = (childContent) => /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1087
|
+
indicator && /* @__PURE__ */ jsx7(
|
|
1088
|
+
"span",
|
|
1089
|
+
{
|
|
1090
|
+
className: cx(
|
|
1091
|
+
"absolute left-0 size-8 bg-brand lg:size-12",
|
|
1092
|
+
"-rotate-90 scale-0",
|
|
1093
|
+
"transition-transform duration-500 [transition-timing-function:cubic-bezier(0.34,1.56,0.64,1)]",
|
|
1094
|
+
"group-hover:rotate-0 group-hover:scale-100",
|
|
1095
|
+
"group-focus-visible:rotate-0 group-focus-visible:scale-100"
|
|
1096
|
+
),
|
|
1097
|
+
"aria-hidden": "true"
|
|
1098
|
+
}
|
|
1099
|
+
),
|
|
1100
|
+
indicator ? /* @__PURE__ */ jsx7(
|
|
1101
|
+
"span",
|
|
1102
|
+
{
|
|
1103
|
+
className: cx(
|
|
1104
|
+
"transition-transform duration-500 [transition-timing-function:cubic-bezier(0.33,1,0.68,1)]",
|
|
1105
|
+
"group-hover:translate-x-24",
|
|
1106
|
+
"group-focus-visible:translate-x-24"
|
|
1107
|
+
),
|
|
1108
|
+
children: childContent
|
|
1109
|
+
}
|
|
1110
|
+
) : childContent,
|
|
1111
|
+
/* @__PURE__ */ jsxs5(
|
|
1112
|
+
"span",
|
|
1113
|
+
{
|
|
1114
|
+
className: cx(
|
|
1115
|
+
"pointer-events-none absolute inset-x-0 -bottom-1",
|
|
1116
|
+
indicator && [
|
|
1117
|
+
"transition-transform duration-500 [transition-timing-function:cubic-bezier(0.33,1,0.68,1)]",
|
|
1118
|
+
"group-hover:translate-x-24 group-focus-visible:translate-x-24"
|
|
1119
|
+
]
|
|
1120
|
+
),
|
|
1121
|
+
"aria-hidden": "true",
|
|
1122
|
+
children: [
|
|
1123
|
+
/* @__PURE__ */ jsx7(
|
|
1124
|
+
"span",
|
|
1125
|
+
{
|
|
1126
|
+
className: cx(
|
|
1127
|
+
"absolute inset-x-0 top-0 h-px bg-current",
|
|
1128
|
+
"origin-left scale-x-100",
|
|
1129
|
+
"transition-transform duration-700 [transition-timing-function:cubic-bezier(0.625,0.05,0,1)]",
|
|
1130
|
+
"delay-300 group-hover:origin-right group-hover:scale-x-0 group-hover:delay-0",
|
|
1131
|
+
"group-focus-visible:origin-right group-focus-visible:scale-x-0 group-focus-visible:delay-0"
|
|
1132
|
+
)
|
|
1133
|
+
}
|
|
1134
|
+
),
|
|
1135
|
+
/* @__PURE__ */ jsx7(
|
|
1136
|
+
"span",
|
|
1137
|
+
{
|
|
1138
|
+
className: cx(
|
|
1139
|
+
"absolute inset-x-0 top-0 h-px bg-current",
|
|
1140
|
+
"origin-right scale-x-0",
|
|
1141
|
+
"transition-transform duration-700 [transition-timing-function:cubic-bezier(0.625,0.05,0,1)]",
|
|
1142
|
+
"delay-0 group-hover:origin-left group-hover:scale-x-100 group-hover:delay-300",
|
|
1143
|
+
"group-focus-visible:origin-left group-focus-visible:scale-x-100 group-focus-visible:delay-300"
|
|
1144
|
+
)
|
|
1145
|
+
}
|
|
1146
|
+
)
|
|
1147
|
+
]
|
|
1148
|
+
}
|
|
1149
|
+
)
|
|
1150
|
+
] });
|
|
1151
|
+
return /* @__PURE__ */ jsx7(Component, { ref, className: combinedClassName, ...restProps, children: /* @__PURE__ */ jsx7(Slottable2, { asChild, child: children, children: renderChildren }) });
|
|
1152
|
+
});
|
|
1153
|
+
var AnimatedLink_default = AnimatedLink;
|
|
1154
|
+
|
|
1155
|
+
// packages/animations/AnimatedProse.jsx
|
|
1156
|
+
import React8, { createContext as createContext3, useState as useState5, useRef as useRef7, useEffect as useEffect6 } from "react";
|
|
1157
|
+
import { motion, animate } from "framer-motion";
|
|
1158
|
+
|
|
1159
|
+
// libs/utils/easings.js
|
|
1160
|
+
function cubicBezier(x1, y1, x2, y2) {
|
|
1161
|
+
const cx2 = 3 * x1;
|
|
1162
|
+
const bx = 3 * (x2 - x1) - cx2;
|
|
1163
|
+
const ax = 1 - cx2 - bx;
|
|
1164
|
+
const cy = 3 * y1;
|
|
1165
|
+
const by = 3 * (y2 - y1) - cy;
|
|
1166
|
+
const ay = 1 - cy - by;
|
|
1167
|
+
function sampleCurveX(t) {
|
|
1168
|
+
return ((ax * t + bx) * t + cx2) * t;
|
|
1169
|
+
}
|
|
1170
|
+
function sampleCurveY(t) {
|
|
1171
|
+
return ((ay * t + by) * t + cy) * t;
|
|
1172
|
+
}
|
|
1173
|
+
function sampleCurveDerivativeX(t) {
|
|
1174
|
+
return (3 * ax * t + 2 * bx) * t + cx2;
|
|
1175
|
+
}
|
|
1176
|
+
function solveCurveX(x, epsilon = 1e-6) {
|
|
1177
|
+
let t = x;
|
|
1178
|
+
for (let i = 0; i < 8; i++) {
|
|
1179
|
+
const currentX = sampleCurveX(t) - x;
|
|
1180
|
+
if (Math.abs(currentX) < epsilon) return t;
|
|
1181
|
+
const currentSlope = sampleCurveDerivativeX(t);
|
|
1182
|
+
if (Math.abs(currentSlope) < epsilon) break;
|
|
1183
|
+
t -= currentX / currentSlope;
|
|
1184
|
+
}
|
|
1185
|
+
let lower = 0;
|
|
1186
|
+
let upper = 1;
|
|
1187
|
+
t = x;
|
|
1188
|
+
if (t < lower) return lower;
|
|
1189
|
+
if (t > upper) return upper;
|
|
1190
|
+
while (lower < upper) {
|
|
1191
|
+
const currentX = sampleCurveX(t);
|
|
1192
|
+
if (Math.abs(currentX - x) < epsilon) return t;
|
|
1193
|
+
if (x > currentX) {
|
|
1194
|
+
lower = t;
|
|
1195
|
+
} else {
|
|
1196
|
+
upper = t;
|
|
1197
|
+
}
|
|
1198
|
+
t = (upper - lower) / 2 + lower;
|
|
1199
|
+
}
|
|
1200
|
+
return t;
|
|
1201
|
+
}
|
|
1202
|
+
return function bezierEase(t) {
|
|
1203
|
+
if (t <= 0) return 0;
|
|
1204
|
+
if (t >= 1) return 1;
|
|
1205
|
+
return sampleCurveY(solveCurveX(t));
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
var NAMED_EASINGS = {
|
|
1209
|
+
linear: (t) => t,
|
|
1210
|
+
easeIn: cubicBezier(0.42, 0, 1, 1),
|
|
1211
|
+
easeOut: cubicBezier(0, 0, 0.58, 1),
|
|
1212
|
+
easeInOut: cubicBezier(0.42, 0, 0.58, 1),
|
|
1213
|
+
circIn: cubicBezier(0.55, 0, 1, 0.45),
|
|
1214
|
+
circOut: cubicBezier(0, 0.55, 0.45, 1),
|
|
1215
|
+
circInOut: cubicBezier(0.85, 0, 0.15, 1),
|
|
1216
|
+
backIn: cubicBezier(0.36, 0, 0.66, -0.56),
|
|
1217
|
+
backOut: cubicBezier(0.34, 1.56, 0.64, 1),
|
|
1218
|
+
backInOut: cubicBezier(0.68, -0.6, 0.32, 1.6),
|
|
1219
|
+
anticipate: cubicBezier(0.36, 0, 0.66, -0.56)
|
|
1220
|
+
};
|
|
1221
|
+
function easingDefinitionToFunction(definition) {
|
|
1222
|
+
if (typeof definition === "function") {
|
|
1223
|
+
return definition;
|
|
1224
|
+
}
|
|
1225
|
+
if (Array.isArray(definition) && definition.length === 4) {
|
|
1226
|
+
const [x1, y1, x2, y2] = definition;
|
|
1227
|
+
return cubicBezier(x1, y1, x2, y2);
|
|
1228
|
+
}
|
|
1229
|
+
if (typeof definition === "string" && NAMED_EASINGS[definition]) {
|
|
1230
|
+
return NAMED_EASINGS[definition];
|
|
1231
|
+
}
|
|
1232
|
+
return NAMED_EASINGS.linear;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// packages/animations/AnimatedProse.jsx
|
|
1236
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1237
|
+
function stagger(amount = 0.1, { startDelay = 0, from = 0, ease } = {}) {
|
|
1238
|
+
return (index, totalElements) => {
|
|
1239
|
+
let fromVal = typeof from === "number" ? from : (function(fromStr, total) {
|
|
1240
|
+
if (fromStr === "first") return 0;
|
|
1241
|
+
let lastIndex = total - 1;
|
|
1242
|
+
return fromStr === "last" ? lastIndex : lastIndex / 2;
|
|
1243
|
+
})(from, totalElements);
|
|
1244
|
+
let distance = amount * Math.abs(fromVal - index);
|
|
1245
|
+
if (ease) {
|
|
1246
|
+
let maxStagger = totalElements * amount;
|
|
1247
|
+
distance = easingDefinitionToFunction(ease)(distance / maxStagger) * maxStagger;
|
|
1248
|
+
}
|
|
1249
|
+
return startDelay + distance;
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
var AnimatedProseContext = createContext3(false);
|
|
1253
|
+
function AnimatedProse({
|
|
1254
|
+
children,
|
|
1255
|
+
className,
|
|
1256
|
+
staggerDelay = 0.03,
|
|
1257
|
+
duration = 0.5,
|
|
1258
|
+
delay = 0,
|
|
1259
|
+
margin = "0px 0px -10% 0px"
|
|
1260
|
+
}) {
|
|
1261
|
+
const [isInView, setIsInView] = useState5(false);
|
|
1262
|
+
const [hasSplitLines, setHasSplitLines] = useState5(false);
|
|
1263
|
+
const containerRef = useRef7(null);
|
|
1264
|
+
const hasAnimatedRef = useRef7(false);
|
|
1265
|
+
useEffect6(() => {
|
|
1266
|
+
if (!containerRef.current) return;
|
|
1267
|
+
const checkSplitLines = () => {
|
|
1268
|
+
var _a;
|
|
1269
|
+
const lines = (_a = containerRef.current) == null ? void 0 : _a.querySelectorAll(".split-line");
|
|
1270
|
+
if (lines && lines.length > 0) {
|
|
1271
|
+
setHasSplitLines(true);
|
|
1272
|
+
return true;
|
|
1273
|
+
}
|
|
1274
|
+
return false;
|
|
1275
|
+
};
|
|
1276
|
+
if (checkSplitLines()) return;
|
|
1277
|
+
const intervalId = setInterval(() => {
|
|
1278
|
+
if (checkSplitLines()) {
|
|
1279
|
+
clearInterval(intervalId);
|
|
1280
|
+
}
|
|
1281
|
+
}, 50);
|
|
1282
|
+
const timeoutId = setTimeout(() => {
|
|
1283
|
+
clearInterval(intervalId);
|
|
1284
|
+
setHasSplitLines(true);
|
|
1285
|
+
}, 500);
|
|
1286
|
+
return () => {
|
|
1287
|
+
clearInterval(intervalId);
|
|
1288
|
+
clearTimeout(timeoutId);
|
|
1289
|
+
};
|
|
1290
|
+
}, []);
|
|
1291
|
+
useEffect6(() => {
|
|
1292
|
+
if (!isInView || !hasSplitLines || !containerRef.current || hasAnimatedRef.current) return;
|
|
1293
|
+
const lines = containerRef.current.querySelectorAll(".split-line");
|
|
1294
|
+
if (lines.length === 0) return;
|
|
1295
|
+
hasAnimatedRef.current = true;
|
|
1296
|
+
containerRef.current.querySelectorAll(".invisible").forEach(makeVisible);
|
|
1297
|
+
const animation = animate(
|
|
1298
|
+
Array.from(lines),
|
|
1299
|
+
{ y: ["100%", "0%"] },
|
|
1300
|
+
{
|
|
1301
|
+
delay: stagger(staggerDelay, { startDelay: delay }),
|
|
1302
|
+
duration,
|
|
1303
|
+
ease: [0.33, 1, 0.68, 1]
|
|
1304
|
+
}
|
|
1305
|
+
);
|
|
1306
|
+
return () => {
|
|
1307
|
+
animation == null ? void 0 : animation.cancel();
|
|
1308
|
+
};
|
|
1309
|
+
}, [isInView, hasSplitLines, staggerDelay, duration, delay]);
|
|
1310
|
+
const handleViewportEnter = () => setIsInView(true);
|
|
1311
|
+
return /* @__PURE__ */ jsx8(AnimatedProseContext.Provider, { value: true, children: /* @__PURE__ */ jsx8(
|
|
1312
|
+
motion.div,
|
|
1313
|
+
{
|
|
1314
|
+
ref: containerRef,
|
|
1315
|
+
onViewportEnter: handleViewportEnter,
|
|
1316
|
+
viewport: { once: true, margin },
|
|
1317
|
+
className: cx(className),
|
|
1318
|
+
children
|
|
1319
|
+
}
|
|
1320
|
+
) });
|
|
1321
|
+
}
|
|
1322
|
+
function makeVisible(el) {
|
|
1323
|
+
el.style.visibility = "visible";
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
// packages/animations/AnimatedSubtext.jsx
|
|
1327
|
+
import React9, { forwardRef as forwardRef3, useRef as useRef8, useState as useState6, useLayoutEffect as useLayoutEffect2, useImperativeHandle as useImperativeHandle2 } from "react";
|
|
1328
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1329
|
+
var AnimatedSubtext = forwardRef3(({
|
|
1330
|
+
children,
|
|
1331
|
+
className,
|
|
1332
|
+
skip = false,
|
|
1333
|
+
staggerDelay = 0.05,
|
|
1334
|
+
duration = 0.8
|
|
1335
|
+
}, ref) => {
|
|
1336
|
+
const elementRef = useRef8(null);
|
|
1337
|
+
const splitTextRef = useRef8(null);
|
|
1338
|
+
const [isSplit, setIsSplit] = useState6(false);
|
|
1339
|
+
useLayoutEffect2(() => {
|
|
1340
|
+
if (elementRef.current && !skip) {
|
|
1341
|
+
setIsSplit(false);
|
|
1342
|
+
splitTextRef.current = new SplitText(elementRef.current, {
|
|
1343
|
+
type: "lines",
|
|
1344
|
+
autoSplit: true,
|
|
1345
|
+
aria: false,
|
|
1346
|
+
deepSlice: true,
|
|
1347
|
+
reduceWhiteSpace: false,
|
|
1348
|
+
mask: "lines",
|
|
1349
|
+
linesClass: "split-line",
|
|
1350
|
+
onSplit: () => setIsSplit(true)
|
|
1351
|
+
});
|
|
1352
|
+
return () => {
|
|
1353
|
+
var _a;
|
|
1354
|
+
(_a = splitTextRef.current) == null ? void 0 : _a.revert();
|
|
1355
|
+
splitTextRef.current = null;
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
}, [skip]);
|
|
1359
|
+
useImperativeHandle2(ref, () => ({
|
|
1360
|
+
reveal: (delay = 0) => {
|
|
1361
|
+
if (!elementRef.current || !splitTextRef.current || skip) return;
|
|
1362
|
+
const lines = splitTextRef.current.lines ?? [];
|
|
1363
|
+
if (lines.length !== 0) {
|
|
1364
|
+
elementRef.current.style.visibility = "visible";
|
|
1365
|
+
gsap.fromTo(
|
|
1366
|
+
lines,
|
|
1367
|
+
{ y: "100%" },
|
|
1368
|
+
{
|
|
1369
|
+
y: "0%",
|
|
1370
|
+
duration,
|
|
1371
|
+
ease: "power3.out",
|
|
1372
|
+
stagger: staggerDelay,
|
|
1373
|
+
delay
|
|
1374
|
+
}
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}), [skip, duration, staggerDelay]);
|
|
1379
|
+
const renderWithLineBreaks = (text) => {
|
|
1380
|
+
const lines = text.split("\n");
|
|
1381
|
+
return lines.map((line, index) => /* @__PURE__ */ jsxs6("span", { children: [
|
|
1382
|
+
line,
|
|
1383
|
+
index < lines.length - 1 && /* @__PURE__ */ jsx9("br", {})
|
|
1384
|
+
] }, index));
|
|
1385
|
+
};
|
|
1386
|
+
return skip ? /* @__PURE__ */ jsx9("p", { className, children: renderWithLineBreaks(children) }) : /* @__PURE__ */ jsx9("p", { ref: elementRef, className: cx("invisible", className), children: renderWithLineBreaks(children) });
|
|
1387
|
+
});
|
|
1388
|
+
var AnimatedSubtext_default = AnimatedSubtext;
|
|
1389
|
+
|
|
1390
|
+
// packages/animations/AnimatedText.jsx
|
|
1391
|
+
import React10, { useState as useState7, useRef as useRef9, useContext as useContext3, useLayoutEffect as useLayoutEffect3, useEffect as useEffect7 } from "react";
|
|
1392
|
+
import { motion as motion2, animate as animate2 } from "framer-motion";
|
|
1393
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1394
|
+
function AnimatedText({
|
|
1395
|
+
children,
|
|
1396
|
+
className,
|
|
1397
|
+
staggerDelay = 0.03,
|
|
1398
|
+
duration = 0.5,
|
|
1399
|
+
delay = 0,
|
|
1400
|
+
margin = "0px 0px -10% 0px",
|
|
1401
|
+
revert = false
|
|
1402
|
+
}) {
|
|
1403
|
+
const [isInView, setIsInView] = useState7(false);
|
|
1404
|
+
const [isSplit, setIsSplit] = useState7(false);
|
|
1405
|
+
const containerRef = useRef9(null);
|
|
1406
|
+
const splitTextRef = useRef9(null);
|
|
1407
|
+
const inAnimatedProse = useContext3(AnimatedProseContext);
|
|
1408
|
+
useLayoutEffect3(() => {
|
|
1409
|
+
if (containerRef.current) {
|
|
1410
|
+
setIsSplit(false);
|
|
1411
|
+
splitTextRef.current = new SplitText(containerRef.current, {
|
|
1412
|
+
type: "lines",
|
|
1413
|
+
autoSplit: true,
|
|
1414
|
+
aria: false,
|
|
1415
|
+
deepSlice: true,
|
|
1416
|
+
reduceWhiteSpace: false,
|
|
1417
|
+
mask: "lines",
|
|
1418
|
+
linesClass: "split-line",
|
|
1419
|
+
onSplit: () => setIsSplit(true)
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
return () => {
|
|
1423
|
+
splitTextRef.current = null;
|
|
1424
|
+
};
|
|
1425
|
+
}, []);
|
|
1426
|
+
useEffect7(() => {
|
|
1427
|
+
if (inAnimatedProse || !isInView || !isSplit || !containerRef.current || !splitTextRef.current) {
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
const lines = splitTextRef.current.lines ?? [];
|
|
1431
|
+
if (lines.length === 0) return;
|
|
1432
|
+
containerRef.current.style.visibility = "visible";
|
|
1433
|
+
const animation = animate2(
|
|
1434
|
+
lines,
|
|
1435
|
+
{ y: ["100%", "0%"] },
|
|
1436
|
+
{
|
|
1437
|
+
delay: stagger(staggerDelay, { startDelay: delay }),
|
|
1438
|
+
duration,
|
|
1439
|
+
ease: [0.33, 1, 0.68, 1]
|
|
1440
|
+
}
|
|
1441
|
+
);
|
|
1442
|
+
const handleRevert = async () => {
|
|
1443
|
+
var _a;
|
|
1444
|
+
try {
|
|
1445
|
+
await animation;
|
|
1446
|
+
if (revert) {
|
|
1447
|
+
(_a = splitTextRef.current) == null ? void 0 : _a.revert();
|
|
1448
|
+
}
|
|
1449
|
+
} catch (err) {
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
handleRevert();
|
|
1453
|
+
return () => {
|
|
1454
|
+
animation == null ? void 0 : animation.cancel();
|
|
1455
|
+
};
|
|
1456
|
+
}, [isInView, isSplit, inAnimatedProse, staggerDelay, duration, delay, revert]);
|
|
1457
|
+
const handleViewportEnter = () => setIsInView(true);
|
|
1458
|
+
return /* @__PURE__ */ jsx10(
|
|
1459
|
+
motion2.span,
|
|
1460
|
+
{
|
|
1461
|
+
ref: containerRef,
|
|
1462
|
+
onViewportEnter: handleViewportEnter,
|
|
1463
|
+
viewport: { once: true, margin },
|
|
1464
|
+
className: cx("invisible", className),
|
|
1465
|
+
children
|
|
1466
|
+
}
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// packages/animations/ScrollAnimatedHeadline.jsx
|
|
1471
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1472
|
+
function ScrollAnimatedHeadline({
|
|
1473
|
+
headline,
|
|
1474
|
+
displayAs,
|
|
1475
|
+
className,
|
|
1476
|
+
headlineClassName
|
|
1477
|
+
}) {
|
|
1478
|
+
if (!(headline == null ? void 0 : headline.text) || !(headline == null ? void 0 : headline.level)) return null;
|
|
1479
|
+
return /* @__PURE__ */ jsx11(
|
|
1480
|
+
AnimatedHeadline_default,
|
|
1481
|
+
{
|
|
1482
|
+
trigger: "scroll",
|
|
1483
|
+
as: headline.level,
|
|
1484
|
+
displayAs,
|
|
1485
|
+
className: cx(headlineClassName),
|
|
1486
|
+
wrapperClassName: className,
|
|
1487
|
+
children: headline.text
|
|
1488
|
+
}
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
// packages/animations/Preloader.jsx
|
|
1493
|
+
import React12, { useRef as useRef10, useState as useState9, useEffect as useEffect9 } from "react";
|
|
1494
|
+
|
|
1495
|
+
// packages/providers/PreloaderProvider.jsx
|
|
1496
|
+
import React11, { createContext as createContext4, useState as useState8, useEffect as useEffect8, useContext as useContext4, useMemo as useMemo5 } from "react";
|
|
1497
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1498
|
+
var PreloaderContext = createContext4(null);
|
|
1499
|
+
var defaultContextValue2 = {
|
|
1500
|
+
phase: "hidden",
|
|
1501
|
+
setPhase: () => {
|
|
1502
|
+
},
|
|
1503
|
+
isInitialLoad: false
|
|
1504
|
+
};
|
|
1505
|
+
function PreloaderProvider({ children }) {
|
|
1506
|
+
const [phase, setPhase] = useState8("loading");
|
|
1507
|
+
const [isInitialLoad, setIsInitialLoad] = useState8(true);
|
|
1508
|
+
useEffect8(() => {
|
|
1509
|
+
if (phase === "revealing") {
|
|
1510
|
+
const timer = setTimeout(() => {
|
|
1511
|
+
setPhase("hidden");
|
|
1512
|
+
setIsInitialLoad(false);
|
|
1513
|
+
}, 200);
|
|
1514
|
+
return () => clearTimeout(timer);
|
|
1515
|
+
}
|
|
1516
|
+
}, [phase]);
|
|
1517
|
+
const contextValue = useMemo5(() => ({
|
|
1518
|
+
phase,
|
|
1519
|
+
setPhase,
|
|
1520
|
+
isInitialLoad
|
|
1521
|
+
}), [phase, isInitialLoad]);
|
|
1522
|
+
return /* @__PURE__ */ jsx12(PreloaderContext.Provider, { value: contextValue, children });
|
|
1523
|
+
}
|
|
1524
|
+
function usePreloader() {
|
|
1525
|
+
return useContext4(PreloaderContext) ?? defaultContextValue2;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
// packages/animations/Preloader.jsx
|
|
1529
|
+
import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1530
|
+
function Preloader() {
|
|
1531
|
+
const { phase, setPhase, isInitialLoad } = usePreloader();
|
|
1532
|
+
const containerRef = useRef10(null);
|
|
1533
|
+
const contentRef = useRef10(null);
|
|
1534
|
+
const squaresContainerRef = useRef10(null);
|
|
1535
|
+
const squaresRefs = useRef10([]);
|
|
1536
|
+
const onReadyRef = useRef10(null);
|
|
1537
|
+
const timelineRef = useRef10(null);
|
|
1538
|
+
const hasStartedRef = useRef10(false);
|
|
1539
|
+
const [isReducedMotion, setIsReducedMotion] = useState9(false);
|
|
1540
|
+
useEffect9(() => {
|
|
1541
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
1542
|
+
setIsReducedMotion(mediaQuery.matches);
|
|
1543
|
+
const handleChange = (e) => setIsReducedMotion(e.matches);
|
|
1544
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
1545
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
1546
|
+
}, []);
|
|
1547
|
+
const handleReady = (readyFn) => {
|
|
1548
|
+
onReadyRef.current = readyFn;
|
|
1549
|
+
};
|
|
1550
|
+
useGSAP(() => {
|
|
1551
|
+
if (!isInitialLoad || phase !== "loading" || hasStartedRef.current) return;
|
|
1552
|
+
if (isReducedMotion) {
|
|
1553
|
+
setPhase("complete");
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
const squares = squaresRefs.current.filter(Boolean);
|
|
1557
|
+
if (squares.length === 0) return;
|
|
1558
|
+
hasStartedRef.current = true;
|
|
1559
|
+
let timeoutId;
|
|
1560
|
+
const runAnimation = () => {
|
|
1561
|
+
var _a, _b;
|
|
1562
|
+
if (!onReadyRef.current) {
|
|
1563
|
+
requestAnimationFrame(runAnimation);
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
timelineRef.current = gsap.timeline();
|
|
1567
|
+
onReadyRef.current();
|
|
1568
|
+
squares.forEach((square, index) => {
|
|
1569
|
+
var _a2;
|
|
1570
|
+
(_a2 = timelineRef.current) == null ? void 0 : _a2.fromTo(
|
|
1571
|
+
square,
|
|
1572
|
+
{
|
|
1573
|
+
x: index === 0 ? -16 : (index - 1) * 18,
|
|
1574
|
+
rotate: 0
|
|
1575
|
+
},
|
|
1576
|
+
{
|
|
1577
|
+
x: index * 18 - 16,
|
|
1578
|
+
rotate: 90,
|
|
1579
|
+
duration: 0.7,
|
|
1580
|
+
ease: "expo.inOut",
|
|
1581
|
+
immediateRender: false
|
|
1582
|
+
},
|
|
1583
|
+
index === 0 ? 0 : ">-25%"
|
|
1584
|
+
);
|
|
1585
|
+
});
|
|
1586
|
+
const timeOffset = 2.2749999999999995;
|
|
1587
|
+
(_a = timelineRef.current) == null ? void 0 : _a.to(
|
|
1588
|
+
contentRef.current,
|
|
1589
|
+
{ opacity: 0, duration: 0.4, ease: "power3.out" },
|
|
1590
|
+
timeOffset + 0.2
|
|
1591
|
+
);
|
|
1592
|
+
const clipState = { value: 0 };
|
|
1593
|
+
const eventState = { value: false };
|
|
1594
|
+
(_b = timelineRef.current) == null ? void 0 : _b.to(
|
|
1595
|
+
clipState,
|
|
1596
|
+
{
|
|
1597
|
+
value: 1,
|
|
1598
|
+
duration: 1.5,
|
|
1599
|
+
ease: "expo.inOut",
|
|
1600
|
+
onUpdate: () => {
|
|
1601
|
+
if (containerRef.current) {
|
|
1602
|
+
const val = clipState.value;
|
|
1603
|
+
containerRef.current.style.clipPath = val <= 0 ? "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)" : val >= 1 ? "polygon(0% 100%, 0% 100%, 0% 100%)" : val <= 0.5 ? `polygon(0% 100%, ${2 * val * 100}% 0%, 100% 0%, 100% 100%)` : `polygon(0% 100%, 100% ${(val - 0.5) * 200}%, 100% 100%)`;
|
|
1604
|
+
}
|
|
1605
|
+
if (!eventState.value && clipState.value >= 0.9) {
|
|
1606
|
+
eventState.value = true;
|
|
1607
|
+
setPhase("revealing");
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
},
|
|
1611
|
+
timeOffset
|
|
1612
|
+
);
|
|
1613
|
+
};
|
|
1614
|
+
timeoutId = setTimeout(runAnimation, 100);
|
|
1615
|
+
return () => {
|
|
1616
|
+
var _a;
|
|
1617
|
+
clearTimeout(timeoutId);
|
|
1618
|
+
(_a = timelineRef.current) == null ? void 0 : _a.kill();
|
|
1619
|
+
};
|
|
1620
|
+
}, { dependencies: [isInitialLoad, isReducedMotion, setPhase] });
|
|
1621
|
+
if (!isInitialLoad || phase === "hidden") return null;
|
|
1622
|
+
const pointerEventsClass = phase === "complete" ? "pointer-events-none" : "";
|
|
1623
|
+
const containerClasses = cx("fixed inset-0 z-[10000] flex items-center justify-center bg-background", pointerEventsClass);
|
|
1624
|
+
return /* @__PURE__ */ jsx13("div", { ref: containerRef, "data-theme": "brand", className: containerClasses, children: /* @__PURE__ */ jsxs7("div", { ref: contentRef, className: "flex flex-col items-center gap-4", children: [
|
|
1625
|
+
/* @__PURE__ */ jsx13("div", { children: /* @__PURE__ */ jsx13(
|
|
1626
|
+
"div",
|
|
1627
|
+
{
|
|
1628
|
+
ref: squaresContainerRef,
|
|
1629
|
+
className: "relative overflow-x-clip overflow-y-visible",
|
|
1630
|
+
style: { width: 70, height: 16 },
|
|
1631
|
+
children: [0, 1, 2, 3].map((index) => /* @__PURE__ */ jsx13(
|
|
1632
|
+
"div",
|
|
1633
|
+
{
|
|
1634
|
+
ref: (el) => {
|
|
1635
|
+
squaresRefs.current[index] = el;
|
|
1636
|
+
},
|
|
1637
|
+
className: "absolute top-0 left-0 bg-foreground",
|
|
1638
|
+
style: {
|
|
1639
|
+
width: 16,
|
|
1640
|
+
height: 16,
|
|
1641
|
+
transform: "translateX(-16px)",
|
|
1642
|
+
transformOrigin: "bottom right"
|
|
1643
|
+
}
|
|
1644
|
+
},
|
|
1645
|
+
index
|
|
1646
|
+
))
|
|
1647
|
+
}
|
|
1648
|
+
) }),
|
|
1649
|
+
/* @__PURE__ */ jsx13("div", { children: /* @__PURE__ */ jsx13("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx13(ScrambleText, { revealMode: true, duration: 1, onReady: handleReady, children: "LOADING" }) }) })
|
|
1650
|
+
] }) });
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// packages/providers/PageEnterProvider.jsx
|
|
1654
|
+
import React13, {
|
|
1655
|
+
createContext as createContext5,
|
|
1656
|
+
useState as useState10,
|
|
1657
|
+
useRef as useRef11,
|
|
1658
|
+
useEffect as useEffect10,
|
|
1659
|
+
useCallback as useCallback6,
|
|
1660
|
+
useMemo as useMemo6,
|
|
1661
|
+
useContext as useContext5
|
|
1662
|
+
} from "react";
|
|
1663
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1664
|
+
var PageEnterContext = createContext5(null);
|
|
1665
|
+
function sortByPriority(a, b) {
|
|
1666
|
+
return a.priority - b.priority;
|
|
1667
|
+
}
|
|
1668
|
+
function PageEnterProvider({ children }) {
|
|
1669
|
+
const { phase: pageTransitionPhase } = usePageTransition();
|
|
1670
|
+
const { phase: preloaderPhase } = usePreloader();
|
|
1671
|
+
const [phase, setPhase] = useState10("waiting");
|
|
1672
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = useState10(false);
|
|
1673
|
+
const callbacksMapRef = useRef11(/* @__PURE__ */ new Map());
|
|
1674
|
+
const isTriggeringRef = useRef11(false);
|
|
1675
|
+
useEffect10(() => {
|
|
1676
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
1677
|
+
setPrefersReducedMotion(mediaQuery.matches);
|
|
1678
|
+
const handleMediaQueryChange = (e) => setPrefersReducedMotion(e.matches);
|
|
1679
|
+
mediaQuery.addEventListener("change", handleMediaQueryChange);
|
|
1680
|
+
return () => {
|
|
1681
|
+
mediaQuery.removeEventListener("change", handleMediaQueryChange);
|
|
1682
|
+
};
|
|
1683
|
+
}, []);
|
|
1684
|
+
const register = useCallback6((id, trigger, priority = 0) => {
|
|
1685
|
+
callbacksMapRef.current.set(id, { id, trigger, priority });
|
|
1686
|
+
}, []);
|
|
1687
|
+
const unregister = useCallback6((id) => {
|
|
1688
|
+
callbacksMapRef.current.delete(id);
|
|
1689
|
+
}, []);
|
|
1690
|
+
const triggerEnter = useCallback6(() => {
|
|
1691
|
+
if (isTriggeringRef.current) {
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
isTriggeringRef.current = true;
|
|
1695
|
+
setPhase("entering");
|
|
1696
|
+
const callbacks = Array.from(callbacksMapRef.current.values());
|
|
1697
|
+
callbacks.sort(sortByPriority);
|
|
1698
|
+
let lastPriority = null;
|
|
1699
|
+
let staggerIndex = 0;
|
|
1700
|
+
for (const item of callbacks) {
|
|
1701
|
+
if (lastPriority !== null && item.priority > lastPriority) {
|
|
1702
|
+
staggerIndex += 0.08;
|
|
1703
|
+
}
|
|
1704
|
+
lastPriority = item.priority;
|
|
1705
|
+
item.trigger(staggerIndex);
|
|
1706
|
+
}
|
|
1707
|
+
setTimeout(() => {
|
|
1708
|
+
setPhase("complete");
|
|
1709
|
+
}, 1e3 * (staggerIndex + 1));
|
|
1710
|
+
}, []);
|
|
1711
|
+
useEffect10(() => {
|
|
1712
|
+
const isPreloaderReady = preloaderPhase === "revealing" || preloaderPhase === "hidden";
|
|
1713
|
+
if ((pageTransitionPhase === "exiting" || pageTransitionPhase === "idle") && phase === "waiting" && isPreloaderReady) {
|
|
1714
|
+
const timerId = setTimeout(() => {
|
|
1715
|
+
triggerEnter();
|
|
1716
|
+
}, 250);
|
|
1717
|
+
return () => clearTimeout(timerId);
|
|
1718
|
+
}
|
|
1719
|
+
}, [pageTransitionPhase, preloaderPhase, phase, triggerEnter]);
|
|
1720
|
+
useEffect10(() => {
|
|
1721
|
+
if (pageTransitionPhase === "entering" || pageTransitionPhase === "holding") {
|
|
1722
|
+
setPhase("waiting");
|
|
1723
|
+
isTriggeringRef.current = false;
|
|
1724
|
+
}
|
|
1725
|
+
}, [pageTransitionPhase]);
|
|
1726
|
+
const contextValue = useMemo6(() => ({
|
|
1727
|
+
phase,
|
|
1728
|
+
register,
|
|
1729
|
+
unregister,
|
|
1730
|
+
prefersReducedMotion
|
|
1731
|
+
}), [phase, register, unregister, prefersReducedMotion]);
|
|
1732
|
+
return /* @__PURE__ */ jsx14(PageEnterContext.Provider, { value: contextValue, children });
|
|
1733
|
+
}
|
|
1734
|
+
var defaultContextValue3 = {
|
|
1735
|
+
phase: "complete",
|
|
1736
|
+
register: () => {
|
|
1737
|
+
},
|
|
1738
|
+
unregister: () => {
|
|
1739
|
+
},
|
|
1740
|
+
prefersReducedMotion: false
|
|
1741
|
+
};
|
|
1742
|
+
function usePageEnterContext() {
|
|
1743
|
+
return useContext5(PageEnterContext) ?? defaultContextValue3;
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// libs/hooks/usePageEnter.js
|
|
1747
|
+
import { useRef as useRef12, useEffect as useEffect11, useMemo as useMemo7 } from "react";
|
|
1748
|
+
function usePageEnter(callback, options = {}) {
|
|
1749
|
+
const { priority = 0, skip = false } = options;
|
|
1750
|
+
const {
|
|
1751
|
+
register,
|
|
1752
|
+
unregister,
|
|
1753
|
+
phase,
|
|
1754
|
+
prefersReducedMotion
|
|
1755
|
+
} = usePageEnterContext();
|
|
1756
|
+
const uuid = useMemo7(() => crypto.randomUUID(), []);
|
|
1757
|
+
const idRef = useRef12(uuid);
|
|
1758
|
+
useEffect11(() => {
|
|
1759
|
+
if (skip) {
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1762
|
+
const currentId = idRef.current;
|
|
1763
|
+
register(currentId, callback, priority);
|
|
1764
|
+
return () => {
|
|
1765
|
+
unregister(currentId);
|
|
1766
|
+
};
|
|
1767
|
+
}, [register, unregister, callback, priority, skip]);
|
|
1768
|
+
const isEntering = phase === "entering";
|
|
1769
|
+
const isComplete = phase === "complete";
|
|
1770
|
+
const returnedState = useMemo7(() => ({
|
|
1771
|
+
phase,
|
|
1772
|
+
prefersReducedMotion,
|
|
1773
|
+
isEntering,
|
|
1774
|
+
isComplete
|
|
1775
|
+
}), [phase, prefersReducedMotion, isEntering, isComplete]);
|
|
1776
|
+
return returnedState;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// libs/hooks/useBreakpoint.js
|
|
1780
|
+
import { useEffect as useEffect12, useState as useState11 } from "react";
|
|
1781
|
+
var BREAKPOINTS = {
|
|
1782
|
+
sm: 640,
|
|
1783
|
+
md: 768,
|
|
1784
|
+
lg: 1024,
|
|
1785
|
+
xl: 1280,
|
|
1786
|
+
"2xl": 1536
|
|
1787
|
+
};
|
|
1788
|
+
function useBreakpoint(breakpoint = "md") {
|
|
1789
|
+
const minWidth = BREAKPOINTS[breakpoint] ?? BREAKPOINTS.md;
|
|
1790
|
+
const [matches, setMatches] = useState11(() => {
|
|
1791
|
+
if (typeof window === "undefined") return false;
|
|
1792
|
+
return window.matchMedia(`(min-width: ${minWidth}px)`).matches;
|
|
1793
|
+
});
|
|
1794
|
+
useEffect12(() => {
|
|
1795
|
+
const mediaQuery = window.matchMedia(`(min-width: ${minWidth}px)`);
|
|
1796
|
+
const handleChange = (event) => setMatches(event.matches);
|
|
1797
|
+
setMatches(mediaQuery.matches);
|
|
1798
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
1799
|
+
return () => {
|
|
1800
|
+
mediaQuery.removeEventListener("change", handleChange);
|
|
1801
|
+
};
|
|
1802
|
+
}, [minWidth]);
|
|
1803
|
+
return matches;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
// libs/constants/timing.js
|
|
1807
|
+
var ASCII_REVEAL_DURATION = 1.2;
|
|
1808
|
+
|
|
1809
|
+
// libs/hooks/useAsciiDelay.js
|
|
1810
|
+
var asciiDelayBase = 0.1 * ASCII_REVEAL_DURATION;
|
|
1811
|
+
function useAsciiDelay() {
|
|
1812
|
+
const md = useBreakpoint("md");
|
|
1813
|
+
return md ? asciiDelayBase : 0;
|
|
1814
|
+
}
|
|
1815
|
+
export {
|
|
1816
|
+
AnimatedButton,
|
|
1817
|
+
AnimatedHeadline_default as AnimatedHeadline,
|
|
1818
|
+
AnimatedLink_default as AnimatedLink,
|
|
1819
|
+
AnimatedProse,
|
|
1820
|
+
AnimatedProseContext,
|
|
1821
|
+
AnimatedSubtext_default as AnimatedSubtext,
|
|
1822
|
+
AnimatedText,
|
|
1823
|
+
DEFAULT_CHARS,
|
|
1824
|
+
Lenis as LenisProvider,
|
|
1825
|
+
PageEnterProvider,
|
|
1826
|
+
PageTransitionProvider,
|
|
1827
|
+
Preloader,
|
|
1828
|
+
PreloaderProvider,
|
|
1829
|
+
ScrambleGroup,
|
|
1830
|
+
ScrambleText,
|
|
1831
|
+
ScrambleTextPlugin,
|
|
1832
|
+
ScrollAnimatedHeadline,
|
|
1833
|
+
ScrollTrigger,
|
|
1834
|
+
SplitText,
|
|
1835
|
+
cva,
|
|
1836
|
+
cx,
|
|
1837
|
+
easingDefinitionToFunction,
|
|
1838
|
+
getCssScrollLocked,
|
|
1839
|
+
getLenis,
|
|
1840
|
+
gsap,
|
|
1841
|
+
scrollToTop,
|
|
1842
|
+
setCssScrollLocked,
|
|
1843
|
+
stagger,
|
|
1844
|
+
useAsciiDelay,
|
|
1845
|
+
useDualLayerScramble,
|
|
1846
|
+
useGSAP,
|
|
1847
|
+
useIdleGSAP,
|
|
1848
|
+
useLenis,
|
|
1849
|
+
usePageEnter,
|
|
1850
|
+
usePageEnterContext,
|
|
1851
|
+
usePageTransition,
|
|
1852
|
+
usePageTransitionContext,
|
|
1853
|
+
usePreloader,
|
|
1854
|
+
useScrambleGroup
|
|
1855
|
+
};
|
|
1856
|
+
//# sourceMappingURL=index.js.map
|