apex-ui-kit 2.9.0 → 2.11.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.
@@ -1,235 +1,237 @@
1
- import React, { useEffect, useRef } from "react";
2
- import { Renderer, Program, Mesh, Triangle } from "ogl";
3
-
4
- const hexToRgb = (hex) => {
5
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
6
- if (!result) return [1, 0.5, 0.2];
7
- return [
8
- parseInt(result[1], 16) / 255,
9
- parseInt(result[2], 16) / 255,
10
- parseInt(result[3], 16) / 255,
11
- ];
12
- };
13
-
14
- const vertex = `#version 300 es
15
- precision highp float;
16
- in vec2 position;
17
- in vec2 uv;
18
- out vec2 vUv;
19
- void main() {
20
- vUv = uv;
21
- gl_Position = vec4(position, 0.0, 1.0);
22
- }
23
- `;
24
-
25
- const fragment = `#version 300 es
26
- precision highp float;
27
- uniform vec2 iResolution;
28
- uniform float iTime;
29
- uniform vec3 uCustomColor;
30
- uniform float uUseCustomColor;
31
- uniform float uSpeed;
32
- uniform float uDirection;
33
- uniform float uScale;
34
- uniform float uOpacity;
35
- uniform vec2 uMouse;
36
- uniform float uMouseInteractive;
37
- out vec4 fragColor;
38
-
39
- void mainImage(out vec4 o, vec2 C) {
40
- vec2 center = iResolution.xy * 0.5;
41
- C = (C - center) / uScale + center;
42
-
43
- vec2 mouseOffset = (uMouse - center) * 0.0002;
44
- C += mouseOffset * length(C - center) * step(0.5, uMouseInteractive);
45
-
46
- float i, d, z, T = iTime * uSpeed * uDirection;
47
- vec3 O, p, S;
48
-
49
- // loop count reduced to 30
50
- for (vec2 r = iResolution.xy, Q; ++i < 35.; O += o.w/d*o.xyz) {
51
- p = z*normalize(vec3(C-.5*r,r.y));
52
- p.z -= 4.;
53
- S = p;
54
- d = p.y-T;
55
-
56
- p.x += .4*(1.+p.y)*sin(d + p.x*0.1)*cos(.34*d + p.x*0.05);
57
- Q = p.xz *= mat2(cos(p.y+vec4(0,11,33,0)-T));
58
- z+= d = abs(sqrt(length(Q*Q)) - .25*(5.+S.y))/3.+8e-4;
59
- o = 1.+sin(S.y+p.z*.5+S.z-length(S-p)+vec4(2,1,0,8));
60
- }
61
-
62
- o.xyz = tanh(O/1e4);
63
- }
64
-
65
- bool finite1(float x){ return !(isnan(x) || isinf(x)); }
66
- vec3 sanitize(vec3 c){
67
- return vec3(
68
- finite1(c.r) ? c.r : 0.0,
69
- finite1(c.g) ? c.g : 0.0,
70
- finite1(c.b) ? c.b : 0.0
71
- );
72
- }
73
-
74
- void main() {
75
- vec4 o = vec4(0.0);
76
- mainImage(o, gl_FragCoord.xy);
77
- vec3 rgb = sanitize(o.rgb);
78
-
79
- float intensity = (rgb.r + rgb.g + rgb.b) / 3.0;
80
- vec3 customColor = intensity * uCustomColor;
81
- vec3 finalColor = mix(rgb, customColor, step(0.5, uUseCustomColor));
82
-
83
- float alpha = length(rgb) * uOpacity;
84
- fragColor = vec4(finalColor, alpha);
85
- }
86
- `;
87
-
88
-
89
- export const SmokeBackground = ({
90
- color = "#00c950",
91
- speed = 1,
92
- direction = "forward",
93
- scale = 1,
94
- opacity = 1,
95
- mouseInteractive = true,
96
- className = "",
97
- maxWidth = 'max-w-4xl',
98
- }) => {
99
- const containerRef = useRef(null);
100
- const mousePos = useRef({ x: 0, y: 0 });
101
-
102
- useEffect(() => {
103
- const mountNode = containerRef.current;
104
- if (!mountNode) return;
105
-
106
- const useCustomColor = color ? 1.0 : 0.0;
107
- const customColorRgb = color ? hexToRgb(color) : [1, 1, 1];
108
-
109
- let directionMultiplier = direction === "backward" ? -1.0 : 1.0;
110
-
111
- // cap DPR depending on mount width to avoid heavy pixel loads on small devices
112
- const mountWidth = mountNode.clientWidth || window.innerWidth;
113
- const dprCap = mountWidth > 1200 ? 1.8 : (mountWidth > 768 ? 1.4 : 1.0);
114
- const dpr = Math.min(window.devicePixelRatio || 1, dprCap);
115
-
116
- const renderer = new Renderer({
117
- webgl: 2,
118
- alpha: true,
119
- antialias: false,
120
- dpr,
121
- });
122
-
123
- const gl = renderer.gl;
124
- const canvas = gl.canvas;
125
- canvas.style.display = "block";
126
- canvas.style.width = "100%";
127
- canvas.style.height = "100%";
128
- mountNode.appendChild(canvas);
129
-
130
- const geometry = new Triangle(gl);
131
-
132
- const program = new Program(gl, {
133
- vertex: vertex,
134
- fragment: fragment,
135
- uniforms: {
136
- iTime: { value: 0 },
137
- iResolution: { value: new Float32Array([1, 1]) },
138
- uCustomColor: { value: new Float32Array(customColorRgb) },
139
- uUseCustomColor: { value: useCustomColor },
140
- uSpeed: { value: speed * 0.4 },
141
- uDirection: { value: directionMultiplier },
142
- uScale: { value: scale },
143
- uOpacity: { value: opacity },
144
- uMouse: { value: new Float32Array([0, 0]) },
145
- uMouseInteractive: { value: mouseInteractive ? 1.0 : 0.0 },
146
- },
147
- });
148
-
149
- const mesh = new Mesh(gl, { geometry, program });
150
-
151
- // 🖱 Mouse throttled to RAF
152
- let mouseNeedsUpdate = false;
153
- const handleMouseMove = (e) => {
154
- if (!mouseInteractive) return;
155
- const rect = mountNode.getBoundingClientRect();
156
- mousePos.current.x = e.clientX - rect.left;
157
- mousePos.current.y = rect.height - (e.clientY - rect.top);
158
- mouseNeedsUpdate = true;
159
- };
160
- if (mouseInteractive) {
161
- mountNode.addEventListener("mousemove", handleMouseMove, { passive: true });
162
- }
163
-
164
- const setSize = () => {
165
- const rect = mountNode.getBoundingClientRect();
166
- const width = Math.max(1, Math.floor(rect.width));
167
- const height = Math.max(1, Math.floor(rect.height));
168
- renderer.setSize(width, height);
169
- const res = program.uniforms.iResolution.value;
170
- res[0] = gl.drawingBufferWidth;
171
- res[1] = gl.drawingBufferHeight;
172
- };
173
-
174
- const ro = new ResizeObserver(() => {
175
- requestAnimationFrame(setSize);
176
- });
177
- ro.observe(mountNode);
178
- setSize();
179
-
180
- let raf = 0;
181
- // Only render while the background is on screen.
182
- let visible = true;
183
- const t0 = performance.now();
184
- const loop = (t) => {
185
- raf = 0;
186
- if (!visible) return;
187
- let timeValue = (t - t0) * 0.001;
188
-
189
- if (direction === "funny") {
190
- const cycle = Math.sin(timeValue * 0.5);
191
- program.uniforms.uDirection.value = cycle;
192
- }
193
-
194
- if (mouseNeedsUpdate) {
195
- const mouseUniform = program.uniforms.uMouse.value;
196
- mouseUniform[0] = mousePos.current.x;
197
- mouseUniform[1] = mousePos.current.y;
198
- mouseNeedsUpdate = false;
199
- }
200
-
201
- program.uniforms.iTime.value = timeValue;
202
- renderer.render({ scene: mesh });
203
- raf = requestAnimationFrame(loop);
204
- };
205
- raf = requestAnimationFrame(loop);
206
- const visibilityObserver = new IntersectionObserver(([entry]) => {
207
- visible = entry.isIntersecting;
208
- if (visible && !raf) raf = requestAnimationFrame(loop);
209
- });
210
- visibilityObserver.observe(mountNode);
211
-
212
- return () => {
213
- cancelAnimationFrame(raf);
214
- visibilityObserver.disconnect();
215
- ro.disconnect();
216
- if (mouseInteractive && mountNode) {
217
- mountNode.removeEventListener("mousemove", handleMouseMove);
218
- }
219
- try {
220
- mountNode.removeChild(canvas);
221
- } catch (error) { console.error("Error removing canvas:", error); }
222
- gl.getExtension("WEBGL_lose_context")?.loseContext();
223
- };
224
- }, [color, speed, direction, scale, opacity, mouseInteractive, className]);
225
-
226
- return (
227
- <div className={`w-full ${className}`}>
228
- <div className={`mx-auto w-full ${maxWidth} h-[100vh] lg:h-[90vh] overflow-hidden relative`}>
229
- <div ref={containerRef} className="absolute inset-0 w-full h-full" />
230
- </div>
231
- </div>
232
- );
233
- };
234
-
235
- export default SmokeBackground;
1
+ import React, { useEffect, useRef } from "react";
2
+ import { Renderer, Program, Mesh, Triangle } from "ogl";
3
+
4
+ const hexToRgb = (hex) => {
5
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
6
+ if (!result) return [1, 0.5, 0.2];
7
+ return [
8
+ parseInt(result[1], 16) / 255,
9
+ parseInt(result[2], 16) / 255,
10
+ parseInt(result[3], 16) / 255,
11
+ ];
12
+ };
13
+
14
+ const vertex = `#version 300 es
15
+ precision highp float;
16
+ in vec2 position;
17
+ in vec2 uv;
18
+ out vec2 vUv;
19
+ void main() {
20
+ vUv = uv;
21
+ gl_Position = vec4(position, 0.0, 1.0);
22
+ }
23
+ `;
24
+
25
+ const fragment = `#version 300 es
26
+ precision highp float;
27
+ uniform vec2 iResolution;
28
+ uniform float iTime;
29
+ uniform vec3 uCustomColor;
30
+ uniform float uUseCustomColor;
31
+ uniform float uSpeed;
32
+ uniform float uDirection;
33
+ uniform float uScale;
34
+ uniform float uOpacity;
35
+ uniform vec2 uMouse;
36
+ uniform float uMouseInteractive;
37
+ out vec4 fragColor;
38
+
39
+ void mainImage(out vec4 o, vec2 C) {
40
+ vec2 center = iResolution.xy * 0.5;
41
+ C = (C - center) / uScale + center;
42
+
43
+ vec2 mouseOffset = (uMouse - center) * 0.0002;
44
+ C += mouseOffset * length(C - center) * step(0.5, uMouseInteractive);
45
+
46
+ float i, d, z, T = iTime * uSpeed * uDirection;
47
+ vec3 O, p, S;
48
+
49
+ // loop count reduced to 30
50
+ for (vec2 r = iResolution.xy, Q; ++i < 35.; O += o.w/d*o.xyz) {
51
+ p = z*normalize(vec3(C-.5*r,r.y));
52
+ p.z -= 4.;
53
+ S = p;
54
+ d = p.y-T;
55
+
56
+ p.x += .4*(1.+p.y)*sin(d + p.x*0.1)*cos(.34*d + p.x*0.05);
57
+ Q = p.xz *= mat2(cos(p.y+vec4(0,11,33,0)-T));
58
+ z+= d = abs(sqrt(length(Q*Q)) - .25*(5.+S.y))/3.+8e-4;
59
+ o = 1.+sin(S.y+p.z*.5+S.z-length(S-p)+vec4(2,1,0,8));
60
+ }
61
+
62
+ o.xyz = tanh(O/1e4);
63
+ }
64
+
65
+ bool finite1(float x){ return !(isnan(x) || isinf(x)); }
66
+ vec3 sanitize(vec3 c){
67
+ return vec3(
68
+ finite1(c.r) ? c.r : 0.0,
69
+ finite1(c.g) ? c.g : 0.0,
70
+ finite1(c.b) ? c.b : 0.0
71
+ );
72
+ }
73
+
74
+ void main() {
75
+ vec4 o = vec4(0.0);
76
+ mainImage(o, gl_FragCoord.xy);
77
+ vec3 rgb = sanitize(o.rgb);
78
+
79
+ float intensity = (rgb.r + rgb.g + rgb.b) / 3.0;
80
+ vec3 customColor = intensity * uCustomColor;
81
+ vec3 finalColor = mix(rgb, customColor, step(0.5, uUseCustomColor));
82
+
83
+ float alpha = length(rgb) * uOpacity;
84
+ fragColor = vec4(finalColor, alpha);
85
+ }
86
+ `;
87
+
88
+
89
+ export const SmokeBackground = ({
90
+ color = "#00c950",
91
+ speed = 1,
92
+ direction = "forward",
93
+ scale = 1,
94
+ opacity = 1,
95
+ mouseInteractive = true,
96
+ className = "",
97
+ maxWidth = 'max-w-4xl',
98
+ }) => {
99
+ const containerRef = useRef(null);
100
+ const mousePos = useRef({ x: 0, y: 0 });
101
+
102
+ useEffect(() => {
103
+ const mountNode = containerRef.current;
104
+ if (!mountNode) return;
105
+
106
+ const useCustomColor = color ? 1.0 : 0.0;
107
+ const customColorRgb = color ? hexToRgb(color) : [1, 1, 1];
108
+
109
+ let directionMultiplier = direction === "backward" ? -1.0 : 1.0;
110
+
111
+ // cap DPR depending on mount width to avoid heavy pixel loads on small devices
112
+ const mountWidth = mountNode.clientWidth || window.innerWidth;
113
+ const dprCap = mountWidth > 1200 ? 1.8 : (mountWidth > 768 ? 1.4 : 1.0);
114
+ // The smoke is soft, so rendering at ~70% resolution and letting the browser upscale
115
+ // looks the same while the 35-step raymarch runs on half the pixels.
116
+ const dpr = Math.min(window.devicePixelRatio || 1, dprCap) * 0.7;
117
+
118
+ const renderer = new Renderer({
119
+ webgl: 2,
120
+ alpha: true,
121
+ antialias: false,
122
+ dpr,
123
+ });
124
+
125
+ const gl = renderer.gl;
126
+ const canvas = gl.canvas;
127
+ canvas.style.display = "block";
128
+ canvas.style.width = "100%";
129
+ canvas.style.height = "100%";
130
+ mountNode.appendChild(canvas);
131
+
132
+ const geometry = new Triangle(gl);
133
+
134
+ const program = new Program(gl, {
135
+ vertex: vertex,
136
+ fragment: fragment,
137
+ uniforms: {
138
+ iTime: { value: 0 },
139
+ iResolution: { value: new Float32Array([1, 1]) },
140
+ uCustomColor: { value: new Float32Array(customColorRgb) },
141
+ uUseCustomColor: { value: useCustomColor },
142
+ uSpeed: { value: speed * 0.4 },
143
+ uDirection: { value: directionMultiplier },
144
+ uScale: { value: scale },
145
+ uOpacity: { value: opacity },
146
+ uMouse: { value: new Float32Array([0, 0]) },
147
+ uMouseInteractive: { value: mouseInteractive ? 1.0 : 0.0 },
148
+ },
149
+ });
150
+
151
+ const mesh = new Mesh(gl, { geometry, program });
152
+
153
+ // 🖱 Mouse throttled to RAF
154
+ let mouseNeedsUpdate = false;
155
+ const handleMouseMove = (e) => {
156
+ if (!mouseInteractive) return;
157
+ const rect = mountNode.getBoundingClientRect();
158
+ mousePos.current.x = e.clientX - rect.left;
159
+ mousePos.current.y = rect.height - (e.clientY - rect.top);
160
+ mouseNeedsUpdate = true;
161
+ };
162
+ if (mouseInteractive) {
163
+ mountNode.addEventListener("mousemove", handleMouseMove, { passive: true });
164
+ }
165
+
166
+ const setSize = () => {
167
+ const rect = mountNode.getBoundingClientRect();
168
+ const width = Math.max(1, Math.floor(rect.width));
169
+ const height = Math.max(1, Math.floor(rect.height));
170
+ renderer.setSize(width, height);
171
+ const res = program.uniforms.iResolution.value;
172
+ res[0] = gl.drawingBufferWidth;
173
+ res[1] = gl.drawingBufferHeight;
174
+ };
175
+
176
+ const ro = new ResizeObserver(() => {
177
+ requestAnimationFrame(setSize);
178
+ });
179
+ ro.observe(mountNode);
180
+ setSize();
181
+
182
+ let raf = 0;
183
+ // Only render while the background is on screen.
184
+ let visible = true;
185
+ const t0 = performance.now();
186
+ const loop = (t) => {
187
+ raf = 0;
188
+ if (!visible) return;
189
+ let timeValue = (t - t0) * 0.001;
190
+
191
+ if (direction === "funny") {
192
+ const cycle = Math.sin(timeValue * 0.5);
193
+ program.uniforms.uDirection.value = cycle;
194
+ }
195
+
196
+ if (mouseNeedsUpdate) {
197
+ const mouseUniform = program.uniforms.uMouse.value;
198
+ mouseUniform[0] = mousePos.current.x;
199
+ mouseUniform[1] = mousePos.current.y;
200
+ mouseNeedsUpdate = false;
201
+ }
202
+
203
+ program.uniforms.iTime.value = timeValue;
204
+ renderer.render({ scene: mesh });
205
+ raf = requestAnimationFrame(loop);
206
+ };
207
+ raf = requestAnimationFrame(loop);
208
+ const visibilityObserver = new IntersectionObserver(([entry]) => {
209
+ visible = entry.isIntersecting;
210
+ if (visible && !raf) raf = requestAnimationFrame(loop);
211
+ });
212
+ visibilityObserver.observe(mountNode);
213
+
214
+ return () => {
215
+ cancelAnimationFrame(raf);
216
+ visibilityObserver.disconnect();
217
+ ro.disconnect();
218
+ if (mouseInteractive && mountNode) {
219
+ mountNode.removeEventListener("mousemove", handleMouseMove);
220
+ }
221
+ try {
222
+ mountNode.removeChild(canvas);
223
+ } catch (error) { console.error("Error removing canvas:", error); }
224
+ gl.getExtension("WEBGL_lose_context")?.loseContext();
225
+ };
226
+ }, [color, speed, direction, scale, opacity, mouseInteractive, className]);
227
+
228
+ return (
229
+ <div className={`w-full ${className}`}>
230
+ <div className={`mx-auto w-full ${maxWidth} h-[100vh] lg:h-[90vh] overflow-hidden relative`}>
231
+ <div ref={containerRef} className="absolute inset-0 w-full h-full" />
232
+ </div>
233
+ </div>
234
+ );
235
+ };
236
+
237
+ export default SmokeBackground;
package/dist/cli/index.js CHANGED
@@ -140,6 +140,8 @@ const componentMap = {
140
140
  "crossed-marquee": "CrossedMarquee",
141
141
  "theme-switcher": "ThemeSwitcher",
142
142
  "liquid-glass": "LiquidGlass",
143
+ "fluid-cursor": "FluidCursor",
144
+ "sketch-reveal": "SketchReveal",
143
145
  "water-drop-reveal": "MaskView",
144
146
  };
