apex-ui-kit 2.1.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/dist/ApexUI-Kit/templates/AnimatedButton/AnimatedButton.jsx +251 -0
  2. package/dist/ApexUI-Kit/templates/AnimatedTabs/AnimatedTabs.jsx +99 -0
  3. package/dist/ApexUI-Kit/templates/BlurRevealText/BlurRevealText.jsx +60 -0
  4. package/dist/ApexUI-Kit/templates/Carousel3D/Carousel3D.jsx +222 -0
  5. package/dist/ApexUI-Kit/templates/DotGlobe/DotGlobe.jsx +289 -0
  6. package/dist/ApexUI-Kit/templates/DotWaveBackground/DotWaveBackground.jsx +189 -0
  7. package/dist/ApexUI-Kit/templates/DynamicIsland/DynamicIsland.jsx +221 -0
  8. package/dist/ApexUI-Kit/templates/FaqAccordion/FaqAccordion.jsx +88 -0
  9. package/dist/ApexUI-Kit/templates/FlipClock/FlipClock.jsx +121 -0
  10. package/dist/ApexUI-Kit/templates/LiquidGradient/LiquidGradient.jsx +238 -0
  11. package/dist/ApexUI-Kit/templates/LoadingScreen/LoadingScreen.jsx +361 -0
  12. package/dist/ApexUI-Kit/templates/MagneticCursor/MagneticCursor.jsx +306 -0
  13. package/dist/ApexUI-Kit/templates/MagnifyDock/MagnifyDock.jsx +154 -0
  14. package/dist/ApexUI-Kit/templates/ProfileCard/ProfileCard.jsx +213 -0
  15. package/dist/ApexUI-Kit/templates/RollingCounter/RollingCounter.jsx +143 -0
  16. package/dist/ApexUI-Kit/templates/ScrambleText/ScrambleText.jsx +132 -0
  17. package/dist/ApexUI-Kit/templates/SidebarNav/SidebarNav.jsx +220 -0
  18. package/dist/ApexUI-Kit/templates/SpotlightGrid/SpotlightGrid.jsx +110 -0
  19. package/dist/ApexUI-Kit/templates/ToastStack/ToastStack.jsx +108 -0
  20. package/dist/ApexUI-Kit/templates/ToolTip/ToolTip.jsx +5 -3
  21. package/dist/ApexUI-Kit/templates/WebGLCarousel/WebGLCarousel.jsx +326 -0
  22. package/dist/ApexUI-Kit/templates/WorldMap/WorldMap.jsx +359 -0
  23. package/dist/cli/index.js +25 -0
  24. package/package.json +1 -1
