argentui 0.3.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/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/index.cjs +838 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +306 -0
- package/dist/index.d.ts +306 -0
- package/dist/index.js +794 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +289 -0
- package/package.json +63 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
// src/surfaces.tsx
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
|
|
4
|
+
// src/metal.tsx
|
|
5
|
+
import { useEffect, useRef, useState } from "react";
|
|
6
|
+
import { LiquidMetal } from "@paper-design/shaders-react";
|
|
7
|
+
|
|
8
|
+
// src/engine.ts
|
|
9
|
+
var VERT = `#version 300 es
|
|
10
|
+
void main() {
|
|
11
|
+
vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
|
12
|
+
gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
|
|
13
|
+
}`;
|
|
14
|
+
var FRAG = `#version 300 es
|
|
15
|
+
precision highp float;
|
|
16
|
+
uniform vec2 u_res;
|
|
17
|
+
uniform float u_time;
|
|
18
|
+
uniform vec3 u_light;
|
|
19
|
+
uniform vec3 u_dark;
|
|
20
|
+
uniform float u_rep;
|
|
21
|
+
uniform float u_angle;
|
|
22
|
+
uniform float u_soft;
|
|
23
|
+
uniform float u_disp;
|
|
24
|
+
uniform float u_dist;
|
|
25
|
+
uniform float u_scale;
|
|
26
|
+
out vec4 outColor;
|
|
27
|
+
|
|
28
|
+
// 2D simplex noise \u2014 Ashima Arts / Stefan Gustavson (webgl-noise, MIT).
|
|
29
|
+
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
|
30
|
+
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
|
31
|
+
vec3 permute(vec3 x) { return mod289(((x * 34.0) + 1.0) * x); }
|
|
32
|
+
float snoise(vec2 v) {
|
|
33
|
+
const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
|
|
34
|
+
vec2 i = floor(v + dot(v, C.yy));
|
|
35
|
+
vec2 x0 = v - i + dot(i, C.xx);
|
|
36
|
+
vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
|
37
|
+
vec4 x12 = x0.xyxy + C.xxzz;
|
|
38
|
+
x12.xy -= i1;
|
|
39
|
+
i = mod289(i);
|
|
40
|
+
vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
|
|
41
|
+
vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
|
|
42
|
+
m = m * m;
|
|
43
|
+
m = m * m;
|
|
44
|
+
vec3 x = 2.0 * fract(p * C.www) - 1.0;
|
|
45
|
+
vec3 h = abs(x) - 0.5;
|
|
46
|
+
vec3 ox = floor(x + 0.5);
|
|
47
|
+
vec3 a0 = x - ox;
|
|
48
|
+
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
|
|
49
|
+
vec3 g;
|
|
50
|
+
g.x = a0.x * x0.x + h.x * x0.y;
|
|
51
|
+
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
|
|
52
|
+
return 130.0 * dot(m, g);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// One reflection band: a soft triangle wave over the warped coordinate.
|
|
56
|
+
float stripe(float coord) {
|
|
57
|
+
float f = fract(coord);
|
|
58
|
+
float tri = abs(f * 2.0 - 1.0);
|
|
59
|
+
return smoothstep(0.5 - u_soft, 0.5 + u_soft, tri);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void main() {
|
|
63
|
+
vec2 uv = (gl_FragCoord.xy / u_res - 0.5);
|
|
64
|
+
uv.x *= u_res.x / u_res.y;
|
|
65
|
+
uv /= max(u_scale, 0.001);
|
|
66
|
+
|
|
67
|
+
float t = u_time;
|
|
68
|
+
|
|
69
|
+
// curvature: bands bend as if wrapping a bulged surface
|
|
70
|
+
float d = length(uv);
|
|
71
|
+
float bulge = 1.0 - 0.45 * d * d;
|
|
72
|
+
|
|
73
|
+
// flowing warp \u2014 two noise octaves drifting at different rates
|
|
74
|
+
float n = snoise(uv * 0.9 + vec2(0.0, -t * 0.28));
|
|
75
|
+
n += 0.35 * snoise(uv * 1.8 + vec2(t * 0.1, -t * 0.45));
|
|
76
|
+
|
|
77
|
+
// rotate so bands run along u_angle
|
|
78
|
+
float c = cos(u_angle), s = sin(u_angle);
|
|
79
|
+
vec2 ruv = mat2(c, -s, s, c) * uv;
|
|
80
|
+
|
|
81
|
+
float coord = ruv.y * bulge * u_rep + n * u_dist * 2.2 - t * 0.22;
|
|
82
|
+
|
|
83
|
+
// per-channel dispersion \u2014 the chrome fringing
|
|
84
|
+
float r = stripe(coord + u_disp);
|
|
85
|
+
float g = stripe(coord);
|
|
86
|
+
float b = stripe(coord - u_disp);
|
|
87
|
+
|
|
88
|
+
vec3 col = vec3(
|
|
89
|
+
mix(u_dark.r, u_light.r, r),
|
|
90
|
+
mix(u_dark.g, u_light.g, g),
|
|
91
|
+
mix(u_dark.b, u_light.b, b)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// rim lift toward the edges
|
|
95
|
+
col *= 0.94 + 0.14 * smoothstep(0.25, 0.85, d);
|
|
96
|
+
|
|
97
|
+
outColor = vec4(col, 1.0);
|
|
98
|
+
}`;
|
|
99
|
+
function hexToRgb(hex) {
|
|
100
|
+
const h = hex.replace("#", "");
|
|
101
|
+
const v = h.length === 3 ? h.split("").map((ch) => ch + ch).join("") : h;
|
|
102
|
+
const n = parseInt(v, 16);
|
|
103
|
+
return [(n >> 16 & 255) / 255, (n >> 8 & 255) / 255, (n & 255) / 255];
|
|
104
|
+
}
|
|
105
|
+
var NATIVE_TONES = {
|
|
106
|
+
silver: { light: "#f8fafc", dark: "#23262b", repetition: 1.8, angle: 68, softness: 0.34, dispersion: 0.03, distortion: 0.38 },
|
|
107
|
+
gold: { light: "#ffe9a8", dark: "#6e5408", repetition: 1.8, angle: 68, softness: 0.34, dispersion: 0.028, distortion: 0.36 },
|
|
108
|
+
gunmetal: { light: "#b6bec9", dark: "#14161a", repetition: 1.6, angle: 80, softness: 0.36, dispersion: 0.025, distortion: 0.34 },
|
|
109
|
+
obsidian: { light: "#787c88", dark: "#000000", repetition: 1.4, angle: 92, softness: 0.42, dispersion: 0.02, distortion: 0.28 }
|
|
110
|
+
};
|
|
111
|
+
function compile(gl, type, src) {
|
|
112
|
+
const sh = gl.createShader(type);
|
|
113
|
+
if (!sh) return null;
|
|
114
|
+
gl.shaderSource(sh, src);
|
|
115
|
+
gl.compileShader(sh);
|
|
116
|
+
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
|
|
117
|
+
console.error("argent shader:", gl.getShaderInfoLog(sh));
|
|
118
|
+
gl.deleteShader(sh);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
return sh;
|
|
122
|
+
}
|
|
123
|
+
function mountMetal(canvas, params) {
|
|
124
|
+
const gl = canvas.getContext("webgl2", { antialias: true });
|
|
125
|
+
if (!gl) return null;
|
|
126
|
+
const vs = compile(gl, gl.VERTEX_SHADER, VERT);
|
|
127
|
+
const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);
|
|
128
|
+
if (!vs || !fs) return null;
|
|
129
|
+
const prog = gl.createProgram();
|
|
130
|
+
gl.attachShader(prog, vs);
|
|
131
|
+
gl.attachShader(prog, fs);
|
|
132
|
+
gl.linkProgram(prog);
|
|
133
|
+
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
|
|
134
|
+
console.error("argent shader link:", gl.getProgramInfoLog(prog));
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
gl.useProgram(prog);
|
|
138
|
+
const U = (name) => gl.getUniformLocation(prog, name);
|
|
139
|
+
const uRes = U("u_res"), uTime = U("u_time"), uLight = U("u_light"), uDark = U("u_dark");
|
|
140
|
+
const uRep = U("u_rep"), uAngle = U("u_angle"), uSoft = U("u_soft");
|
|
141
|
+
const uDisp = U("u_disp"), uDist = U("u_dist"), uScale = U("u_scale");
|
|
142
|
+
let p = { ...params };
|
|
143
|
+
let raf = 0;
|
|
144
|
+
let tAcc = Math.random() * 40;
|
|
145
|
+
let last = performance.now();
|
|
146
|
+
let dead = false;
|
|
147
|
+
function draw() {
|
|
148
|
+
raf = 0;
|
|
149
|
+
if (dead) return;
|
|
150
|
+
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
|
151
|
+
const w = Math.max(1, Math.round(canvas.clientWidth * dpr));
|
|
152
|
+
const h = Math.max(1, Math.round(canvas.clientHeight * dpr));
|
|
153
|
+
if (canvas.width !== w || canvas.height !== h) {
|
|
154
|
+
canvas.width = w;
|
|
155
|
+
canvas.height = h;
|
|
156
|
+
gl.viewport(0, 0, w, h);
|
|
157
|
+
}
|
|
158
|
+
const now = performance.now();
|
|
159
|
+
tAcc += (now - last) / 1e3 * p.speed;
|
|
160
|
+
last = now;
|
|
161
|
+
gl.uniform2f(uRes, w, h);
|
|
162
|
+
gl.uniform1f(uTime, tAcc);
|
|
163
|
+
gl.uniform3fv(uLight, hexToRgb(p.light));
|
|
164
|
+
gl.uniform3fv(uDark, hexToRgb(p.dark));
|
|
165
|
+
gl.uniform1f(uRep, p.repetition);
|
|
166
|
+
gl.uniform1f(uAngle, p.angle * Math.PI / 180);
|
|
167
|
+
gl.uniform1f(uSoft, Math.max(0.02, p.softness));
|
|
168
|
+
gl.uniform1f(uDisp, p.dispersion);
|
|
169
|
+
gl.uniform1f(uDist, p.distortion);
|
|
170
|
+
gl.uniform1f(uScale, p.scale);
|
|
171
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
172
|
+
if (p.speed > 0) raf = requestAnimationFrame(draw);
|
|
173
|
+
}
|
|
174
|
+
draw();
|
|
175
|
+
return {
|
|
176
|
+
update(np) {
|
|
177
|
+
p = { ...p, ...np };
|
|
178
|
+
last = performance.now();
|
|
179
|
+
if (!raf) raf = requestAnimationFrame(draw);
|
|
180
|
+
},
|
|
181
|
+
destroy() {
|
|
182
|
+
dead = true;
|
|
183
|
+
if (raf) cancelAnimationFrame(raf);
|
|
184
|
+
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/metal.tsx
|
|
190
|
+
import { jsx } from "react/jsx-runtime";
|
|
191
|
+
var TONE_PARAMS = {
|
|
192
|
+
silver: {
|
|
193
|
+
colorBack: "#a7abb1",
|
|
194
|
+
colorTint: "#ffffff",
|
|
195
|
+
repetition: 3,
|
|
196
|
+
softness: 0.18,
|
|
197
|
+
shiftRed: 0.32,
|
|
198
|
+
shiftBlue: 0.32,
|
|
199
|
+
distortion: 0.14,
|
|
200
|
+
contour: 0.55,
|
|
201
|
+
angle: 68
|
|
202
|
+
},
|
|
203
|
+
gold: {
|
|
204
|
+
colorBack: "#94700e",
|
|
205
|
+
colorTint: "#ffedb0",
|
|
206
|
+
repetition: 3,
|
|
207
|
+
softness: 0.2,
|
|
208
|
+
shiftRed: 0.3,
|
|
209
|
+
shiftBlue: 0.12,
|
|
210
|
+
distortion: 0.13,
|
|
211
|
+
contour: 0.5,
|
|
212
|
+
angle: 68
|
|
213
|
+
},
|
|
214
|
+
gunmetal: {
|
|
215
|
+
colorBack: "#33373d",
|
|
216
|
+
colorTint: "#b2bac4",
|
|
217
|
+
repetition: 2.6,
|
|
218
|
+
softness: 0.26,
|
|
219
|
+
shiftRed: 0.22,
|
|
220
|
+
shiftBlue: 0.32,
|
|
221
|
+
distortion: 0.1,
|
|
222
|
+
contour: 0.45,
|
|
223
|
+
angle: 80
|
|
224
|
+
},
|
|
225
|
+
obsidian: {
|
|
226
|
+
colorBack: "#000000",
|
|
227
|
+
colorTint: "#9498a6",
|
|
228
|
+
repetition: 2,
|
|
229
|
+
softness: 0.4,
|
|
230
|
+
shiftRed: 0.14,
|
|
231
|
+
shiftBlue: 0.24,
|
|
232
|
+
distortion: 0.07,
|
|
233
|
+
contour: 0.32,
|
|
234
|
+
angle: 92
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
var imageMountQueue = Promise.resolve();
|
|
238
|
+
function useStaggeredMount() {
|
|
239
|
+
const [go, setGo] = useState(false);
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
let alive = true;
|
|
242
|
+
imageMountQueue = imageMountQueue.then(async () => {
|
|
243
|
+
if (!alive) return;
|
|
244
|
+
setGo(true);
|
|
245
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
246
|
+
});
|
|
247
|
+
return () => {
|
|
248
|
+
alive = false;
|
|
249
|
+
};
|
|
250
|
+
}, []);
|
|
251
|
+
return go;
|
|
252
|
+
}
|
|
253
|
+
function useMounted() {
|
|
254
|
+
const [m, setM] = useState(false);
|
|
255
|
+
useEffect(() => setM(true), []);
|
|
256
|
+
return m;
|
|
257
|
+
}
|
|
258
|
+
function useReducedMotion() {
|
|
259
|
+
const [reduced, setReduced] = useState(false);
|
|
260
|
+
useEffect(() => {
|
|
261
|
+
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
262
|
+
setReduced(mq.matches);
|
|
263
|
+
const onChange = (e) => setReduced(e.matches);
|
|
264
|
+
mq.addEventListener("change", onChange);
|
|
265
|
+
return () => mq.removeEventListener("change", onChange);
|
|
266
|
+
}, []);
|
|
267
|
+
return reduced;
|
|
268
|
+
}
|
|
269
|
+
function useInView(ref, margin = "250px") {
|
|
270
|
+
const [inView, setInView] = useState(false);
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
const el = ref.current;
|
|
273
|
+
if (!el) return;
|
|
274
|
+
const io = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting), { rootMargin: margin });
|
|
275
|
+
io.observe(el);
|
|
276
|
+
return () => io.disconnect();
|
|
277
|
+
}, [ref, margin]);
|
|
278
|
+
return inView;
|
|
279
|
+
}
|
|
280
|
+
var FINISHES = {
|
|
281
|
+
/** Cards, nav, panels — broad flowing reflection bands (the tone defaults). */
|
|
282
|
+
surface: { scale: 1.1 },
|
|
283
|
+
/** Buttons — slightly spread, calmer warp so labels stay readable. */
|
|
284
|
+
button: { scale: 1.4, distortion: 0.1 },
|
|
285
|
+
/** Thin horizontal strips (progress) — near-vertical stripes crossing the bar. */
|
|
286
|
+
bar: { angle: 14, repetition: 5, softness: 0.3, distortion: 0.06, shift: 0.5, scale: 0.8, speed: 0.75 },
|
|
287
|
+
/** Small round things (toggle thumbs) — one soft highlight, like a polished sphere. */
|
|
288
|
+
orb: { angle: 112, angleJitter: 20, repetition: 1.7, softness: 0.5, distortion: 0.05, shift: 0.4, scale: 1.5 },
|
|
289
|
+
/** Hairline edges (badges, thin borders) — dense bands so any slice sparkles. */
|
|
290
|
+
rim: { angle: 40, repetition: 4.5, softness: 0.3, distortion: 0.08, shift: 0.5, scale: 0.85, speed: 0.85 }
|
|
291
|
+
};
|
|
292
|
+
var EFFECTS = {
|
|
293
|
+
/** The default — steady flowing reflection bands. */
|
|
294
|
+
flow: {},
|
|
295
|
+
/** Slow, heavy, half-melted — soft wide bands churning lazily. */
|
|
296
|
+
molten: { softness: 0.5, distortion: 0.3, speed: 0.45, shift: 0.6 },
|
|
297
|
+
/** Agitated surface — tighter bands, hard noise, quick motion. */
|
|
298
|
+
ripple: { repetition: 4, softness: 0.24, distortion: 0.5, speed: 1.4 },
|
|
299
|
+
/** Mirror-polished — crisp hard bands, strong chromatic fringe, calm. */
|
|
300
|
+
chrome: { softness: 0.05, distortion: 0.05, shift: 1.6, speed: 0.8 },
|
|
301
|
+
/** Horizontal swells rolling through the surface. */
|
|
302
|
+
wave: { angle: 0, repetition: 5, softness: 0.36, distortion: 0.18, speed: 0.7 }
|
|
303
|
+
};
|
|
304
|
+
var FILL_STYLE = { position: "absolute", inset: 0, width: "100%", height: "100%" };
|
|
305
|
+
function NativeCanvas({ params }) {
|
|
306
|
+
const canvasRef = useRef(null);
|
|
307
|
+
const mountRef = useRef(null);
|
|
308
|
+
const initial = useRef(params);
|
|
309
|
+
initial.current = params;
|
|
310
|
+
useEffect(() => {
|
|
311
|
+
const canvas = canvasRef.current;
|
|
312
|
+
if (!canvas) return;
|
|
313
|
+
mountRef.current = mountMetal(canvas, initial.current);
|
|
314
|
+
return () => {
|
|
315
|
+
mountRef.current?.destroy();
|
|
316
|
+
mountRef.current = null;
|
|
317
|
+
};
|
|
318
|
+
}, []);
|
|
319
|
+
useEffect(() => {
|
|
320
|
+
mountRef.current?.update(params);
|
|
321
|
+
}, [params]);
|
|
322
|
+
return /* @__PURE__ */ jsx("canvas", { ref: canvasRef, style: { ...FILL_STYLE, display: "block" } });
|
|
323
|
+
}
|
|
324
|
+
function MetalFill({ tone, speed = 1, scale, engine = "paper", finish = "surface", effect = "flow", angle }) {
|
|
325
|
+
const ref = useRef(null);
|
|
326
|
+
const mounted = useMounted();
|
|
327
|
+
const inView = useInView(ref);
|
|
328
|
+
const reduced = useReducedMotion();
|
|
329
|
+
const f = FINISHES[finish];
|
|
330
|
+
const e = EFFECTS[effect];
|
|
331
|
+
const [jitter] = useState(() => f.angleJitter ? (Math.random() * 2 - 1) * f.angleJitter : 0);
|
|
332
|
+
const base = TONE_PARAMS[tone];
|
|
333
|
+
const effAngle = (angle ?? e.angle ?? f.angle ?? base.angle ?? 70) + jitter;
|
|
334
|
+
const effScale = scale ?? f.scale ?? 1.1;
|
|
335
|
+
const effSpeed = (reduced ? 0 : speed) * (f.speed ?? 1) * (e.speed ?? 1);
|
|
336
|
+
const shift = (f.shift ?? 1) * (e.shift ?? 1);
|
|
337
|
+
const repetition = e.repetition ?? f.repetition ?? base.repetition;
|
|
338
|
+
const softness = e.softness ?? f.softness ?? base.softness;
|
|
339
|
+
const distortion = e.distortion ?? f.distortion ?? base.distortion;
|
|
340
|
+
const native = NATIVE_TONES[tone];
|
|
341
|
+
const nativeParams = {
|
|
342
|
+
...native,
|
|
343
|
+
angle: effAngle,
|
|
344
|
+
repetition: repetition ?? native.repetition,
|
|
345
|
+
softness: softness ?? native.softness,
|
|
346
|
+
// the native warp amount runs ~3× paper's distortion scale
|
|
347
|
+
distortion: distortion !== void 0 ? distortion * 3 : native.distortion,
|
|
348
|
+
dispersion: native.dispersion * shift,
|
|
349
|
+
speed: effSpeed,
|
|
350
|
+
scale: effScale
|
|
351
|
+
};
|
|
352
|
+
return /* @__PURE__ */ jsx("span", { ref, "aria-hidden": "true", style: { position: "absolute", inset: 0 }, children: mounted && inView && (engine === "native" ? /* @__PURE__ */ jsx(NativeCanvas, { params: nativeParams }) : /* @__PURE__ */ jsx(
|
|
353
|
+
LiquidMetal,
|
|
354
|
+
{
|
|
355
|
+
shape: "none",
|
|
356
|
+
fit: "cover",
|
|
357
|
+
scale: effScale,
|
|
358
|
+
speed: effSpeed,
|
|
359
|
+
...base,
|
|
360
|
+
angle: effAngle,
|
|
361
|
+
repetition,
|
|
362
|
+
softness,
|
|
363
|
+
distortion,
|
|
364
|
+
shiftRed: (base.shiftRed ?? 0.3) * shift,
|
|
365
|
+
shiftBlue: (base.shiftBlue ?? 0.3) * shift,
|
|
366
|
+
style: FILL_STYLE
|
|
367
|
+
}
|
|
368
|
+
)) });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/haptics.ts
|
|
372
|
+
var enabled = true;
|
|
373
|
+
function setHaptics(on) {
|
|
374
|
+
enabled = on;
|
|
375
|
+
}
|
|
376
|
+
function vibrate(pattern) {
|
|
377
|
+
if (!enabled || typeof navigator === "undefined" || !("vibrate" in navigator)) return;
|
|
378
|
+
try {
|
|
379
|
+
navigator.vibrate(pattern);
|
|
380
|
+
} catch {
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
var PRESS_PATTERN = 8;
|
|
384
|
+
|
|
385
|
+
// src/surfaces.tsx
|
|
386
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
387
|
+
function cx(...parts) {
|
|
388
|
+
return parts.filter(Boolean).join(" ");
|
|
389
|
+
}
|
|
390
|
+
function assignRef(ref, node) {
|
|
391
|
+
if (typeof ref === "function") ref(node);
|
|
392
|
+
else if (ref) ref.current = node;
|
|
393
|
+
}
|
|
394
|
+
function buildVars(radius, borderWidth, halo, style) {
|
|
395
|
+
const vars = {
|
|
396
|
+
borderRadius: radius,
|
|
397
|
+
"--argent-bw": `${borderWidth}px`,
|
|
398
|
+
"--argent-radius": `${radius}px`,
|
|
399
|
+
...style
|
|
400
|
+
};
|
|
401
|
+
if (halo) vars["--argent-halo"] = `${halo === true ? 8 : halo}px`;
|
|
402
|
+
return vars;
|
|
403
|
+
}
|
|
404
|
+
var Metal = forwardRef(function Metal2({
|
|
405
|
+
as,
|
|
406
|
+
tone = "silver",
|
|
407
|
+
variant = "border",
|
|
408
|
+
frame = "single",
|
|
409
|
+
tint = false,
|
|
410
|
+
borderWidth = 1.5,
|
|
411
|
+
revealOnHover = false,
|
|
412
|
+
radius = 14,
|
|
413
|
+
speed,
|
|
414
|
+
metalScale,
|
|
415
|
+
engine,
|
|
416
|
+
finish,
|
|
417
|
+
effect,
|
|
418
|
+
angle,
|
|
419
|
+
sheen = false,
|
|
420
|
+
halo = false,
|
|
421
|
+
className,
|
|
422
|
+
style,
|
|
423
|
+
children,
|
|
424
|
+
...rest
|
|
425
|
+
}, ref) {
|
|
426
|
+
const Tag = as ?? "div";
|
|
427
|
+
const border = variant === "border";
|
|
428
|
+
return /* @__PURE__ */ jsxs(
|
|
429
|
+
Tag,
|
|
430
|
+
{
|
|
431
|
+
ref: (node) => assignRef(ref, node),
|
|
432
|
+
className: cx(
|
|
433
|
+
"argent",
|
|
434
|
+
border ? "argent--border" : "argent--fill",
|
|
435
|
+
border && frame === "double" && "argent--double",
|
|
436
|
+
border && tint && "argent--tint",
|
|
437
|
+
revealOnHover && "argent--reveal",
|
|
438
|
+
sheen && "argent--sheen",
|
|
439
|
+
!!halo && "argent--halo",
|
|
440
|
+
className
|
|
441
|
+
),
|
|
442
|
+
"data-tone": tone,
|
|
443
|
+
style: buildVars(radius, borderWidth, halo, style),
|
|
444
|
+
...rest,
|
|
445
|
+
children: [
|
|
446
|
+
/* @__PURE__ */ jsx2("span", { className: "argent-fill", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(MetalFill, { tone, speed, scale: metalScale, engine, finish, effect, angle }) }),
|
|
447
|
+
border && /* @__PURE__ */ jsx2("span", { className: "argent-core", "aria-hidden": "true" }),
|
|
448
|
+
sheen && /* @__PURE__ */ jsx2("span", { className: "argent-sheen", "aria-hidden": "true" }),
|
|
449
|
+
/* @__PURE__ */ jsx2("span", { className: "argent-content", children })
|
|
450
|
+
]
|
|
451
|
+
}
|
|
452
|
+
);
|
|
453
|
+
});
|
|
454
|
+
var MetalCard = forwardRef(function MetalCard2({ className, radius = 18, ...rest }, ref) {
|
|
455
|
+
return /* @__PURE__ */ jsx2(Metal, { ref, radius, className: cx("argent-card", className), ...rest });
|
|
456
|
+
});
|
|
457
|
+
var SIZE_RADIUS = { sm: 11, md: 13, lg: 15 };
|
|
458
|
+
var MetalButton = forwardRef(function MetalButton2({ tone = "silver", size = "md", variant = "border", radius, borderWidth = 1.5, revealOnHover = true, haptics = true, speed, engine, finish = "button", effect, angle, halo = false, type = "button", className, children, style, onPointerDown, ...rest }, ref) {
|
|
459
|
+
const border = variant === "border";
|
|
460
|
+
return /* @__PURE__ */ jsxs(
|
|
461
|
+
"button",
|
|
462
|
+
{
|
|
463
|
+
ref,
|
|
464
|
+
type,
|
|
465
|
+
onPointerDown: (e) => {
|
|
466
|
+
if (haptics) vibrate(PRESS_PATTERN);
|
|
467
|
+
onPointerDown?.(e);
|
|
468
|
+
},
|
|
469
|
+
className: cx("argent", border ? "argent--border" : "argent--fill", border && revealOnHover && "argent--reveal", "argent--sheen", !!halo && "argent--halo", "argent-btn", `argent-btn--${size}`, className),
|
|
470
|
+
"data-tone": tone,
|
|
471
|
+
style: buildVars(radius ?? SIZE_RADIUS[size], borderWidth, halo, style),
|
|
472
|
+
...rest,
|
|
473
|
+
children: [
|
|
474
|
+
/* @__PURE__ */ jsx2("span", { className: "argent-fill", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(MetalFill, { tone, speed, engine, finish, effect, angle }) }),
|
|
475
|
+
border && /* @__PURE__ */ jsx2("span", { className: "argent-core", "aria-hidden": "true" }),
|
|
476
|
+
/* @__PURE__ */ jsx2("span", { className: "argent-sheen", "aria-hidden": "true" }),
|
|
477
|
+
/* @__PURE__ */ jsx2("span", { className: "argent-content argent-btn-label", children })
|
|
478
|
+
]
|
|
479
|
+
}
|
|
480
|
+
);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// src/controls.tsx
|
|
484
|
+
import { forwardRef as forwardRef2, useState as useState2 } from "react";
|
|
485
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
486
|
+
var MetalToggle = forwardRef2(function MetalToggle2({ tone = "silver", checked, defaultChecked = false, onCheckedChange, haptics = true, className, onClick, ...rest }, ref) {
|
|
487
|
+
const [internal, setInternal] = useState2(defaultChecked);
|
|
488
|
+
const isOn = checked ?? internal;
|
|
489
|
+
return /* @__PURE__ */ jsx3(
|
|
490
|
+
"button",
|
|
491
|
+
{
|
|
492
|
+
ref,
|
|
493
|
+
type: "button",
|
|
494
|
+
role: "switch",
|
|
495
|
+
"aria-checked": isOn,
|
|
496
|
+
"data-tone": tone,
|
|
497
|
+
"data-checked": isOn || void 0,
|
|
498
|
+
className: ["argent-toggle", className].filter(Boolean).join(" "),
|
|
499
|
+
onClick: (e) => {
|
|
500
|
+
if (haptics) vibrate(PRESS_PATTERN);
|
|
501
|
+
if (checked === void 0) setInternal(!isOn);
|
|
502
|
+
onCheckedChange?.(!isOn);
|
|
503
|
+
onClick?.(e);
|
|
504
|
+
},
|
|
505
|
+
...rest,
|
|
506
|
+
children: /* @__PURE__ */ jsx3("span", { className: "argent-toggle-thumb", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(MetalFill, { tone, finish: "orb" }) })
|
|
507
|
+
}
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
var MetalProgress = forwardRef2(function MetalProgress2({ tone = "silver", value, height = 10, className, style, ...rest }, ref) {
|
|
511
|
+
const indeterminate = value === void 0;
|
|
512
|
+
const clamped = indeterminate ? 0 : Math.max(0, Math.min(100, value));
|
|
513
|
+
return /* @__PURE__ */ jsx3(
|
|
514
|
+
"div",
|
|
515
|
+
{
|
|
516
|
+
ref,
|
|
517
|
+
role: "progressbar",
|
|
518
|
+
"aria-valuemin": 0,
|
|
519
|
+
"aria-valuemax": 100,
|
|
520
|
+
"aria-valuenow": indeterminate ? void 0 : clamped,
|
|
521
|
+
"data-tone": tone,
|
|
522
|
+
className: ["argent-progress", indeterminate && "argent-progress--indeterminate", className].filter(Boolean).join(" "),
|
|
523
|
+
style: { height, ...style },
|
|
524
|
+
...rest,
|
|
525
|
+
children: /* @__PURE__ */ jsx3(
|
|
526
|
+
"span",
|
|
527
|
+
{
|
|
528
|
+
className: "argent-progress-fill",
|
|
529
|
+
style: indeterminate ? void 0 : { width: `${clamped}%` },
|
|
530
|
+
"aria-hidden": "true",
|
|
531
|
+
children: /* @__PURE__ */ jsx3(MetalFill, { tone, finish: "bar" })
|
|
532
|
+
}
|
|
533
|
+
)
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
});
|
|
537
|
+
var MetalBadge = forwardRef2(function MetalBadge2({ className, radius = 999, borderWidth = 1, finish = "rim", ...rest }, ref) {
|
|
538
|
+
return /* @__PURE__ */ jsx3(
|
|
539
|
+
Metal,
|
|
540
|
+
{
|
|
541
|
+
ref,
|
|
542
|
+
as: "span",
|
|
543
|
+
variant: "border",
|
|
544
|
+
radius,
|
|
545
|
+
borderWidth,
|
|
546
|
+
finish,
|
|
547
|
+
className: ["argent-badge", className].filter(Boolean).join(" "),
|
|
548
|
+
...rest
|
|
549
|
+
}
|
|
550
|
+
);
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
// src/text.tsx
|
|
554
|
+
import { forwardRef as forwardRef3, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
555
|
+
import { LiquidMetal as LiquidMetal2 } from "@paper-design/shaders-react";
|
|
556
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
557
|
+
var DEFAULT_STACK = "-apple-system, 'Helvetica Neue', Helvetica, Arial, sans-serif";
|
|
558
|
+
function svgOpen(g) {
|
|
559
|
+
const style = g.fontCss ? `<style>${g.fontCss}</style>` : "";
|
|
560
|
+
return `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${g.w} ${g.h}' width='${g.w}' height='${g.h}'>${style}`;
|
|
561
|
+
}
|
|
562
|
+
function svgText(g, attrs) {
|
|
563
|
+
const safe = g.text.replace(/&/g, "&").replace(/</g, "<");
|
|
564
|
+
return `<text x='50%' y='${Math.round(g.fontSize * 1)}' font-size='${g.fontSize}' font-family="${g.fontFamily.replace(/"/g, "'")}" font-weight='${g.fontWeight}' text-anchor='middle' ${attrs}>${safe}</text>`;
|
|
565
|
+
}
|
|
566
|
+
function encode(svg) {
|
|
567
|
+
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
|
568
|
+
}
|
|
569
|
+
function fillSilhouette(g) {
|
|
570
|
+
return encode(`${svgOpen(g)}${svgText(g, "fill='#000'")}</svg>`);
|
|
571
|
+
}
|
|
572
|
+
function outlineSilhouette(g, ow) {
|
|
573
|
+
return encode(`${svgOpen(g)}${svgText(g, `fill='none' stroke='#000' stroke-width='${ow}' stroke-linejoin='round'`)}</svg>`);
|
|
574
|
+
}
|
|
575
|
+
function interior(g, fill, gradient) {
|
|
576
|
+
if (!gradient) return encode(`${svgOpen(g)}${svgText(g, `fill='${fill}'`)}</svg>`);
|
|
577
|
+
const defs = `<defs><linearGradient id='g' x1='0' y1='0' x2='0' y2='1'><stop offset='0' stop-color='${gradient[0]}'/><stop offset='1' stop-color='${gradient[1]}'/></linearGradient></defs>`;
|
|
578
|
+
return encode(`${svgOpen(g)}${defs}${svgText(g, "fill='url(#g)'")}</svg>`);
|
|
579
|
+
}
|
|
580
|
+
var LAYER = { position: "absolute", inset: 0, width: "100%", height: "100%" };
|
|
581
|
+
function ShaderText({
|
|
582
|
+
text,
|
|
583
|
+
tone,
|
|
584
|
+
variant,
|
|
585
|
+
fill,
|
|
586
|
+
fillGradient,
|
|
587
|
+
outlineWidth,
|
|
588
|
+
fontSize,
|
|
589
|
+
fontWeight,
|
|
590
|
+
fontFamily,
|
|
591
|
+
fontCss,
|
|
592
|
+
speed,
|
|
593
|
+
shimmer,
|
|
594
|
+
className,
|
|
595
|
+
style,
|
|
596
|
+
...rest
|
|
597
|
+
}) {
|
|
598
|
+
const ref = useRef2(null);
|
|
599
|
+
const mounted = useMounted();
|
|
600
|
+
const inView = useInView(ref);
|
|
601
|
+
const reduced = useReducedMotion();
|
|
602
|
+
const turn = useStaggeredMount();
|
|
603
|
+
const [geom, setGeom] = useState3(null);
|
|
604
|
+
const outlined = variant === "outline";
|
|
605
|
+
const ow = outlineWidth ?? Math.max(2, Math.round(fontSize * 0.05));
|
|
606
|
+
useEffect2(() => {
|
|
607
|
+
let alive = true;
|
|
608
|
+
const fontSpec = `${fontWeight} ${fontSize}px ${fontFamily}`;
|
|
609
|
+
const measure = () => {
|
|
610
|
+
if (!alive) return;
|
|
611
|
+
const ctx = document.createElement("canvas").getContext("2d");
|
|
612
|
+
if (!ctx) return;
|
|
613
|
+
ctx.font = fontSpec;
|
|
614
|
+
const m = ctx.measureText(text);
|
|
615
|
+
setGeom({
|
|
616
|
+
text,
|
|
617
|
+
w: Math.ceil(m.width + fontSize * 0.24 + ow * 2),
|
|
618
|
+
h: Math.ceil(fontSize * 1.3 + ow),
|
|
619
|
+
fontSize,
|
|
620
|
+
fontWeight,
|
|
621
|
+
fontFamily,
|
|
622
|
+
fontCss
|
|
623
|
+
});
|
|
624
|
+
};
|
|
625
|
+
if (document.fonts?.load) document.fonts.load(fontSpec, text).then(measure, measure);
|
|
626
|
+
else measure();
|
|
627
|
+
return () => {
|
|
628
|
+
alive = false;
|
|
629
|
+
};
|
|
630
|
+
}, [text, fontSize, fontWeight, fontFamily, fontCss, ow]);
|
|
631
|
+
const ready = mounted && inView && turn && geom;
|
|
632
|
+
const { colorBack: _drop, ...params } = TONE_PARAMS[tone];
|
|
633
|
+
const shaderParams = outlined ? { ...params, contour: 0.2, distortion: Math.min(params.distortion ?? 0.1, 0.08) } : params;
|
|
634
|
+
return /* @__PURE__ */ jsx4(
|
|
635
|
+
"span",
|
|
636
|
+
{
|
|
637
|
+
ref,
|
|
638
|
+
role: "img",
|
|
639
|
+
"aria-label": text,
|
|
640
|
+
className,
|
|
641
|
+
style: {
|
|
642
|
+
position: "relative",
|
|
643
|
+
display: "inline-block",
|
|
644
|
+
verticalAlign: "middle",
|
|
645
|
+
width: geom?.w,
|
|
646
|
+
height: geom?.h,
|
|
647
|
+
...style
|
|
648
|
+
},
|
|
649
|
+
...rest,
|
|
650
|
+
children: ready ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
651
|
+
outlined && /* @__PURE__ */ jsx4("img", { src: interior(geom, fill, fillGradient), alt: "", "aria-hidden": "true", style: LAYER }),
|
|
652
|
+
/* @__PURE__ */ jsx4(
|
|
653
|
+
LiquidMetal2,
|
|
654
|
+
{
|
|
655
|
+
image: outlined ? outlineSilhouette(geom, ow) : fillSilhouette(geom),
|
|
656
|
+
suspendWhenProcessingImage: false,
|
|
657
|
+
colorBack: "#00000000",
|
|
658
|
+
fit: "contain",
|
|
659
|
+
scale: 0.97,
|
|
660
|
+
speed: reduced ? 0 : speed,
|
|
661
|
+
...shaderParams,
|
|
662
|
+
style: LAYER
|
|
663
|
+
},
|
|
664
|
+
`${geom.text}|${geom.fontFamily}|${geom.w}|${outlined ? "o" : "f"}`
|
|
665
|
+
)
|
|
666
|
+
] }) : (
|
|
667
|
+
// CSS chrome stands in until the shader is ready (and during SSR)
|
|
668
|
+
/* @__PURE__ */ jsx4(
|
|
669
|
+
"span",
|
|
670
|
+
{
|
|
671
|
+
"aria-hidden": "true",
|
|
672
|
+
className: ["argent-text", shimmer && !outlined && "argent-text--shimmer"].filter(Boolean).join(" "),
|
|
673
|
+
"data-tone": tone,
|
|
674
|
+
style: {
|
|
675
|
+
fontSize,
|
|
676
|
+
fontWeight,
|
|
677
|
+
fontFamily,
|
|
678
|
+
lineHeight: 1.3,
|
|
679
|
+
whiteSpace: "nowrap",
|
|
680
|
+
...outlined && {
|
|
681
|
+
background: "none",
|
|
682
|
+
WebkitTextFillColor: fillGradient?.[0] ?? fill,
|
|
683
|
+
color: fillGradient?.[0] ?? fill,
|
|
684
|
+
WebkitTextStroke: "1px rgba(220, 224, 230, 0.55)"
|
|
685
|
+
}
|
|
686
|
+
},
|
|
687
|
+
children: text
|
|
688
|
+
}
|
|
689
|
+
)
|
|
690
|
+
)
|
|
691
|
+
}
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
var MetalText = forwardRef3(function MetalText2({
|
|
695
|
+
as,
|
|
696
|
+
tone = "silver",
|
|
697
|
+
shimmer = true,
|
|
698
|
+
shader = false,
|
|
699
|
+
variant = "fill",
|
|
700
|
+
fill = "#101114",
|
|
701
|
+
fillGradient,
|
|
702
|
+
outlineWidth,
|
|
703
|
+
fontSize = 64,
|
|
704
|
+
fontWeight = 800,
|
|
705
|
+
fontFamily = DEFAULT_STACK,
|
|
706
|
+
fontCss,
|
|
707
|
+
speed = 1,
|
|
708
|
+
className,
|
|
709
|
+
children,
|
|
710
|
+
...rest
|
|
711
|
+
}, ref) {
|
|
712
|
+
if (shader) {
|
|
713
|
+
return /* @__PURE__ */ jsx4(
|
|
714
|
+
ShaderText,
|
|
715
|
+
{
|
|
716
|
+
text: String(children),
|
|
717
|
+
tone,
|
|
718
|
+
variant,
|
|
719
|
+
fill,
|
|
720
|
+
fillGradient,
|
|
721
|
+
outlineWidth,
|
|
722
|
+
fontSize,
|
|
723
|
+
fontWeight,
|
|
724
|
+
fontFamily,
|
|
725
|
+
fontCss,
|
|
726
|
+
speed,
|
|
727
|
+
shimmer,
|
|
728
|
+
className,
|
|
729
|
+
...rest
|
|
730
|
+
}
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
const Tag = as ?? "span";
|
|
734
|
+
return /* @__PURE__ */ jsx4(
|
|
735
|
+
Tag,
|
|
736
|
+
{
|
|
737
|
+
ref,
|
|
738
|
+
className: ["argent-text", shimmer && "argent-text--shimmer", className].filter(Boolean).join(" "),
|
|
739
|
+
"data-tone": tone,
|
|
740
|
+
...rest,
|
|
741
|
+
children
|
|
742
|
+
}
|
|
743
|
+
);
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
// src/logo.tsx
|
|
747
|
+
import { useRef as useRef3 } from "react";
|
|
748
|
+
import { LiquidMetal as LiquidMetal3 } from "@paper-design/shaders-react";
|
|
749
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
750
|
+
function MetalLogo({ src, tone = "silver", size = 160, width, height, speed = 1, style, ...rest }) {
|
|
751
|
+
const ref = useRef3(null);
|
|
752
|
+
const mounted = useMounted();
|
|
753
|
+
const inView = useInView(ref);
|
|
754
|
+
const reduced = useReducedMotion();
|
|
755
|
+
const turn = useStaggeredMount();
|
|
756
|
+
const w = width ?? size;
|
|
757
|
+
const h = height ?? size;
|
|
758
|
+
const { colorBack: _drop, ...params } = TONE_PARAMS[tone];
|
|
759
|
+
return /* @__PURE__ */ jsx5("div", { ref, style: { position: "relative", width: w, height: h, ...style }, ...rest, children: mounted && inView && turn && /* @__PURE__ */ jsx5(
|
|
760
|
+
LiquidMetal3,
|
|
761
|
+
{
|
|
762
|
+
image: src,
|
|
763
|
+
suspendWhenProcessingImage: false,
|
|
764
|
+
colorBack: "#00000000",
|
|
765
|
+
fit: "contain",
|
|
766
|
+
scale: 0.92,
|
|
767
|
+
speed: reduced ? 0 : speed,
|
|
768
|
+
...params,
|
|
769
|
+
style: { position: "absolute", inset: 0, width: "100%", height: "100%" }
|
|
770
|
+
},
|
|
771
|
+
src
|
|
772
|
+
) });
|
|
773
|
+
}
|
|
774
|
+
export {
|
|
775
|
+
EFFECTS,
|
|
776
|
+
FINISHES,
|
|
777
|
+
Metal,
|
|
778
|
+
MetalBadge,
|
|
779
|
+
MetalButton,
|
|
780
|
+
MetalCard,
|
|
781
|
+
MetalFill,
|
|
782
|
+
MetalLogo,
|
|
783
|
+
MetalProgress,
|
|
784
|
+
MetalText,
|
|
785
|
+
MetalToggle,
|
|
786
|
+
NATIVE_TONES,
|
|
787
|
+
TONE_PARAMS,
|
|
788
|
+
mountMetal,
|
|
789
|
+
setHaptics,
|
|
790
|
+
useInView,
|
|
791
|
+
useMounted,
|
|
792
|
+
useReducedMotion
|
|
793
|
+
};
|
|
794
|
+
//# sourceMappingURL=index.js.map
|