145
147
  // Packages only some components need, installed on top of the shared ones.
@@ -151,6 +153,31 @@ const extraDeps = {
151
153
  WebGLCarousel: ["ogl"],
152
154
  CrossedMarquee: ["simple-icons"],
153
155
  };
156
+ // Packages the app already has (or that ship with Node), never installed by us.
157
+ const BUILTIN = new Set(["react", "react-dom", "path", "fs", "url"]);
158
+ // The npm packages a component folder imports, read from its source files.
159
+ async function importedPackages(dir) {
160
+ const found = new Set();
161
+ const walk = async (d) => {
162
+ for (const entry of await fs.readdir(d, { withFileTypes: true })) {
163
+ const full = path.join(d, entry.name);
164
+ if (entry.isDirectory())
165
+ await walk(full);
166
+ else if (/\.(jsx?|tsx?|mjs)$/.test(entry.name)) {
167
+ const code = await fs.readFile(full, "utf8");
168
+ const re = /(?:from\s+|import\s*\(\s*|import\s+)["']([^"'.\/][^"']*)["']/g;
169
+ for (let m = re.exec(code); m; m = re.exec(code)) {
170
+ const spec = m[1];
171
+ const name = spec.startsWith("@") ? spec.split("/").slice(0, 2).join("/") : spec.split("/")[0];
172
+ if (!BUILTIN.has(name))
173
+ found.add(name);
174
+ }
175
+ }
176
+ }
177
+ };
178
+ await walk(dir);
179
+ return [...found];
180
+ }
154
181
  async function main() {
155
182
  const args = process.argv.slice(2);
156
183
  const command = args[0];
@@ -218,26 +245,6 @@ async function main() {
218
245
  { title: "TypeScript (Coming Soon)", value: "ts", disabled: true },
219
246
  ],
220
247
  });