@@ -0,0 +1,326 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+ import { Camera, Mesh, Plane, Program, Renderer, Texture, Transform } from "ogl";
3
+
4
+ /*
5
+ * WebGL Carousel: images stand on a full 360° ring drawn with OGL, seen from
6
+ * slightly above. Drag, swipe or use a sideways scroll to spin it; let go and it settles on the nearest image.
7
+ * Fast spins ripple the images and the far side of the ring dims.
8
+ */
9
+
10
+ const IK = "https://ik.imagekit.io/ny6yinyut/apexui/assets";
11
+ const img = (name) => `${IK}/${name}?tr=f-jpg,q-80,w-640`;
12
+
13
+ const DEFAULT_ITEMS = [
14
+ { image: img("ironman.png"), title: "Iron Man" },
15
+ { image: img("spiderman.png"), title: "Spider-Man" },
16
+ { image: img("captainamerica.png"), title: "Captain America" },
17
+ { image: img("hulk.png"), title: "Hulk" },
18
+ { image: img("black-panther.png"), title: "Black Panther" },
19
+ { image: img("doctorStrange.png"), title: "Doctor Strange" },
20
+ { image: img("thanos.png"), title: "Thanos" },
21
+ { image: img("png/spider-man.png"), title: "Miles" },
22
+ ];
23
+
24
+ const cardVertex = /* glsl */ `
25
+ attribute vec3 position;
26
+ attribute vec2 uv;
27
+ uniform mat4 modelViewMatrix;
28
+ uniform mat4 projectionMatrix;
29
+ uniform float uSpeed;
30
+ uniform float uTime;
31
+ varying vec2 vUv;
32
+ void main() {
33
+ vUv = uv;
34
+ vec3 p = position;
35
+ // Ripple across the image while the ring spins fast.
36
+ p.z += sin(p.y * 3.1416 + uTime * 3.0) * uSpeed * 0.03 + sin(p.x * 3.1416) * abs(uSpeed) * 0.02;
37
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);
38
+ }
39
+ `;
40
+
41
+ const cardFragment = /* glsl */ `
42
+ precision highp float;
43
+ uniform sampler2D tMap;
44
+ uniform vec2 uImageSize;
45
+ uniform vec2 uPlaneSize;
46
+ uniform float uRadius;
47
+ uniform float uFade;
48
+ uniform float uReady;
49
+ varying vec2 vUv;
50
+
51
+ float roundedBox(vec2 p, vec2 b, float r) {
52
+ vec2 q = abs(p) - b + r;
53
+ return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r;
54
+ }
55
+
56
+ void main() {
57
+ vec2 ratio = vec2(
58
+ min((uPlaneSize.x / uPlaneSize.y) / (uImageSize.x / uImageSize.y), 1.0),
59
+ min((uPlaneSize.y / uPlaneSize.x) / (uImageSize.y / uImageSize.x), 1.0)
60
+ );
61
+ vec2 uv = vec2(vUv.x * ratio.x + (1.0 - ratio.x) * 0.5, vUv.y * ratio.y + (1.0 - ratio.y) * 0.5);
62
+ // Back faces show the image mirrored, so flip it to read the right way round.
63
+ if (!gl_FrontFacing) uv.x = 1.0 - uv.x;
64
+ vec3 col = mix(vec3(0.1), texture2D(tMap, uv).rgb, uReady);
65
+ vec2 p = (vUv - 0.5) * uPlaneSize;
66
+ float d = roundedBox(p, uPlaneSize * 0.5, uRadius * min(uPlaneSize.x, uPlaneSize.y));
67
+ float edge = 0.006 * min(uPlaneSize.x, uPlaneSize.y);
68
+ float alpha = 1.0 - smoothstep(-edge, edge, d);
69
+ gl_FragColor = vec4(col * uFade, alpha);
70
+ }
71
+ `;
72
+
73
+ const WebGLCarousel = ({
74
+ items = DEFAULT_ITEMS,
75
+ height = "30rem",
76
+ tilt = 3,
77
+ radiusScale = 0.83,
78
+ autoRotate = 6,
79
+ borderRadius = 0.06,
80
+ wheel = true,
81
+ showTitle = true,
82
+ className = "",
83
+ }) => {
84
+ const containerRef = useRef(null);
85
+ const opts = useRef(null);
86
+ opts.current = { tilt, radiusScale, autoRotate, borderRadius, wheel };
87
+ const [active, setActive] = useState(0);
88
+
89
+ useEffect(() => {
90
+ const container = containerRef.current;
91
+ const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
92
+ const renderer = new Renderer({ dpr: Math.min(window.devicePixelRatio || 1, 2), alpha: true, antialias: true });
93
+ const gl = renderer.gl;
94
+ gl.clearColor(0, 0, 0, 0);
95
+ gl.canvas.style.cssText = "position:absolute;inset:0;width:100%;height:100%;display:block;";
96
+ container.prepend(gl.canvas);
97
+
98
+ const camera = new Camera(gl, { fov: 35 });
99
+ const scene = new Transform();
100
+ const ring = new Transform();
101
+ ring.setParent(scene);
102
+ const geometry = new Plane(gl, { widthSegments: 20, heightSegments: 8 });
103
+ const count = Math.max(items.length, 1);
104
+ const W = 1;
105
+ const H = W / 0.75;
106
+ const step = (Math.PI * 2) / count;
107
+
108
+ const meshes = items.map((item) => {
109
+ const texture = new Texture(gl, { generateMipmaps: true });
110
+ const program = new Program(gl, {
111
+ vertex: cardVertex,
112
+ fragment: cardFragment,
113
+ transparent: true,
114
+ cullFace: null,
115
+ uniforms: {
116
+ tMap: { value: texture },
117
+ uImageSize: { value: [1, 1] },
118
+ uPlaneSize: { value: [W, H] },
119
+ uRadius: { value: opts.current.borderRadius },
120
+ uFade: { value: 1 },
121
+ uReady: { value: 0 },
122
+ uSpeed: { value: 0 },
123
+ uTime: { value: 0 },
124
+ },
125
+ });
126
+ const image = new Image();
127
+ image.crossOrigin = "anonymous";
128
+ image.onload = () => {
129
+ texture.image = image;
130
+ program.uniforms.uImageSize.value = [image.naturalWidth, image.naturalHeight];
131
+ program.uniforms.uReady.value = 1;
132
+ };
133
+ image.src = item.image;
134
+ const mesh = new Mesh(gl, { geometry, program });
135
+ mesh.scale.set(W, H, 1);
136
+ mesh.setParent(ring);
137
+ return mesh;
138
+ });
139
+
140
+
141
+ let radius = 1;
142
+ let cssWidth = 1;
143
+ const layout = () => {
144
+ const { width, height: h } = container.getBoundingClientRect();
145
+ cssWidth = Math.max(1, width);
146
+ renderer.setSize(cssWidth, Math.max(1, h));
147
+ camera.perspective({ aspect: cssWidth / Math.max(1, h) });
148
+ radius = Math.max((count * W * 1.18) / (Math.PI * 2), W * 1.1) * opts.current.radiusScale;
149
+ meshes.forEach((m, i) => {
150
+ const a = i * step;
151
+ m.position.set(Math.sin(a) * radius, 0, Math.cos(a) * radius);
152
+ m.rotation.y = a;
153
+ });
154
+ // Pull the camera back until the whole ring fits.
155
+ const t = (opts.current.tilt * Math.PI) / 180;
156
+ const half = Math.tan(((camera.fov / 2) * Math.PI) / 180);
157
+ const needW = (radius + W * 0.6) / (half * camera.aspect);
158
+ const needH = (H * 0.62 + radius * Math.sin(t)) / half;
159
+ // The front card sits a radius closer to the camera, so keep that much extra room.
160
+ const dist = Math.max(needW, needH) * 0.8 + radius * 0.9;
161
+ camera.position.set(0, Math.sin(t) * dist, Math.cos(t) * dist);
162
+ camera.lookAt([0, 0, 0]);
163
+ };
164
+
165
+ const s = { angle: 0, target: 0, dragging: false, startX: 0, startTarget: 0, moved: false, hover: false };
166
+ let raf = 0;
167
+ let last = performance.now();
168
+ let time = 0;
169
+ let lastActive = -1;
170
+ let visible = true;
171
+
172
+ const update = (now) => {
173
+ const dt = Math.min(0.05, (now - last) / 1000);
174
+ last = now;
175
+ time += dt;
176
+ const o = opts.current;
177
+ if (!s.dragging && !s.hover && !reduced && o.autoRotate) s.target -= ((o.autoRotate * Math.PI) / 180) * dt;
178
+ const prev = s.angle;
179
+ s.angle += (s.target - s.angle) * (reduced ? 1 : 0.08);
180
+ const speed = dt ? (s.angle - prev) / dt / step : 0;
181
+ ring.rotation.y = s.angle;
182
+
183
+ let nearest = 0;
184
+ let best = -Infinity;
185
+ meshes.forEach((mesh, i) => {
186
+ const facing = Math.cos(i * step + s.angle);
187
+ mesh.program.uniforms.uFade.value = 0.28 + 0.72 * ((facing + 1) / 2) ** 1.6;
188
+ mesh.program.uniforms.uSpeed.value = Math.max(-2, Math.min(2, speed));
189
+ mesh.program.uniforms.uTime.value = time;
190
+ mesh.program.uniforms.uRadius.value = o.borderRadius;
191
+ if (facing > best) {
192
+ best = facing;
193
+ nearest = i;
194
+ }
195
+ });
196
+ if (nearest !== lastActive) {
197
+ lastActive = nearest;
198
+ setActive(nearest);
199
+ }
200
+ renderer.render({ scene, camera });
201
+ };
202
+
203
+ const loop = (now) => {
204
+ update(now);
205
+ raf = requestAnimationFrame(loop);
206
+ };
207
+ const start = () => {
208
+ if (raf || !visible) return;
209
+ last = performance.now();
210
+ raf = requestAnimationFrame(loop);
211
+ };
212
+ const stop = () => {
213
+ cancelAnimationFrame(raf);
214
+ raf = 0;
215
+ };
216
+
217
+ const snap = () => (s.target = Math.round(s.target / step) * step);
218
+ const anglePerPx = () => (Math.PI * 1.1) / cssWidth;
219
+ const onDown = (e) => {
220
+ if (e.button !== undefined && e.button !== 0) return;
221
+ s.dragging = true;
222
+ s.moved = false;
223
+ s.startX = e.clientX;
224
+ s.startTarget = s.target;
225
+ };
226
+ const onMove = (e) => {
227
+ if (!s.dragging) return;
228
+ const dx = e.clientX - s.startX;
229
+ if (Math.abs(dx) > 4) s.moved = true;
230
+ // Drag right → the front of the ring moves right.
231
+ s.target = s.startTarget + dx * anglePerPx();
232
+ };
233
+ const onUp = (e) => {
234
+ if (!s.dragging) return;
235
+ s.dragging = false;
236
+ if (s.moved) {
237
+ s.target += (s.target - s.angle) * 0.5;
238
+ snap();
239
+ return;
240
+ }
241
+ // A click on the left or right half brings that neighbour forward.
242
+ const rect = container.getBoundingClientRect();
243
+ const side = (e.clientX - rect.left) / rect.width - 0.5;
244
+ if (Math.abs(side) > 0.15) s.target += side > 0 ? -step : step;
245
+ snap();
246
+ };
247
+ let wheelTimer = 0;
248
+ const onWheel = (e) => {
249
+ const sideways = Math.abs(e.deltaX) > Math.abs(e.deltaY);
250
+ if (!opts.current.wheel || (!sideways && !e.shiftKey)) return;
251
+ e.preventDefault();
252
+ s.target -= (sideways ? e.deltaX : e.deltaY) * anglePerPx() * 0.5;
253
+ clearTimeout(wheelTimer);
254
+ wheelTimer = setTimeout(snap, 160);
255
+ };
256
+ const onEnter = (e) => (s.hover = e.pointerType === "mouse");
257
+ const onLeave = () => (s.hover = false);
258
+
259
+ container.addEventListener("pointerdown", onDown);
260
+ window.addEventListener("pointermove", onMove);
261
+ window.addEventListener("pointerup", onUp);
262
+ container.addEventListener("pointerenter", onEnter);
263
+ container.addEventListener("pointerleave", onLeave);
264
+ container.addEventListener("wheel", onWheel, { passive: false });
265
+
266
+ const ro = new ResizeObserver(layout);
267
+ ro.observe(container);
268
+ const io = new IntersectionObserver(([entry]) => {
269
+ visible = entry.isIntersecting;
270
+ if (visible) start();
271
+ else stop();
272
+ });
273
+ io.observe(container);
274
+ layout();
275
+ start();
276
+
277
+ // Re-layout when tilt or radius change.
278
+ const relayout = setInterval(() => {
279
+ if (s.lastTilt !== opts.current.tilt || s.lastRadius !== opts.current.radiusScale) {
280
+ s.lastTilt = opts.current.tilt;
281
+ s.lastRadius = opts.current.radiusScale;
282
+ layout();
283
+ }
284
+ }, 150);
285
+
286
+ return () => {
287
+ stop();
288
+ clearInterval(relayout);
289
+ clearTimeout(wheelTimer);
290
+ ro.disconnect();
291
+ io.disconnect();
292
+ container.removeEventListener("pointerdown", onDown);
293
+ window.removeEventListener("pointermove", onMove);
294
+ window.removeEventListener("pointerup", onUp);
295
+ container.removeEventListener("pointerenter", onEnter);
296
+ container.removeEventListener("pointerleave", onLeave);
297
+ container.removeEventListener("wheel", onWheel);
298
+ gl.canvas.remove();
299
+ gl.getExtension("WEBGL_lose_context")?.loseContext();
300
+ };
301
+ // Items rebuild the scene; other props are read live from opts.
302
+ }, [items]);
303
+
304
+ return (
305
+ <div className={`flex w-full flex-col items-center ${className}`}>
306
+ <div
307
+ ref={containerRef}
308
+ role="region"
309
+ aria-roledescription="carousel"
310
+ aria-label="WebGL carousel"
311
+ className="relative w-full cursor-grab touch-pan-y select-none active:cursor-grabbing"
312
+ style={{ height }}
313
+ />
314
+ {showTitle && (
315
+ <p className="mt-1 text-[0.95rem] font-semibold" aria-live="polite">
316
+ {items[active]?.title}
317
+ <span className="ml-2 text-[0.75rem] font-normal opacity-50">
318
+ {active + 1} / {items.length}
319
+ </span>
320
+ </p>
321
+ )}
322
+ </div>
323
+ );
324
+ };
325
+
326
+ export default WebGLCarousel;