fancy-ui-react 0.2.1 → 0.4.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 +56 -20
- package/dist/components/animated-beam/AnimatedBeam.d.ts +47 -11
- package/dist/components/animated-beam/AnimatedBeam.js +293 -118
- package/dist/components/animated-tooltip/AnimatedTooltip.d.ts +10 -1
- package/dist/components/animated-tooltip/AnimatedTooltip.js +170 -101
- package/dist/components/bento-grid/BentoFrame.d.ts +16 -0
- package/dist/components/bento-grid/BentoFrame.js +28 -0
- package/dist/components/bento-grid/BentoGrid.d.ts +5 -1
- package/dist/components/bento-grid/BentoGrid.js +71 -2
- package/dist/components/bento-grid/BentoGridCard.js +34 -26
- package/dist/components/bento-grid/BentoGridItem.js +10 -10
- package/dist/components/border-beam/BorderBeam.d.ts +55 -10
- package/dist/components/border-beam/BorderBeam.js +93 -16
- package/dist/components/button/Button.js +5 -1
- package/dist/components/compare/Compare.d.ts +32 -8
- package/dist/components/compare/Compare.js +213 -95
- package/dist/components/container-scroll/ContainerScroll.d.ts +26 -4
- package/dist/components/container-scroll/ContainerScroll.js +88 -49
- package/dist/components/datamosh-transition/DatamoshTransition.d.ts +76 -0
- package/dist/components/datamosh-transition/DatamoshTransition.js +368 -0
- package/dist/components/datamosh-transition/datamosh-transition-core.d.ts +180 -0
- package/dist/components/datamosh-transition/datamosh-transition-core.js +370 -0
- package/dist/components/datamosh-transition/index.d.ts +3 -0
- package/dist/components/dock/Dock.d.ts +10 -7
- package/dist/components/dock/Dock.js +76 -31
- package/dist/components/dock/DockIcon.d.ts +14 -3
- package/dist/components/dock/DockIcon.js +23 -19
- package/dist/components/dock/DockSeparator.d.ts +4 -1
- package/dist/components/dock/DockSeparator.js +5 -2
- package/dist/components/dock/types.d.ts +13 -6
- package/dist/components/dropdown-menu/menu-shared.d.ts +11 -8
- package/dist/components/editorial-engine/engine.js +1 -14
- package/dist/components/fireworks-hdr/webgl2-renderer.js +0 -19
- package/dist/components/fireworks-hdr/webgpu-renderer.js +3 -22
- package/dist/components/flip-card/FlipCard.d.ts +42 -1
- package/dist/components/flip-card/FlipCard.js +178 -25
- package/dist/components/flip-card/index.d.ts +1 -1
- package/dist/components/fluid-cursor/FluidCursor.js +5 -4
- package/dist/components/fluid-cursor/dev.d.ts +1 -0
- package/dist/components/fluid-cursor/dev.js +8 -0
- package/dist/components/fluid-cursor/fluid-shared.d.ts +0 -2
- package/dist/components/fluid-cursor/fluid-shared.js +0 -2
- package/dist/components/fluid-cursor/webgpu-engine.js +10 -24
- package/dist/components/glow-border/GlowBorder.d.ts +41 -1
- package/dist/components/glow-border/GlowBorder.js +96 -16
- package/dist/components/glow-border/index.d.ts +2 -2
- package/dist/components/gradient-button/GradientButton.d.ts +10 -5
- package/dist/components/gradient-button/GradientButton.js +25 -18
- package/dist/components/image-trail-cursor/ImageTrailCursor.js +1 -0
- package/dist/components/image-trail-cursor/trail-variants.d.ts +37 -1
- package/dist/components/image-trail-cursor/trail-variants.js +331 -8
- package/dist/components/interactive-hover-button/InteractiveHoverButton.js +30 -35
- package/dist/components/marquee/Marquee.d.ts +7 -3
- package/dist/components/marquee/Marquee.js +134 -2
- package/dist/components/marquee/ReviewCard.d.ts +2 -1
- package/dist/components/marquee/ReviewCard.js +20 -9
- package/dist/components/meteors/Meteors.d.ts +32 -10
- package/dist/components/meteors/Meteors.js +53 -20
- package/dist/components/mosaic-glow/mosaic-glow-core.d.ts +98 -10
- package/dist/components/neon-border/NeonBorder.d.ts +24 -5
- package/dist/components/neon-border/NeonBorder.js +37 -7
- package/dist/components/pagination/Pagination.js +220 -82
- package/dist/components/rainbow-button/RainbowButton.js +13 -5
- package/dist/components/ripple-button/RippleButton.d.ts +20 -1
- package/dist/components/ripple-button/RippleButton.js +44 -21
- package/dist/components/shimmer-button/ShimmerButton.d.ts +5 -5
- package/dist/components/shimmer-button/ShimmerButton.js +17 -19
- package/dist/components/smooth-cursor/SmoothCursor.d.ts +13 -9
- package/dist/components/smooth-cursor/SmoothCursor.js +114 -163
- package/dist/components/smooth-cursor/smooth-cursor-core.d.ts +85 -0
- package/dist/components/smooth-cursor/smooth-cursor-core.js +327 -0
- package/dist/components/stepper/Step.js +32 -19
- package/dist/components/stepper/Stepper.js +18 -2
- package/dist/components/stepper/types.d.ts +13 -0
- package/dist/components/timeline/Timeline.d.ts +8 -1
- package/dist/components/timeline/Timeline.js +165 -76
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -1
- package/dist/internals/markdown.d.ts +5 -0
- package/dist/internals/motion/haptics.d.ts +0 -4
- package/dist/internals/motion/presence-core.d.ts +59 -0
- package/dist/internals/motion/presence-core.js +162 -0
- package/dist/internals/motion/presence.d.ts +2 -4
- package/dist/internals/motion/presence.js +1 -156
- package/dist/internals/motion/raf.d.ts +1 -1
- package/dist/internals/motion/stagger.d.ts +3 -4
- package/dist/internals/motion/tokens.js +4 -0
- package/dist/internals/motion/transitions.d.ts +17 -17
- package/dist/internals/motion/types.d.ts +6 -7
- package/dist/internals/relative-time.d.ts +1 -1
- package/dist/styles.css +3545 -204
- package/package.json +18 -2
- package/dist/components/fireworks-hdr/webgpu-types.d.ts +0 -159
|
@@ -1,35 +1,188 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
4
|
import { cn } from "../../utils.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import { useReducedMotion } from "../../internals/motion/media-query.js";
|
|
6
|
+
import { useInertAttribute } from "../../internals/dom/use-inert-attribute.js";
|
|
7
|
+
import { useLiveRef } from "../../internals/dom/use-live-ref.js";
|
|
8
|
+
/* empty css */
|
|
9
|
+
function showsBack(angle) {
|
|
10
|
+
return Math.abs(Math.round(angle / 180)) % 2 === 1;
|
|
11
|
+
}
|
|
12
|
+
function flipDirection(rect, point, axis, entering) {
|
|
13
|
+
const along = axis === "y" ? point.x - rect.left - rect.width / 2 : point.y - rect.top - rect.height / 2;
|
|
14
|
+
const towardEnd = entering ? along < 0 : along >= 0;
|
|
15
|
+
const sign = towardEnd ? 1 : -1;
|
|
16
|
+
return axis === "y" ? sign : -sign;
|
|
17
|
+
}
|
|
18
|
+
const INTERACTIVE = "a, button, input, select, textarea, label, [role='button'], [contenteditable='true']";
|
|
19
|
+
function FlipCard({
|
|
20
|
+
rotate = "y",
|
|
21
|
+
trigger = "hover",
|
|
22
|
+
flipped = false,
|
|
23
|
+
onFlip,
|
|
24
|
+
duration = 700,
|
|
25
|
+
glare = true,
|
|
26
|
+
label,
|
|
27
|
+
className = "",
|
|
28
|
+
children,
|
|
29
|
+
back
|
|
30
|
+
}) {
|
|
31
|
+
const reduced = useReducedMotion();
|
|
32
|
+
const [angle, setAngle] = useState(() => flipped ? 180 : 0);
|
|
33
|
+
const angleRef = useRef(angle);
|
|
34
|
+
const showingBack = showsBack(angle);
|
|
35
|
+
const rootRef = useRef(null);
|
|
36
|
+
const liftRef = useRef(null);
|
|
37
|
+
const shadowRef = useRef(null);
|
|
38
|
+
const frontInertRef = useInertAttribute(showingBack);
|
|
39
|
+
const backInertRef = useInertAttribute(!showingBack);
|
|
40
|
+
const live = useLiveRef({ onFlip, duration, reduced });
|
|
41
|
+
function setAngleTo(next) {
|
|
42
|
+
angleRef.current = next;
|
|
43
|
+
setAngle(next);
|
|
44
|
+
}
|
|
45
|
+
function lift() {
|
|
46
|
+
const { reduced: reduced2, duration: duration2 } = live.current;
|
|
47
|
+
if (reduced2) return;
|
|
48
|
+
const opts = { duration: duration2, easing: "cubic-bezier(0.33, 1, 0.68, 1)" };
|
|
49
|
+
liftRef.current?.animate?.(
|
|
50
|
+
[
|
|
51
|
+
{ transform: "translateZ(0) scale(1)" },
|
|
52
|
+
{ transform: "translateZ(40px) scale(1.05)", offset: 0.45 },
|
|
53
|
+
{ transform: "translateZ(0) scale(1)" }
|
|
54
|
+
],
|
|
55
|
+
opts
|
|
56
|
+
);
|
|
57
|
+
shadowRef.current?.animate?.(
|
|
58
|
+
[
|
|
59
|
+
{ opacity: 0.35, transform: "translateY(10px) scale(0.92)", filter: "blur(14px)" },
|
|
60
|
+
{
|
|
61
|
+
opacity: 0.18,
|
|
62
|
+
transform: "translateY(26px) scale(0.85)",
|
|
63
|
+
filter: "blur(26px)",
|
|
64
|
+
offset: 0.45
|
|
65
|
+
},
|
|
66
|
+
{ opacity: 0.35, transform: "translateY(10px) scale(0.92)", filter: "blur(14px)" }
|
|
67
|
+
],
|
|
68
|
+
opts
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
function turn(direction) {
|
|
72
|
+
setAngleTo(angleRef.current + 180 * direction);
|
|
73
|
+
const next = showsBack(angleRef.current);
|
|
74
|
+
live.current.onFlip?.(next);
|
|
75
|
+
lift();
|
|
76
|
+
}
|
|
77
|
+
const liveLift = useLiveRef(lift);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (flipped !== showsBack(angleRef.current)) {
|
|
80
|
+
setAngleTo(angleRef.current + 180);
|
|
81
|
+
live.current.onFlip?.(flipped);
|
|
82
|
+
liveLift.current();
|
|
83
|
+
}
|
|
84
|
+
}, [flipped, live, liveLift]);
|
|
85
|
+
function pointerCross(event, entering) {
|
|
86
|
+
const root = rootRef.current;
|
|
87
|
+
if (trigger !== "hover" || event.pointerType === "touch" || !root) return;
|
|
88
|
+
const wantBack = entering;
|
|
89
|
+
if (showsBack(angleRef.current) === wantBack) return;
|
|
90
|
+
const dir = flipDirection(
|
|
91
|
+
root.getBoundingClientRect(),
|
|
92
|
+
{ x: event.clientX, y: event.clientY },
|
|
93
|
+
rotate,
|
|
94
|
+
entering
|
|
95
|
+
);
|
|
96
|
+
turn(dir);
|
|
97
|
+
}
|
|
98
|
+
function handleFocus(event) {
|
|
99
|
+
if (trigger !== "hover" || showsBack(angleRef.current)) return;
|
|
100
|
+
if (event.target === rootRef.current) turn(1);
|
|
101
|
+
}
|
|
102
|
+
function handleBlur(event) {
|
|
103
|
+
if (event.target !== rootRef.current) return;
|
|
104
|
+
if (trigger !== "hover" || !showsBack(angleRef.current)) return;
|
|
105
|
+
const root = rootRef.current;
|
|
106
|
+
if (root && event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;
|
|
107
|
+
turn(1);
|
|
108
|
+
}
|
|
109
|
+
function isInnerControl(target) {
|
|
110
|
+
const root = rootRef.current;
|
|
111
|
+
if (!(target instanceof Element) || !root) return false;
|
|
112
|
+
const control = target.closest(INTERACTIVE);
|
|
113
|
+
return control !== null && control !== root && root.contains(control);
|
|
114
|
+
}
|
|
115
|
+
function handleClick(event) {
|
|
116
|
+
const touchOnHover = trigger === "hover" && event.nativeEvent.pointerType === "touch";
|
|
117
|
+
if (trigger !== "click" && !touchOnHover) return;
|
|
118
|
+
if (isInnerControl(event.target)) return;
|
|
119
|
+
turn(1);
|
|
120
|
+
}
|
|
121
|
+
function handleKeyDown(event) {
|
|
122
|
+
if (trigger !== "click" || event.target !== rootRef.current) return;
|
|
123
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
turn(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const style = {
|
|
129
|
+
"--fc-duration": `${duration}ms`,
|
|
130
|
+
"--fc-target": `${angle}deg`
|
|
131
|
+
};
|
|
132
|
+
return /* @__PURE__ */ jsxs(
|
|
11
133
|
"div",
|
|
12
134
|
{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
135
|
+
ref: rootRef,
|
|
136
|
+
className: cn("group ft-flip-card relative h-72 w-56 [perspective:1000px]", className),
|
|
137
|
+
"data-axis": rotate,
|
|
138
|
+
"data-flipped": showingBack ? "" : void 0,
|
|
139
|
+
"data-trigger": trigger,
|
|
140
|
+
style,
|
|
141
|
+
role: trigger === "click" ? "button" : "group",
|
|
142
|
+
tabIndex: 0,
|
|
143
|
+
"aria-pressed": trigger === "click" ? showingBack : void 0,
|
|
144
|
+
"aria-label": label,
|
|
145
|
+
"aria-roledescription": "flip card",
|
|
146
|
+
onPointerEnter: (e) => pointerCross(e, true),
|
|
147
|
+
onPointerLeave: (e) => pointerCross(e, false),
|
|
148
|
+
onFocus: handleFocus,
|
|
149
|
+
onBlur: handleBlur,
|
|
150
|
+
onClick: handleClick,
|
|
151
|
+
onKeyDown: handleKeyDown,
|
|
17
152
|
children: [
|
|
18
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
19
|
-
/* @__PURE__ */ jsx(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
153
|
+
/* @__PURE__ */ jsx("div", { ref: shadowRef, className: "ft-flip-card__shadow", "aria-hidden": "true" }),
|
|
154
|
+
/* @__PURE__ */ jsx("div", { ref: liftRef, className: "ft-flip-card__lift size-full [transform-style:preserve-3d]", children: /* @__PURE__ */ jsxs("div", { className: "ft-flip-card__inner relative size-full rounded-2xl [transform-style:preserve-3d]", children: [
|
|
155
|
+
/* @__PURE__ */ jsxs(
|
|
156
|
+
"div",
|
|
157
|
+
{
|
|
158
|
+
ref: frontInertRef,
|
|
159
|
+
className: "ft-flip-card__face ft-flip-card__front bg-card text-card-foreground absolute size-full overflow-hidden rounded-2xl border [backface-visibility:hidden]",
|
|
160
|
+
"aria-hidden": showingBack ? "true" : void 0,
|
|
161
|
+
children: [
|
|
162
|
+
children,
|
|
163
|
+
glare ? /* @__PURE__ */ jsx("span", { className: "ft-flip-card__light", "aria-hidden": "true" }) : null
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
),
|
|
167
|
+
/* @__PURE__ */ jsxs(
|
|
168
|
+
"div",
|
|
169
|
+
{
|
|
170
|
+
ref: backInertRef,
|
|
171
|
+
className: "ft-flip-card__face ft-flip-card__back bg-card text-card-foreground absolute size-full overflow-hidden rounded-2xl border p-4 [backface-visibility:hidden]",
|
|
172
|
+
"aria-hidden": showingBack ? void 0 : "true",
|
|
173
|
+
children: [
|
|
174
|
+
back,
|
|
175
|
+
glare ? /* @__PURE__ */ jsx("span", { className: "ft-flip-card__light", "aria-hidden": "true" }) : null
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
)
|
|
179
|
+
] }) })
|
|
29
180
|
]
|
|
30
181
|
}
|
|
31
|
-
)
|
|
182
|
+
);
|
|
32
183
|
}
|
|
33
184
|
export {
|
|
34
|
-
FlipCard
|
|
185
|
+
FlipCard,
|
|
186
|
+
flipDirection,
|
|
187
|
+
showsBack
|
|
35
188
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { FlipCard } from "./FlipCard.js";
|
|
2
|
-
export type { FlipCardProps } from "./FlipCard.js";
|
|
2
|
+
export type { FlipCardProps, FlipCardTrigger } from "./FlipCard.js";
|
|
@@ -3,7 +3,8 @@ import { jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useRef, useEffect } from "react";
|
|
4
4
|
import { useLiveRef } from "../../internals/dom/use-live-ref.js";
|
|
5
5
|
import { cn } from "../../utils.js";
|
|
6
|
-
import { hexToRgb, clickBoost,
|
|
6
|
+
import { hexToRgb, clickBoost, wrap, pointerPrototype, scaleRadiusForContainer, HSVtoRGB } from "./fluid-shared.js";
|
|
7
|
+
import { isDev } from "./dev.js";
|
|
7
8
|
import { startWebGpuFluid } from "./webgpu-engine.js";
|
|
8
9
|
function FluidCursor({
|
|
9
10
|
simResolution = 128,
|
|
@@ -110,7 +111,7 @@ function FluidCursor({
|
|
|
110
111
|
function registerInstance(cleanup) {
|
|
111
112
|
if (!allowMultiple) {
|
|
112
113
|
if (activeInstanceRef.current) {
|
|
113
|
-
if (
|
|
114
|
+
if (isDev()) {
|
|
114
115
|
console.warn(
|
|
115
116
|
"[FluidCursor] Destroying previous instance. Only one instance is allowed by default. Use `allowMultiple={true}` to opt out of singleton behavior."
|
|
116
117
|
);
|
|
@@ -210,7 +211,7 @@ function FluidCursor({
|
|
|
210
211
|
activeCleanup = registerInstance(handle.cleanup);
|
|
211
212
|
notifyReady(handle);
|
|
212
213
|
}).catch((error) => {
|
|
213
|
-
if (
|
|
214
|
+
if (isDev()) {
|
|
214
215
|
console.warn("[FluidCursor] WebGPU setup failed, falling back to WebGL:", error);
|
|
215
216
|
}
|
|
216
217
|
if (!disposed) activeCleanup = startWebGl() ?? null;
|
|
@@ -262,7 +263,7 @@ function FluidCursor({
|
|
|
262
263
|
}
|
|
263
264
|
} catch {
|
|
264
265
|
}
|
|
265
|
-
if (
|
|
266
|
+
if (isDev()) {
|
|
266
267
|
console.info(
|
|
267
268
|
renderLevel === "webgl-p3" ? "[FluidCursor] HDR level: webgl-p3 (wide gamut fallback)" : "[FluidCursor] HDR level: webgl-sdr (display-p3 drawing buffer rejected)"
|
|
268
269
|
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isDev(): boolean;
|
|
@@ -88,5 +88,3 @@ export interface FluidCursorHandle {
|
|
|
88
88
|
burst(x: number, y: number, dx: number, dy: number, color: ColorRGB): void;
|
|
89
89
|
readonly renderLevel: FluidRenderLevel;
|
|
90
90
|
}
|
|
91
|
-
/** True outside a production build: gates the engines' console diagnostics. */
|
|
92
|
-
export declare const DEV: boolean;
|
|
@@ -110,10 +110,8 @@ function correctDeltaY(delta, width, height) {
|
|
|
110
110
|
if (aspectRatio > 1) return delta / aspectRatio;
|
|
111
111
|
return delta;
|
|
112
112
|
}
|
|
113
|
-
const DEV = typeof process !== "undefined" && process.env.NODE_ENV !== "production";
|
|
114
113
|
export {
|
|
115
114
|
CLICK_PEAK,
|
|
116
|
-
DEV,
|
|
117
115
|
HSVtoRGB,
|
|
118
116
|
clickBoost,
|
|
119
117
|
correctDeltaX,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { getSimResolution, correctRadius, scaleRadiusForContainer,
|
|
2
|
+
import { getSimResolution, correctRadius, scaleRadiusForContainer, pointerPrototype, correctDeltaX, correctDeltaY } from "./fluid-shared.js";
|
|
3
3
|
const WGSL = (
|
|
4
4
|
/* wgsl */
|
|
5
5
|
`
|
|
@@ -527,11 +527,8 @@ async function createWebGpuFluid(canvas, opts) {
|
|
|
527
527
|
}
|
|
528
528
|
probe.configure(configuration);
|
|
529
529
|
probe.unconfigure();
|
|
530
|
-
} catch
|
|
530
|
+
} catch {
|
|
531
531
|
device.destroy();
|
|
532
|
-
if (DEV) {
|
|
533
|
-
console.warn("[FluidCursor] WebGPU canvas configuration rejected:", error);
|
|
534
|
-
}
|
|
535
532
|
return null;
|
|
536
533
|
}
|
|
537
534
|
const maybeContext = canvas.getContext("webgpu");
|
|
@@ -547,24 +544,11 @@ async function createWebGpuFluid(canvas, opts) {
|
|
|
547
544
|
extendedToneMapping = applied?.toneMapping?.mode === "extended";
|
|
548
545
|
} catch {
|
|
549
546
|
}
|
|
550
|
-
if (DEV) {
|
|
551
|
-
console.info(
|
|
552
|
-
extendedToneMapping ? "[FluidCursor] extended tone mapping active (webgpu-hdr if the display reports high dynamic range)" : "[FluidCursor] HDR level: webgpu-sdr (float16 + P3, tone mapping clamped by browser)"
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
547
|
let destroyed = false;
|
|
556
548
|
let lost = false;
|
|
557
|
-
device.lost.then((
|
|
549
|
+
device.lost.then(() => {
|
|
558
550
|
lost = true;
|
|
559
|
-
if (!destroyed && DEV) {
|
|
560
|
-
console.warn(`[FluidCursor] WebGPU device lost (${info.reason}): ${info.message}`);
|
|
561
|
-
}
|
|
562
551
|
});
|
|
563
|
-
if (DEV) {
|
|
564
|
-
device.addEventListener("uncapturederror", (event) => {
|
|
565
|
-
console.error("[FluidCursor] WebGPU uncaptured error:", event.error.message);
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
552
|
const module = device.createShaderModule({ code: WGSL });
|
|
569
553
|
const bindGroupLayout = device.createBindGroupLayout({
|
|
570
554
|
entries: [
|
|
@@ -643,10 +627,7 @@ async function createWebGpuFluid(canvas, opts) {
|
|
|
643
627
|
device.destroy();
|
|
644
628
|
}
|
|
645
629
|
};
|
|
646
|
-
} catch
|
|
647
|
-
if (DEV) {
|
|
648
|
-
console.warn("[FluidCursor] WebGPU init failed, falling back to WebGL:", error);
|
|
649
|
-
}
|
|
630
|
+
} catch {
|
|
650
631
|
return null;
|
|
651
632
|
}
|
|
652
633
|
}
|
|
@@ -805,6 +786,7 @@ async function startWebGpuFluid(canvas, opts) {
|
|
|
805
786
|
const pointer = pointers[0];
|
|
806
787
|
for (let i = 0; i < touches.length; i++) {
|
|
807
788
|
const touch = touches[i];
|
|
789
|
+
if (!touch) continue;
|
|
808
790
|
const { x: posX, y: posY } = getCanvasPos(touch.clientX, touch.clientY);
|
|
809
791
|
updatePointerDownData(pointer, touch.identifier, posX, posY);
|
|
810
792
|
}
|
|
@@ -815,6 +797,7 @@ async function startWebGpuFluid(canvas, opts) {
|
|
|
815
797
|
const pointer = pointers[0];
|
|
816
798
|
for (let i = 0; i < touches.length; i++) {
|
|
817
799
|
const touch = touches[i];
|
|
800
|
+
if (!touch) continue;
|
|
818
801
|
const { x: posX, y: posY } = getCanvasPos(touch.clientX, touch.clientY);
|
|
819
802
|
updatePointerDownData(pointer, touch.identifier, posX, posY);
|
|
820
803
|
}
|
|
@@ -824,6 +807,7 @@ async function startWebGpuFluid(canvas, opts) {
|
|
|
824
807
|
const pointer = pointers[0];
|
|
825
808
|
for (let i = 0; i < touches.length; i++) {
|
|
826
809
|
const touch = touches[i];
|
|
810
|
+
if (!touch) continue;
|
|
827
811
|
const { x: posX, y: posY } = getCanvasPos(touch.clientX, touch.clientY);
|
|
828
812
|
updatePointerMoveData(pointer, posX, posY, pointer.color);
|
|
829
813
|
}
|
|
@@ -847,7 +831,9 @@ async function startWebGpuFluid(canvas, opts) {
|
|
|
847
831
|
let observer = null;
|
|
848
832
|
if (opts.pauseWhenHidden) {
|
|
849
833
|
observer = new IntersectionObserver(
|
|
850
|
-
(
|
|
834
|
+
(entries) => {
|
|
835
|
+
const entry = entries[0];
|
|
836
|
+
if (!entry) return;
|
|
851
837
|
const wasVisible = isVisible;
|
|
852
838
|
isVisible = entry.isIntersecting;
|
|
853
839
|
if (isVisible && !wasVisible && !stopped) {
|
|
@@ -1,8 +1,48 @@
|
|
|
1
|
+
export type GlowBorderPreset = "chromatic" | "silver" | "gold";
|
|
1
2
|
export interface GlowBorderProps {
|
|
3
|
+
/** Additional classes on the overlay */
|
|
2
4
|
className?: string;
|
|
5
|
+
/** Border radius in pixels (the overlay also inherits its parent's radius) */
|
|
3
6
|
borderRadius?: number;
|
|
7
|
+
/**
|
|
8
|
+
* Custom metal tints: one colour or several. Overrides `preset`; neutral
|
|
9
|
+
* silver and shadow tones are woven in so it still reads as metal.
|
|
10
|
+
*/
|
|
4
11
|
color?: string | string[];
|
|
12
|
+
/** Width of the metal ring in pixels */
|
|
5
13
|
borderWidth?: number;
|
|
14
|
+
/** Length of one flow cycle of the metal, in seconds. The glint laps in 0.4× that. */
|
|
6
15
|
duration?: number;
|
|
16
|
+
/** Metal palette: iridescent chrome, cool steel or warm gold */
|
|
17
|
+
preset?: GlowBorderPreset;
|
|
18
|
+
/** Intensity of the glint and its glow, 0–1 */
|
|
19
|
+
strength?: number;
|
|
7
20
|
}
|
|
8
|
-
|
|
21
|
+
interface Palette {
|
|
22
|
+
/** Bright tints, the colours the reflections flash. */
|
|
23
|
+
tints: string[];
|
|
24
|
+
/** The metal's body between reflections. */
|
|
25
|
+
body: string;
|
|
26
|
+
/** The shadow a reflection falls off into. */
|
|
27
|
+
shadow: string;
|
|
28
|
+
}
|
|
29
|
+
/** Palettes per preset, one per theme: light pages need deeper tones to read. */
|
|
30
|
+
export declare const GLOW_BORDER_PRESETS: Record<GlowBorderPreset, {
|
|
31
|
+
dark: Palette;
|
|
32
|
+
light: Palette;
|
|
33
|
+
}>;
|
|
34
|
+
/** A custom `color` turned into a metal palette: the tints, over neutral body and shadow. */
|
|
35
|
+
export declare function customPalette(color: string | string[], theme: "dark" | "light"): Palette;
|
|
36
|
+
/**
|
|
37
|
+
* One metal field as a conic gradient: reflections (tints) alternating
|
|
38
|
+
* with body and shadow, spread around the turn so no two flashes touch.
|
|
39
|
+
* Two of these, centred apart and turning against each other, are what
|
|
40
|
+
* make the surface look liquid rather than spun.
|
|
41
|
+
*/
|
|
42
|
+
export declare function metalField(p: Palette, angleVar: string, at: string, offset?: number): string;
|
|
43
|
+
/** Both metal fields, stacked for `background` (blended in CSS). */
|
|
44
|
+
export declare function metalBackground(p: Palette): string;
|
|
45
|
+
/** The glint's colour trail: a short arc that brightens to white. */
|
|
46
|
+
export declare function glintArc(p: Palette): string;
|
|
47
|
+
export declare function GlowBorder({ className, borderRadius, color, borderWidth, duration, preset, strength, }: GlowBorderProps): import("react").JSX.Element;
|
|
48
|
+
export {};
|
|
@@ -1,39 +1,119 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
3
|
import { cn } from "../../utils.js";
|
|
4
4
|
/* empty css */
|
|
5
|
+
const GLOW_BORDER_PRESETS = {
|
|
6
|
+
chromatic: {
|
|
7
|
+
dark: {
|
|
8
|
+
tints: ["#ffffff", "#f5b8d0", "#8fe3d9", "#b9a8ff", "#f3d58a"],
|
|
9
|
+
body: "#8a9099",
|
|
10
|
+
shadow: "#2a2d33"
|
|
11
|
+
},
|
|
12
|
+
light: {
|
|
13
|
+
tints: ["#5b636d", "#d9669a", "#2fa89a", "#7a62e6", "#c99a2e"],
|
|
14
|
+
body: "#9aa3ad",
|
|
15
|
+
shadow: "#e3e6ea"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
silver: {
|
|
19
|
+
dark: {
|
|
20
|
+
tints: ["#ffffff", "#dfe4ea", "#b8c2cf", "#f2f5f8"],
|
|
21
|
+
body: "#7d848f",
|
|
22
|
+
shadow: "#2b2f35"
|
|
23
|
+
},
|
|
24
|
+
light: {
|
|
25
|
+
tints: ["#4b525c", "#7d8793", "#5f6975", "#2f353d"],
|
|
26
|
+
body: "#aab2bc",
|
|
27
|
+
shadow: "#e6e9ed"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
gold: {
|
|
31
|
+
dark: {
|
|
32
|
+
tints: ["#fff4cf", "#f7d98b", "#ffe7a3", "#e9b95c"],
|
|
33
|
+
body: "#c8973f",
|
|
34
|
+
shadow: "#4a3616"
|
|
35
|
+
},
|
|
36
|
+
light: {
|
|
37
|
+
tints: ["#8a6424", "#b8862f", "#6e4d17", "#c99a2e"],
|
|
38
|
+
body: "#d9b56a",
|
|
39
|
+
shadow: "#f3e8cf"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function customPalette(color, theme) {
|
|
44
|
+
const list = (Array.isArray(color) ? color : [color]).filter(Boolean);
|
|
45
|
+
const tints = list.length ? list : ["#ffffff"];
|
|
46
|
+
return theme === "dark" ? { tints: ["#ffffff", ...tints], body: "#7d848f", shadow: "#2a2d33" } : { tints, body: "#9aa3ad", shadow: "#e3e6ea" };
|
|
47
|
+
}
|
|
48
|
+
function metalField(p, angleVar, at, offset = 0) {
|
|
49
|
+
const stops = [];
|
|
50
|
+
const n = p.tints.length;
|
|
51
|
+
const step = 100 / n;
|
|
52
|
+
for (let i = 0; i < n; i++) {
|
|
53
|
+
const base = i * step;
|
|
54
|
+
const tint = p.tints[(i + offset) % n];
|
|
55
|
+
stops.push(`${p.shadow} ${base.toFixed(1)}%`);
|
|
56
|
+
stops.push(`${p.shadow} ${(base + step * 0.22).toFixed(1)}%`);
|
|
57
|
+
stops.push(`${p.body} ${(base + step * 0.4).toFixed(1)}%`);
|
|
58
|
+
stops.push(`${tint} ${(base + step * 0.5).toFixed(1)}%`);
|
|
59
|
+
stops.push(`${p.body} ${(base + step * 0.6).toFixed(1)}%`);
|
|
60
|
+
stops.push(`${p.shadow} ${(base + step * 0.8).toFixed(1)}%`);
|
|
61
|
+
}
|
|
62
|
+
stops.push(`${p.shadow} 100%`);
|
|
63
|
+
return `conic-gradient(from var(${angleVar}) at ${at}, ${stops.join(", ")})`;
|
|
64
|
+
}
|
|
65
|
+
function metalBackground(p) {
|
|
66
|
+
return `${metalField(p, "--gb-a1", "30% 40%")}, ${metalField(p, "--gb-a2", "72% 65%", 2)}`;
|
|
67
|
+
}
|
|
68
|
+
function glintArc(p) {
|
|
69
|
+
const lead = p.tints[1] ?? p.tints[0];
|
|
70
|
+
const tail = p.tints[2] ?? p.tints[p.tints.length - 1];
|
|
71
|
+
return `conic-gradient(from var(--gb-a3), transparent 0%, transparent 72%, ${tail} 84%, #ffffff 90%, ${lead} 94%, transparent 99%)`;
|
|
72
|
+
}
|
|
5
73
|
function GlowBorder({
|
|
6
74
|
className = "",
|
|
7
75
|
borderRadius = 10,
|
|
8
|
-
color
|
|
9
|
-
borderWidth =
|
|
10
|
-
duration = 10
|
|
76
|
+
color,
|
|
77
|
+
borderWidth = 1.5,
|
|
78
|
+
duration = 10,
|
|
79
|
+
preset = "chromatic",
|
|
80
|
+
strength = 0.8
|
|
11
81
|
}) {
|
|
12
|
-
const
|
|
82
|
+
const palettes = color !== void 0 ? { dark: customPalette(color, "dark"), light: customPalette(color, "light") } : GLOW_BORDER_PRESETS[preset] ?? GLOW_BORDER_PRESETS.chromatic;
|
|
83
|
+
const strengthC = Math.min(1, Math.max(0, Number.isFinite(strength) ? strength : 0.8));
|
|
13
84
|
const styles = {
|
|
14
85
|
"--glow-border-radius": `${borderRadius}px`,
|
|
15
86
|
"--glow-border-width": `${borderWidth}px`,
|
|
16
87
|
"--glow-duration": `${duration}s`,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
maskComposite: "exclude",
|
|
23
|
-
padding: "var(--glow-border-width)",
|
|
88
|
+
"--glow-strength": `${strengthC}`,
|
|
89
|
+
"--gb-metal-dark": metalBackground(palettes.dark),
|
|
90
|
+
"--gb-metal-light": metalBackground(palettes.light),
|
|
91
|
+
"--gb-glint-dark": glintArc(palettes.dark),
|
|
92
|
+
"--gb-glint-light": glintArc(palettes.light),
|
|
24
93
|
borderRadius: "var(--glow-border-radius)"
|
|
25
94
|
};
|
|
26
|
-
return /* @__PURE__ */
|
|
95
|
+
return /* @__PURE__ */ jsxs(
|
|
27
96
|
"div",
|
|
28
97
|
{
|
|
29
98
|
className: cn(
|
|
30
|
-
"animate-glow pointer-events-none absolute inset-0 size-full rounded-[inherit]
|
|
99
|
+
"glow-border animate-glow pointer-events-none absolute inset-0 size-full rounded-[inherit]",
|
|
31
100
|
className
|
|
32
101
|
),
|
|
33
|
-
style: styles
|
|
102
|
+
style: styles,
|
|
103
|
+
"aria-hidden": "true",
|
|
104
|
+
children: [
|
|
105
|
+
/* @__PURE__ */ jsx("span", { className: "glow-border__halo", children: /* @__PURE__ */ jsx("span", {}) }),
|
|
106
|
+
/* @__PURE__ */ jsx("span", { className: "glow-border__metal" }),
|
|
107
|
+
/* @__PURE__ */ jsx("span", { className: "glow-border__glint" })
|
|
108
|
+
]
|
|
34
109
|
}
|
|
35
110
|
);
|
|
36
111
|
}
|
|
37
112
|
export {
|
|
38
|
-
|
|
113
|
+
GLOW_BORDER_PRESETS,
|
|
114
|
+
GlowBorder,
|
|
115
|
+
customPalette,
|
|
116
|
+
glintArc,
|
|
117
|
+
metalBackground,
|
|
118
|
+
metalField
|
|
39
119
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { GlowBorder } from "./GlowBorder.js";
|
|
2
|
-
export type { GlowBorderProps } from "./GlowBorder.js";
|
|
1
|
+
export { GlowBorder, GLOW_BORDER_PRESETS } from "./GlowBorder.js";
|
|
2
|
+
export type { GlowBorderPreset, GlowBorderProps } from "./GlowBorder.js";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import type { ButtonHTMLAttributes, ReactNode } from "react";
|
|
2
2
|
type BaseProps = {
|
|
3
|
-
/**
|
|
3
|
+
/** Colours of the beam, spread along its arc */
|
|
4
4
|
colors?: string[];
|
|
5
|
-
/**
|
|
5
|
+
/** Time for the beam to travel once around the button, in milliseconds */
|
|
6
6
|
duration?: number;
|
|
7
|
-
/**
|
|
7
|
+
/** Width of the lit border in pixels */
|
|
8
8
|
borderWidth?: number;
|
|
9
9
|
/** Border radius in pixels */
|
|
10
10
|
borderRadius?: number;
|
|
11
|
-
/**
|
|
11
|
+
/** Softness of the glow the beam casts inside the button, in pixels */
|
|
12
12
|
blur?: number;
|
|
13
|
-
/** Background color of the button
|
|
13
|
+
/** Background color of the button face */
|
|
14
14
|
bgColor?: string;
|
|
15
15
|
/** Custom CSS class */
|
|
16
16
|
className?: string;
|
|
@@ -24,5 +24,10 @@ type BaseProps = {
|
|
|
24
24
|
};
|
|
25
25
|
export interface GradientButtonProps extends BaseProps, Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof BaseProps> {
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* The beam as a conic gradient: a main arc carrying every colour over ~40%
|
|
29
|
+
* of the turn, and a fainter echo opposite it. Turned by `--gb-angle`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function beamGradient(colors: string[]): string;
|
|
27
32
|
export declare const GradientButton: import("react").ForwardRefExoticComponent<GradientButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
28
33
|
export {};
|