221
- // Install dependencies
222
- console.log(chalk.cyan("📦 Installing dependencies..."));
223
- const deps = [
224
- "gsap",
225
- "framer-motion",
226
- "three",
227
- "@fortawesome/fontawesome-svg-core",
228
- "@fortawesome/free-solid-svg-icons",
229
- "@fortawesome/react-fontawesome",
230
- "react-icons",
231
- "lucide-react",
232
- ...(extraDeps[matchedName] ?? []),
233
- ];
234
- try {
235
- execSync(`npm install ${deps.join(" ")}`, { stdio: "inherit" });
236
- }
237
- catch (err) {
238
- console.log(chalk.red("❌ Failed to install dependencies."));
239
- return;
240
- }
241
248
  // Prepare destination
242
249
  const apexKitPath = path.join(basePath, "ApexUI-Kit");
243
250
  const dest = path.join(apexKitPath, matchedName);
@@ -248,6 +255,21 @@ async function main() {
248
255
  console.log(chalk.red(`❌ Internal error: Component folder '${matchedName}' not found.`));
249
256
  return;
250
257
  }
258
+ // Install only what this component imports, so the app stays light.
259
+ const deps = [...new Set([...(await importedPackages(source)), ...(extraDeps[matchedName] ?? [])])];
260
+ if (deps.length) {
261
+ console.log(chalk.cyan(`📦 Installing ${deps.join(", ")}...`));
262
+ try {
263
+ execSync(`npm install ${deps.join(" ")}`, { stdio: "inherit" });
264
+ }
265
+ catch (err) {
266
+ console.log(chalk.red("❌ Failed to install dependencies."));
267
+ return;
268
+ }
269
+ }
270
+ else {
271
+ console.log(chalk.cyan("📦 No extra packages needed."));
272
+ }
251
273
  await fs.ensureDir(dest);
252
274
  await fs.copy(source, dest);
253
275
  console.log(chalk.green(`✅ '${matchedName}' added to ${dest}`));
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "apex-ui-kit",
3
- "version": "2.9.0",
3
+ "version": "2.11.0",
4
4
  "license": "MIT",
5
- "description": "116 free, animated UI components for React, styled with Tailwind CSS. Add any of them with one CLI command.",
5
+ "description": "118 free, animated UI components for React, styled with Tailwind CSS. Add any of them with one CLI command.",
6
6
  "homepage": "https://apex-ui.in",
7
7
  "repository": {
8
8
  "type": "git",
@@ -60,5 +60,11 @@
60
60
  "@types/node": "^20.4.0",
61
61
  "copyfiles": "^2.4.1",
62
62
  "typescript": "^5.4.0"
63
+ },
64
+ "sideEffects": [
65
+ "*.css"
66
+ ],
67
+ "engines": {
68
+ "node": ">=18"
63
69
  }
64
70
  }