apex-ui-kit 2.6.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ReadMe.md +22 -10
- package/dist/ApexUI-Kit/templates/AccordionMarquee/AccordionMarquee.jsx +340 -161
- package/dist/ApexUI-Kit/templates/AnimatedTabs/AnimatedTabs.jsx +1 -1
- package/dist/ApexUI-Kit/templates/ArcGallery/ArcGallery.jsx +356 -0
- package/dist/ApexUI-Kit/templates/BounceCards/BounceCards.jsx +22 -2
- package/dist/ApexUI-Kit/templates/CircularCarousel/CircularCarousel.jsx +381 -0
- package/dist/ApexUI-Kit/templates/EdgeBlur/EdgeBlur.jsx +92 -0
- package/dist/ApexUI-Kit/templates/FlipClock/FlipClock.jsx +5 -1
- package/dist/ApexUI-Kit/templates/GooeyNav/GooeyNav.jsx +2 -2
- package/dist/ApexUI-Kit/templates/HorizonGlow/HorizonGlow.jsx +259 -0
- package/dist/ApexUI-Kit/templates/MagneticButton/MagneticButton.jsx +69 -0
- package/dist/ApexUI-Kit/templates/Marquee3D/Marquee3D.jsx +68 -0
- package/dist/ApexUI-Kit/templates/NavDropDown/NavDropDown.jsx +123 -1
- package/dist/ApexUI-Kit/templates/ParticleText/ParticleText.jsx +328 -0
- package/dist/ApexUI-Kit/templates/RadialMenu/RadialMenu.jsx +103 -0
- package/dist/ApexUI-Kit/templates/ScrollStack/ScrollStack.jsx +88 -0
- package/dist/ApexUI-Kit/templates/SidebarNav/SidebarNav.jsx +22 -1
- package/dist/ApexUI-Kit/templates/SunriseBackground/SunriseBackground.jsx +290 -0
- package/dist/ApexUI-Kit/templates/WireframeKnot3D/WireframeKnot3D.jsx +112 -0
- package/dist/cli/index.js +11 -0
- package/package.json +2 -2
- package/dist/ApexUI-Kit/templates/AccordionMarquee/AccordionMarquee.css +0 -1
|
@@ -1,161 +1,340 @@
|
|
|
1
|
-
import React, { useLayoutEffect, useRef } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
1
|
+
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Accordion Marquee: a list of big rows with one colored band that glides
|
|
5
|
+
* from row to row under the pointer. The band's two edges ride separate
|
|
6
|
+
* springs (the leading edge is quicker), so it stretches like liquid as it
|
|
7
|
+
* travels and snaps back when it lands; it also leans a little toward the
|
|
8
|
+
* pointer. Inside the band each row's marquee scrolls, and moving the mouse
|
|
9
|
+
* sideways speeds it up or turns it around. Works with touch (tap a row)
|
|
10
|
+
* and keyboard (Tab through the rows).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const IK = "https://ik.imagekit.io/ny6yinyut/apexui/assets/componentsimage";
|
|
14
|
+
const shot = (n) => `${IK}/${n}.png?tr=f-auto,q-70,w-420`;
|
|
15
|
+
const DEFAULT_ITEMS = [
|
|
16
|
+
{ title: "Cybernetic Dreams", text: "Explore the future", image: shot("galaxy-3d") },
|
|
17
|
+
{ title: "Quantum Leap", text: "Journey through spacetime", image: shot("warp-tunnel") },
|
|
18
|
+
{ title: "Neural Networks", text: "Unravel complexity", image: shot("particle-morph") },
|
|
19
|
+
{ title: "Digital Odyssey", text: "Voyage into the digital world", image: shot("retro-grid-3d") },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const clamp = (v, a, b) => Math.min(b, Math.max(a, v));
|
|
23
|
+
const SHAPES = { pill: "rounded-full aspect-[2.2/1]", rounded: "rounded-[14cqh] aspect-[3/2]", circle: "rounded-full aspect-square" };
|
|
24
|
+
|
|
25
|
+
const Part = ({ item, shape }) => (
|
|
26
|
+
<div className="flex h-full shrink-0 items-center">
|
|
27
|
+
<span className="whitespace-nowrap px-[0.45em] text-[clamp(0.95rem,min(36cqh,7.5cqw),6rem)] font-semibold leading-none tracking-[-0.02em]">{item.text ?? item.title}</span>
|
|
28
|
+
{item.image ? <span className={`mx-[3cqh] block h-[58cqh] shrink-0 bg-cover bg-center ${shape}`} style={{ backgroundImage: `url(${item.image})` }} /> : <span className="mx-[0.3em] text-[clamp(0.8rem,24cqh,3rem)] leading-none opacity-70">✦</span>}
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const AccordionMarquee = ({
|
|
33
|
+
items = DEFAULT_ITEMS,
|
|
34
|
+
speed = 70,
|
|
35
|
+
direction = "left",
|
|
36
|
+
cursorBoost = 0.5,
|
|
37
|
+
duration = 0.5,
|
|
38
|
+
stretch = 0.6,
|
|
39
|
+
magnet = 0.2,
|
|
40
|
+
bgColor = "#bbf451",
|
|
41
|
+
textColor = "#18181b",
|
|
42
|
+
menuBg = "#0f0f12",
|
|
43
|
+
menuColor = "#fafafa",
|
|
44
|
+
lineColor = "rgba(255,255,255,0.14)",
|
|
45
|
+
inset = 0,
|
|
46
|
+
radius = 0,
|
|
47
|
+
imageShape = "pill",
|
|
48
|
+
showIndex = false,
|
|
49
|
+
dimOthers = false,
|
|
50
|
+
intro = true,
|
|
51
|
+
height = "32rem",
|
|
52
|
+
onSelect,
|
|
53
|
+
className = "",
|
|
54
|
+
}) => {
|
|
55
|
+
const root = useRef(null);
|
|
56
|
+
const list = useRef(null);
|
|
57
|
+
const bandRef = useRef(null);
|
|
58
|
+
const rowEls = useRef([]);
|
|
59
|
+
const strips = useRef([]);
|
|
60
|
+
const [copies, setCopies] = useState([]);
|
|
61
|
+
const live = useRef({});
|
|
62
|
+
live.current = { speed, direction, cursorBoost, duration, stretch, magnet, inset, radius, dimOthers, onSelect };
|
|
63
|
+
const engine = useRef(null);
|
|
64
|
+
const list_ = items.length ? items : DEFAULT_ITEMS;
|
|
65
|
+
|
|
66
|
+
useLayoutEffect(() => {
|
|
67
|
+
const nav = root.current;
|
|
68
|
+
const reduced = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
69
|
+
const rows = [];
|
|
70
|
+
const runs = [];
|
|
71
|
+
const b = { on: false, index: -1, top: 0, bot: 0, vTop: 0, vBot: 0, tTop: 0, tBot: 0, lead: 1 };
|
|
72
|
+
const cur = { x: 0, y: null, t: 0, vx: 0 };
|
|
73
|
+
let raf = 0;
|
|
74
|
+
let last = 0;
|
|
75
|
+
let visible = true;
|
|
76
|
+
|
|
77
|
+
const mark = () => {
|
|
78
|
+
rowEls.current.forEach((li, i) => {
|
|
79
|
+
if (!li) return;
|
|
80
|
+
li.toggleAttribute("data-active", i === b.index);
|
|
81
|
+
li.toggleAttribute("data-dim", live.current.dimOthers && b.index >= 0 && i !== b.index);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const paint = () => {
|
|
86
|
+
const band = bandRef.current;
|
|
87
|
+
if (!band) return;
|
|
88
|
+
const H = list.current?.clientHeight ?? 0;
|
|
89
|
+
const o = live.current;
|
|
90
|
+
const tall = b.bot - b.top;
|
|
91
|
+
if (!b.on || tall < 0.3) {
|
|
92
|
+
band.style.visibility = "hidden";
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const side = clamp(o.inset, 0, 40);
|
|
96
|
+
const r = Math.min(o.radius, tall / 2);
|
|
97
|
+
band.style.visibility = "visible";
|
|
98
|
+
band.style.clipPath = `inset(${b.top.toFixed(2)}px ${side}px ${(H - b.bot).toFixed(2)}px ${side}px round ${r.toFixed(2)}px)`;
|
|
99
|
+
runs.forEach((run, i) => {
|
|
100
|
+
const track = strips.current[i]?.firstElementChild;
|
|
101
|
+
if (track && run.spin > 0) track.style.transform = `translate3d(${run.x.toFixed(2)}px,0,0)`;
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const frame = (now) => {
|
|
106
|
+
raf = 0;
|
|
107
|
+
const o = live.current;
|
|
108
|
+
const dt = last ? Math.min(0.05, (now - last) / 1000) : 1 / 60;
|
|
109
|
+
last = now;
|
|
110
|
+
let busy = false;
|
|
111
|
+
|
|
112
|
+
if (b.on) {
|
|
113
|
+
const row = rows[b.index];
|
|
114
|
+
if (row) {
|
|
115
|
+
const ins = Math.min(o.inset, row.h / 2 - 2);
|
|
116
|
+
const lean = cur.y === null || reduced ? 0 : clamp((cur.y - (row.top + row.h / 2)) * o.magnet, -row.h * 0.3, row.h * 0.3);
|
|
117
|
+
b.tTop = row.top + ins + lean;
|
|
118
|
+
b.tBot = row.top + row.h - ins + lean;
|
|
119
|
+
}
|
|
120
|
+
const move = (b.tTop + b.tBot) / 2 - (b.top + b.bot) / 2;
|
|
121
|
+
if (Math.abs(move) > 1) b.lead = Math.sign(move);
|
|
122
|
+
if (reduced) {
|
|
123
|
+
b.top = b.tTop;
|
|
124
|
+
b.bot = b.tBot;
|
|
125
|
+
b.vTop = b.vBot = 0;
|
|
126
|
+
} else {
|
|
127
|
+
// Two springs: the edge heading the move is stiffer, the trailing one lags.
|
|
128
|
+
const w = (Math.PI * 2) / Math.max(0.1, o.duration);
|
|
129
|
+
const quick = w * (1 + o.stretch);
|
|
130
|
+
const lazy = w * (1 - 0.55 * o.stretch);
|
|
131
|
+
const wTop = b.lead > 0 ? lazy : quick;
|
|
132
|
+
const wBot = b.lead > 0 ? quick : lazy;
|
|
133
|
+
const n = Math.ceil(dt * 240);
|
|
134
|
+
const h = dt / n;
|
|
135
|
+
for (let k = 0; k < n; k++) {
|
|
136
|
+
b.vTop += (-wTop * wTop * (b.top - b.tTop) - 1.8 * wTop * b.vTop) * h;
|
|
137
|
+
b.vBot += (-wBot * wBot * (b.bot - b.tBot) - 1.8 * wBot * b.vBot) * h;
|
|
138
|
+
b.top += b.vTop * h;
|
|
139
|
+
b.bot += b.vBot * h;
|
|
140
|
+
if (b.bot < b.top) {
|
|
141
|
+
const mid = (b.top + b.bot) / 2;
|
|
142
|
+
const v = (b.vTop + b.vBot) / 2;
|
|
143
|
+
b.top = b.bot = mid;
|
|
144
|
+
b.vTop = b.vBot = v;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const still = Math.abs(b.top - b.tTop) < 0.1 && Math.abs(b.bot - b.tBot) < 0.1 && Math.abs(b.vTop) + Math.abs(b.vBot) < 3;
|
|
149
|
+
if (still) {
|
|
150
|
+
b.top = b.tTop;
|
|
151
|
+
b.bot = b.tBot;
|
|
152
|
+
b.vTop = b.vBot = 0;
|
|
153
|
+
if (b.index < 0) b.on = false;
|
|
154
|
+
} else busy = true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Marquees: only the rows the band covers run, easing in and out.
|
|
158
|
+
cur.vx *= Math.exp(-dt / 0.3);
|
|
159
|
+
const push = clamp(cur.vx * o.cursorBoost * 0.5, -1500, 1500);
|
|
160
|
+
runs.forEach((run, i) => {
|
|
161
|
+
const row = rows[i];
|
|
162
|
+
const covered = b.on && row && Math.min(b.bot, row.top + row.h) - Math.max(b.top, row.top) > row.h * 0.04;
|
|
163
|
+
const goal = covered ? 1 : 0;
|
|
164
|
+
run.spin += (goal - run.spin) * (1 - Math.exp(-dt / 0.3));
|
|
165
|
+
if (Math.abs(goal - run.spin) < 0.003) run.spin = goal;
|
|
166
|
+
if (!run.spin) return;
|
|
167
|
+
busy = true;
|
|
168
|
+
if (!run.part || reduced) return;
|
|
169
|
+
const way = o.direction === "right" ? 1 : o.direction === "alternate" && i % 2 ? 1 : -1;
|
|
170
|
+
run.x += (way * o.speed + push) * run.spin * dt;
|
|
171
|
+
run.x = (((run.x % run.part) + run.part) % run.part) - run.part;
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
paint();
|
|
175
|
+
if (busy && visible) raf = requestAnimationFrame(frame);
|
|
176
|
+
else last = 0;
|
|
177
|
+
};
|
|
178
|
+
const wake = () => {
|
|
179
|
+
if (!raf && visible) raf = requestAnimationFrame(frame);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const measure = () => {
|
|
183
|
+
rows.length = 0;
|
|
184
|
+
rowEls.current.forEach((li, i) => {
|
|
185
|
+
if (!li) return;
|
|
186
|
+
rows[i] = { top: li.offsetTop, h: li.offsetHeight };
|
|
187
|
+
const strip = strips.current[i];
|
|
188
|
+
if (strip) {
|
|
189
|
+
strip.style.top = `${li.offsetTop}px`;
|
|
190
|
+
strip.style.height = `${li.offsetHeight}px`;
|
|
191
|
+
}
|
|
192
|
+
runs[i] = runs[i] || { x: 0, part: 0, spin: 0 };
|
|
193
|
+
});
|
|
194
|
+
const need = rows.map((row, i) => {
|
|
195
|
+
const part = strips.current[i]?.firstElementChild?.firstElementChild;
|
|
196
|
+
const pw = part?.getBoundingClientRect().width ?? 0;
|
|
197
|
+
const run = runs[i];
|
|
198
|
+
if (pw > 0) {
|
|
199
|
+
run.part = pw;
|
|
200
|
+
run.x = (((run.x % pw) + pw) % pw) - pw;
|
|
201
|
+
}
|
|
202
|
+
return pw > 0 ? Math.ceil((list.current?.clientWidth ?? 0) / pw) + 2 : 3;
|
|
203
|
+
});
|
|
204
|
+
setCopies((c) => (c.length === need.length && c.every((v, i) => v === need[i]) ? c : need));
|
|
205
|
+
if (b.on && b.index >= 0) wake();
|
|
206
|
+
paint();
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const rowAt = (y) => rows.findIndex((r) => r && y >= r.top && y < r.top + r.h);
|
|
210
|
+
const goTo = (i, fromY) => {
|
|
211
|
+
const row = rows[i];
|
|
212
|
+
if (!row) return;
|
|
213
|
+
if (!b.on) {
|
|
214
|
+
// Grow out of the edge the pointer came in from.
|
|
215
|
+
const start = fromY !== undefined && fromY > row.top + row.h / 2 ? row.top + row.h : row.top;
|
|
216
|
+
b.top = b.bot = start;
|
|
217
|
+
b.vTop = b.vBot = 0;
|
|
218
|
+
b.on = true;
|
|
219
|
+
}
|
|
220
|
+
b.index = i;
|
|
221
|
+
mark();
|
|
222
|
+
wake();
|
|
223
|
+
};
|
|
224
|
+
const release = (toward) => {
|
|
225
|
+
if (!b.on) return;
|
|
226
|
+
const p = toward === "top" ? b.tTop : toward === "bottom" ? b.tBot : (b.tTop + b.tBot) / 2;
|
|
227
|
+
b.tTop = b.tBot = p;
|
|
228
|
+
b.index = -1;
|
|
229
|
+
cur.y = null;
|
|
230
|
+
mark();
|
|
231
|
+
wake();
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const ul = list.current;
|
|
235
|
+
const localY = (e) => e.clientY - ul.getBoundingClientRect().top;
|
|
236
|
+
const onMove = (e) => {
|
|
237
|
+
if (e.pointerType === "touch") return;
|
|
238
|
+
const now = performance.now();
|
|
239
|
+
const gap = (now - cur.t) / 1000;
|
|
240
|
+
if (gap > 0 && gap < 0.1) cur.vx += ((e.clientX - cur.x) / gap - cur.vx) * (1 - Math.exp(-gap / 0.08));
|
|
241
|
+
cur.x = e.clientX;
|
|
242
|
+
cur.t = now;
|
|
243
|
+
cur.y = localY(e);
|
|
244
|
+
const i = rowAt(cur.y);
|
|
245
|
+
if (i >= 0 && i !== b.index) goTo(i, cur.y);
|
|
246
|
+
else wake();
|
|
247
|
+
};
|
|
248
|
+
const onLeave = (e) => {
|
|
249
|
+
if (e.pointerType === "touch" || b.index < 0) return;
|
|
250
|
+
const y = localY(e);
|
|
251
|
+
const row = rows[b.index];
|
|
252
|
+
release(y <= 0 ? "top" : y >= ul.clientHeight ? "bottom" : row && y < row.top + row.h / 2 ? "top" : "bottom");
|
|
253
|
+
};
|
|
254
|
+
const onDown = (e) => {
|
|
255
|
+
if (e.pointerType !== "touch") return;
|
|
256
|
+
const y = localY(e);
|
|
257
|
+
const i = rowAt(y);
|
|
258
|
+
if (i >= 0) goTo(i, y);
|
|
259
|
+
};
|
|
260
|
+
ul.addEventListener("pointermove", onMove);
|
|
261
|
+
ul.addEventListener("pointerleave", onLeave);
|
|
262
|
+
ul.addEventListener("pointerdown", onDown);
|
|
263
|
+
engine.current = { goTo, release };
|
|
264
|
+
|
|
265
|
+
const ro = new ResizeObserver(measure);
|
|
266
|
+
ro.observe(nav);
|
|
267
|
+
const io = new IntersectionObserver(([en]) => {
|
|
268
|
+
visible = en.isIntersecting;
|
|
269
|
+
if (visible) wake();
|
|
270
|
+
});
|
|
271
|
+
io.observe(nav);
|
|
272
|
+
measure();
|
|
273
|
+
|
|
274
|
+
return () => {
|
|
275
|
+
cancelAnimationFrame(raf);
|
|
276
|
+
ro.disconnect();
|
|
277
|
+
io.disconnect();
|
|
278
|
+
ul.removeEventListener("pointermove", onMove);
|
|
279
|
+
ul.removeEventListener("pointerleave", onLeave);
|
|
280
|
+
ul.removeEventListener("pointerdown", onDown);
|
|
281
|
+
engine.current = null;
|
|
282
|
+
};
|
|
283
|
+
}, [list_.length]);
|
|
284
|
+
|
|
285
|
+
// Rows rise in and the lines draw once on mount.
|
|
286
|
+
useEffect(() => {
|
|
287
|
+
const nav = root.current;
|
|
288
|
+
if (!intro || !nav || window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return undefined;
|
|
289
|
+
const ease = "cubic-bezier(0.22, 1, 0.36, 1)";
|
|
290
|
+
const anims = [
|
|
291
|
+
...[...nav.querySelectorAll("[data-rise]")].map((el, i) => el.animate([{ transform: "translateY(105%)" }, { transform: "none" }], { duration: 850, delay: 80 + i * 80, easing: ease, fill: "backwards" })),
|
|
292
|
+
...[...nav.querySelectorAll("[data-rule]")].map((el, i) => el.animate([{ transform: "scaleX(0)" }, { transform: "none" }], { duration: 1000, delay: i * 80, easing: ease, fill: "backwards" })),
|
|
293
|
+
];
|
|
294
|
+
return () => anims.forEach((a) => a.cancel());
|
|
295
|
+
}, [intro]);
|
|
296
|
+
|
|
297
|
+
const shape = SHAPES[imageShape] ?? SHAPES.pill;
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
<nav ref={root} aria-label="Menu" className={`relative w-full select-none overflow-hidden ${className}`} style={{ height, background: menuBg, color: menuColor }}>
|
|
301
|
+
<ul ref={list} className="relative m-0 flex h-full list-none flex-col p-0">
|
|
302
|
+
{list_.map((item, i) => {
|
|
303
|
+
const Tag = item.href ? "a" : "button";
|
|
304
|
+
return (
|
|
305
|
+
<li key={i} ref={(e) => (rowEls.current[i] = e)} className="group relative min-h-[3.5rem] flex-1 overflow-hidden [container-type:size]">
|
|
306
|
+
{i > 0 && <span data-rule="" aria-hidden="true" className="pointer-events-none absolute inset-x-0 top-0 h-px origin-left" style={{ background: lineColor }} />}
|
|
307
|
+
<Tag
|
|
308
|
+
{...(item.href ? { href: item.href } : { type: "button" })}
|
|
309
|
+
onFocus={(e) => e.currentTarget.matches(":focus-visible") && engine.current?.goTo(i)}
|
|
310
|
+
onBlur={(e) => !list.current?.contains(e.relatedTarget) && engine.current?.release("bottom")}
|
|
311
|
+
onClick={(e) => live.current.onSelect?.(item, i, e)}
|
|
312
|
+
className={`relative flex h-full w-full cursor-pointer items-center justify-center border-0 bg-transparent ${showIndex ? "px-[clamp(2.4rem,10cqw,5rem)]" : "px-[clamp(1rem,4cqw,2.5rem)]"} text-inherit no-underline outline-none transition-opacity duration-500 group-data-[dim]:opacity-30 focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-current`}
|
|
313
|
+
>
|
|
314
|
+
{showIndex && <span className="absolute left-[clamp(1rem,4cqw,2.5rem)] font-mono text-[clamp(0.65rem,11cqh,0.9rem)] tabular-nums opacity-50">{String(i + 1).padStart(2, "0")}</span>}
|
|
315
|
+
<span className="block overflow-hidden py-[0.06em]">
|
|
316
|
+
<span data-rise="" className="block whitespace-nowrap text-[clamp(1rem,min(38cqh,7cqw),6.5rem)] font-semibold uppercase leading-[1.05] tracking-[-0.02em]">
|
|
317
|
+
{item.title}
|
|
318
|
+
</span>
|
|
319
|
+
</span>
|
|
320
|
+
</Tag>
|
|
321
|
+
</li>
|
|
322
|
+
);
|
|
323
|
+
})}
|
|
324
|
+
</ul>
|
|
325
|
+
<div ref={bandRef} aria-hidden="true" className="pointer-events-none absolute inset-0 z-10" style={{ background: bgColor, color: textColor, visibility: "hidden", clipPath: "inset(50% 0 50% 0)" }}>
|
|
326
|
+
{list_.map((item, i) => (
|
|
327
|
+
<div key={i} ref={(e) => (strips.current[i] = e)} className="absolute inset-x-0 overflow-hidden [container-type:size]">
|
|
328
|
+
<div className="flex h-full w-max items-center will-change-transform">
|
|
329
|
+
{Array.from({ length: copies[i] ?? 3 }, (_, k) => (
|
|
330
|
+
<Part key={k} item={item} shape={shape} />
|
|
331
|
+
))}
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
))}
|
|
335
|
+
</div>
|
|
336
|
+
</nav>
|
|
337
|
+
);
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
export default AccordionMarquee;
|
|
@@ -58,7 +58,7 @@ const AnimatedTabs = ({ tabs = DEFAULT_TABS, defaultTab, variant = "pill", accen
|
|
|
58
58
|
aria-controls={`${id}-panel`}
|
|
59
59
|
tabIndex={on ? 0 : -1}
|
|
60
60
|
onClick={() => select(i)}
|
|
61
|
-
className={`relative flex-1 whitespace-nowrap px-
|
|
61
|
+
className={`relative flex-1 whitespace-nowrap px-3 py-2 text-[0.82rem] sm:px-4 sm:text-[0.88rem] font-medium outline-none transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-current/40 ${variant === "underline" ? "pb-3" : variant === "pill" ? "rounded-full" : "rounded-lg"} ${on ? (variant === "pill" ? "text-neutral-950" : "") : dark ? "text-white/55 hover:text-white" : "text-neutral-500 hover:text-neutral-900"}`}
|
|
62
62
|
>
|
|
63
63
|
{on && (
|
|
64
64
|
<motion.span
|