fancy-ui-react 0.3.0 → 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 +52 -19
- 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/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/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.js +2 -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 +3470 -203
- package/package.json +18 -2
- package/dist/components/fireworks-hdr/webgpu-types.d.ts +0 -159
|
@@ -1,133 +1,202 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useCallback,
|
|
3
|
+
import { useState, useRef, useCallback, useEffect } from "react";
|
|
4
4
|
import { cn } from "../../utils.js";
|
|
5
|
-
import {
|
|
5
|
+
import { useLiveRef } from "../../internals/dom/use-live-ref.js";
|
|
6
|
+
import { linear } from "../../internals/motion/easing.js";
|
|
7
|
+
import { useReducedMotion } from "../../internals/motion/media-query.js";
|
|
6
8
|
import { usePresence } from "../../internals/motion/presence.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return
|
|
9
|
+
/* empty css */
|
|
10
|
+
function tooltipId(itemId) {
|
|
11
|
+
return `animated-tooltip-${itemId}`;
|
|
12
|
+
}
|
|
13
|
+
function transformFor(mouseX, size, reduced) {
|
|
14
|
+
const lean = reduced ? 0 : Math.max(-1, Math.min(1, mouseX / (size / 2)));
|
|
15
|
+
const rotation = lean * 7;
|
|
16
|
+
const translation = lean * 14;
|
|
17
|
+
return `translateX(calc(-50% + ${translation}px)) rotate(${rotation}deg)`;
|
|
10
18
|
}
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
function partOffset(i, activeIndex, reduced) {
|
|
20
|
+
if (activeIndex < 0 || reduced) return 0;
|
|
21
|
+
const d = i - activeIndex;
|
|
22
|
+
const distance = Math.abs(d);
|
|
23
|
+
if (distance === 1) return Math.sign(d) * 6;
|
|
24
|
+
if (distance === 2) return Math.sign(d) * 2;
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
function sink(_node, params) {
|
|
28
|
+
if (!params || params.entering) {
|
|
29
|
+
return { delay: 0, duration: 0, easing: linear, css: () => "" };
|
|
30
|
+
}
|
|
31
|
+
const still = params.still;
|
|
17
32
|
return {
|
|
18
33
|
delay: 0,
|
|
19
|
-
duration:
|
|
20
|
-
easing:
|
|
21
|
-
css: (t
|
|
22
|
-
transform: ${transform} scale(${1 - sd * u});
|
|
23
|
-
opacity: ${targetOpacity - od * u}
|
|
24
|
-
`
|
|
34
|
+
duration: still ? 120 : 160,
|
|
35
|
+
easing: (t) => t * t,
|
|
36
|
+
css: (t) => still ? `opacity: ${t};` : `opacity: ${t}; transform: translateY(${(1 - t) * 4}px) scale(${0.97 + 0.03 * t});`
|
|
25
37
|
};
|
|
26
38
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
spec.current = null;
|
|
56
|
-
}, []);
|
|
57
|
-
const tooltipRef = useComposedRefs(
|
|
58
|
-
attach,
|
|
59
|
-
presence.register((node) => spec.current ??= tooltipScale(node))
|
|
39
|
+
function AvatarItem({
|
|
40
|
+
item,
|
|
41
|
+
active,
|
|
42
|
+
shift,
|
|
43
|
+
reduced,
|
|
44
|
+
onEnter,
|
|
45
|
+
onMove,
|
|
46
|
+
onLeave,
|
|
47
|
+
onFocusIn,
|
|
48
|
+
onTip
|
|
49
|
+
}) {
|
|
50
|
+
const presence = usePresence(active);
|
|
51
|
+
const cardRef = presence.register(sink, (entering) => ({ entering, still: reduced }));
|
|
52
|
+
const tipNode = useRef(null);
|
|
53
|
+
const itemId = item.id;
|
|
54
|
+
const attachTip = useCallback(
|
|
55
|
+
(node) => {
|
|
56
|
+
if (node) {
|
|
57
|
+
tipNode.current = node;
|
|
58
|
+
onTip(itemId, node, null);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
onTip(itemId, null, tipNode.current);
|
|
62
|
+
tipNode.current = null;
|
|
63
|
+
},
|
|
64
|
+
[itemId, onTip]
|
|
60
65
|
);
|
|
61
|
-
function draw(next) {
|
|
62
|
-
pose.current = next;
|
|
63
|
-
if (tooltip.current) tooltip.current.style.transform = transformFor(next);
|
|
64
|
-
}
|
|
65
|
-
function handleMouseEnter(event) {
|
|
66
|
-
draw(poseAt(event));
|
|
67
|
-
onHoverStart(item.id);
|
|
68
|
-
}
|
|
69
|
-
function handleMouseMove(event) {
|
|
70
|
-
if (!hovered) return;
|
|
71
|
-
draw(poseAt(event));
|
|
72
|
-
}
|
|
73
|
-
function handleFocus() {
|
|
74
|
-
draw({ rotation: 0, translation: 0 });
|
|
75
|
-
onHoverStart(item.id);
|
|
76
|
-
}
|
|
77
66
|
return /* @__PURE__ */ jsxs(
|
|
78
67
|
"div",
|
|
79
68
|
{
|
|
80
|
-
className: "group relative -
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
69
|
+
className: cn("at-item group relative", active && "at-active", shift !== 0 && "at-parted"),
|
|
70
|
+
style: { "--_at-shift": `${shift}px` },
|
|
71
|
+
"data-active": active ? "" : void 0,
|
|
72
|
+
"data-part": shift < 0 ? "before" : shift > 0 ? "after" : void 0,
|
|
73
|
+
onMouseEnter: (event) => onEnter(item.id, event),
|
|
74
|
+
onMouseLeave: onLeave,
|
|
75
|
+
onMouseMove: onMove,
|
|
76
|
+
onFocus: () => onFocusIn(item.id),
|
|
77
|
+
onBlur: onLeave,
|
|
86
78
|
tabIndex: 0,
|
|
87
|
-
"aria-describedby":
|
|
79
|
+
"aria-describedby": active ? tooltipId(item.id) : void 0,
|
|
88
80
|
children: [
|
|
89
|
-
presence.mounted && /* @__PURE__ */
|
|
81
|
+
presence.mounted && /* @__PURE__ */ jsx(
|
|
90
82
|
"div",
|
|
91
83
|
{
|
|
92
|
-
ref:
|
|
84
|
+
ref: attachTip,
|
|
93
85
|
id: tooltipId(item.id),
|
|
94
86
|
role: "tooltip",
|
|
95
|
-
className: "pointer-events-none absolute
|
|
96
|
-
children: [
|
|
97
|
-
/* @__PURE__ */ jsx("
|
|
98
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
99
|
-
/* @__PURE__ */ jsx("
|
|
100
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
101
|
-
]
|
|
87
|
+
className: "at-tip pointer-events-none absolute left-1/2 z-50",
|
|
88
|
+
children: /* @__PURE__ */ jsxs("div", { ref: cardRef, className: "at-card", children: [
|
|
89
|
+
/* @__PURE__ */ jsx("span", { className: "at-pointer", "aria-hidden": "true" }),
|
|
90
|
+
/* @__PURE__ */ jsx("div", { className: "at-name", children: item.name }),
|
|
91
|
+
/* @__PURE__ */ jsx("span", { className: "at-rule", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "at-glint" }) }),
|
|
92
|
+
/* @__PURE__ */ jsx("div", { className: "at-role", children: item.designation })
|
|
93
|
+
] })
|
|
102
94
|
}
|
|
103
95
|
),
|
|
104
|
-
/* @__PURE__ */
|
|
105
|
-
"
|
|
106
|
-
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
96
|
+
/* @__PURE__ */ jsxs("div", { className: cn("at-avatar", active && !reduced && "at-lifted"), children: [
|
|
97
|
+
/* @__PURE__ */ jsx("span", { className: "at-glow", "aria-hidden": "true" }),
|
|
98
|
+
/* @__PURE__ */ jsx("span", { className: "at-disc", "aria-hidden": "true" }),
|
|
99
|
+
/* @__PURE__ */ jsx("span", { className: "at-ring", "aria-hidden": "true" }),
|
|
100
|
+
/* @__PURE__ */ jsx(
|
|
101
|
+
"img",
|
|
102
|
+
{
|
|
103
|
+
src: item.image,
|
|
104
|
+
alt: item.name,
|
|
105
|
+
className: "at-img relative !m-0 rounded-full object-cover object-top !p-0"
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
] })
|
|
112
109
|
]
|
|
113
110
|
}
|
|
114
111
|
);
|
|
115
112
|
}
|
|
116
|
-
function AnimatedTooltip({ items, className }) {
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
113
|
+
function AnimatedTooltip({ items, className, accent, size = 56 }) {
|
|
114
|
+
const reduced = useReducedMotion();
|
|
115
|
+
const [hoveredId, setHoveredId] = useState(null);
|
|
116
|
+
const mouseX = useRef(0);
|
|
117
|
+
const hovered = useRef(null);
|
|
118
|
+
const tips = useRef(/* @__PURE__ */ new Map());
|
|
119
|
+
const reducedRef = useLiveRef(reduced);
|
|
120
|
+
const sizeRef = useLiveRef(size);
|
|
121
|
+
const draw = useCallback(
|
|
122
|
+
(itemId) => {
|
|
123
|
+
const node = tips.current.get(itemId);
|
|
124
|
+
if (node)
|
|
125
|
+
node.style.transform = transformFor(mouseX.current, sizeRef.current, reducedRef.current);
|
|
126
|
+
},
|
|
127
|
+
[reducedRef, sizeRef]
|
|
128
|
+
);
|
|
129
|
+
const onTip = useCallback(
|
|
130
|
+
(itemId, node, previous) => {
|
|
131
|
+
if (node) {
|
|
132
|
+
tips.current.set(itemId, node);
|
|
133
|
+
draw(itemId);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (tips.current.get(itemId) === previous) tips.current.delete(itemId);
|
|
137
|
+
},
|
|
138
|
+
[draw]
|
|
139
|
+
);
|
|
140
|
+
const activate = useCallback(
|
|
141
|
+
(itemId) => {
|
|
142
|
+
hovered.current = itemId;
|
|
143
|
+
setHoveredId(itemId);
|
|
144
|
+
draw(itemId);
|
|
145
|
+
},
|
|
146
|
+
[draw]
|
|
147
|
+
);
|
|
148
|
+
const handleMouseEnter = useCallback(
|
|
149
|
+
(itemId, event) => {
|
|
150
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
151
|
+
mouseX.current = event.clientX - rect.left - rect.width / 2;
|
|
152
|
+
activate(itemId);
|
|
153
|
+
},
|
|
154
|
+
[activate]
|
|
155
|
+
);
|
|
156
|
+
const handleMouseMove = useCallback(
|
|
157
|
+
(event) => {
|
|
158
|
+
if (hovered.current === null) return;
|
|
159
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
160
|
+
mouseX.current = event.clientX - rect.left - rect.width / 2;
|
|
161
|
+
draw(hovered.current);
|
|
162
|
+
},
|
|
163
|
+
[draw]
|
|
164
|
+
);
|
|
165
|
+
const handleLeave = useCallback(() => {
|
|
166
|
+
hovered.current = null;
|
|
167
|
+
mouseX.current = 0;
|
|
168
|
+
setHoveredId(null);
|
|
123
169
|
}, []);
|
|
124
|
-
|
|
170
|
+
const handleFocusIn = useCallback(
|
|
171
|
+
(itemId) => {
|
|
172
|
+
mouseX.current = 0;
|
|
173
|
+
activate(itemId);
|
|
174
|
+
},
|
|
175
|
+
[activate]
|
|
176
|
+
);
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (hovered.current !== null) draw(hovered.current);
|
|
179
|
+
}, [draw, reduced, size]);
|
|
180
|
+
const activeIndex = hoveredId === null ? -1 : items.findIndex((item) => item.id === hoveredId);
|
|
181
|
+
const rootStyle = {
|
|
182
|
+
"--_at-size": `${size}px`,
|
|
183
|
+
...accent ? {
|
|
184
|
+
"--at-accent": accent,
|
|
185
|
+
"--at-accent-2": `color-mix(in oklab, ${accent} 62%, white)`
|
|
186
|
+
} : {}
|
|
187
|
+
};
|
|
188
|
+
return /* @__PURE__ */ jsx("div", { className: cn("at-root flex flex-row items-center", className), style: rootStyle, children: items.map((item, i) => /* @__PURE__ */ jsx(
|
|
125
189
|
AvatarItem,
|
|
126
190
|
{
|
|
127
191
|
item,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
192
|
+
active: hoveredId === item.id,
|
|
193
|
+
shift: partOffset(i, activeIndex, reduced),
|
|
194
|
+
reduced,
|
|
195
|
+
onEnter: handleMouseEnter,
|
|
196
|
+
onMove: handleMouseMove,
|
|
197
|
+
onLeave: handleLeave,
|
|
198
|
+
onFocusIn: handleFocusIn,
|
|
199
|
+
onTip
|
|
131
200
|
},
|
|
132
201
|
item.id
|
|
133
202
|
)) });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Internal: the nested double frame both BentoGridItem and BentoGridCard are
|
|
4
|
+
* drawn in. Not exported from index.ts — the two public tiles own their
|
|
5
|
+
* content, this owns the chrome (outer hairline frame, inner lit panel, the
|
|
6
|
+
* accent glow that rises from the bottom on hover, and the bottom-edge light
|
|
7
|
+
* line).
|
|
8
|
+
*/
|
|
9
|
+
export interface BentoFrameProps extends HTMLAttributes<HTMLDivElement> {
|
|
10
|
+
/** Classes for the outer frame (the grid item itself). */
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Classes for the inner panel. */
|
|
13
|
+
panelClass?: string;
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export declare function BentoFrame({ className, panelClass, children, ...rest }: BentoFrameProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "../../utils.js";
|
|
4
|
+
/* empty css */
|
|
5
|
+
function BentoFrame({
|
|
6
|
+
className = "",
|
|
7
|
+
panelClass = "",
|
|
8
|
+
children,
|
|
9
|
+
...rest
|
|
10
|
+
}) {
|
|
11
|
+
return /* @__PURE__ */ jsx("div", { className: cn("bento-tile relative flex rounded-2xl p-1.5", className), ...rest, children: /* @__PURE__ */ jsxs(
|
|
12
|
+
"div",
|
|
13
|
+
{
|
|
14
|
+
className: cn(
|
|
15
|
+
"bento-panel relative isolate flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden rounded-[10px]",
|
|
16
|
+
panelClass
|
|
17
|
+
),
|
|
18
|
+
children: [
|
|
19
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "bento-glow" }),
|
|
20
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "bento-edge" }),
|
|
21
|
+
children
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
) });
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
BentoFrame
|
|
28
|
+
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
2
|
export interface BentoGridProps extends HTMLAttributes<HTMLDivElement> {
|
|
3
3
|
className?: string;
|
|
4
|
+
/** Tiles fade up in a stagger the first time the grid scrolls into view. */
|
|
5
|
+
reveal?: boolean;
|
|
6
|
+
/** Colour of the hover glow, the lit bottom edge, the icon ring and the CTA arrow. Any CSS colour; writes `--bento-accent`. */
|
|
7
|
+
accent?: string;
|
|
4
8
|
children?: ReactNode;
|
|
5
9
|
}
|
|
6
|
-
export declare function BentoGrid({ className, children, ...rest }: BentoGridProps): import("react").JSX.Element;
|
|
10
|
+
export declare function BentoGrid({ className, reveal, accent, children, style, onFocus, ...rest }: BentoGridProps): import("react").JSX.Element;
|
|
@@ -1,14 +1,83 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
3
4
|
import { cn } from "../../utils.js";
|
|
4
|
-
|
|
5
|
+
import { useElementRef } from "../../internals/dom/use-element-ref.js";
|
|
6
|
+
import { useInView } from "../../internals/motion/in-view.js";
|
|
7
|
+
import { useReducedMotion } from "../../internals/motion/media-query.js";
|
|
8
|
+
import { staggerDelay } from "../../internals/motion/stagger.js";
|
|
9
|
+
/* empty css */
|
|
10
|
+
const STEP = 70;
|
|
11
|
+
const CAP = 420;
|
|
12
|
+
function BentoGrid({
|
|
13
|
+
className = "",
|
|
14
|
+
reveal = true,
|
|
15
|
+
accent,
|
|
16
|
+
children,
|
|
17
|
+
style,
|
|
18
|
+
onFocus,
|
|
19
|
+
...rest
|
|
20
|
+
}) {
|
|
21
|
+
const [node, nodeRef] = useElementRef();
|
|
22
|
+
const [shown, setShown] = useState(false);
|
|
23
|
+
const reduced = useReducedMotion();
|
|
24
|
+
const revealing = reveal && !reduced;
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!node) return;
|
|
27
|
+
const root = node;
|
|
28
|
+
function apply() {
|
|
29
|
+
const kids = Array.from(root.children).filter(
|
|
30
|
+
(el) => el instanceof HTMLElement
|
|
31
|
+
);
|
|
32
|
+
kids.forEach((el, i) => {
|
|
33
|
+
el.style.setProperty("--bento-i", String(i));
|
|
34
|
+
el.style.setProperty(
|
|
35
|
+
"--bento-delay",
|
|
36
|
+
`${staggerDelay(i, kids.length, STEP, "first", CAP)}ms`
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
apply();
|
|
41
|
+
const mo = new MutationObserver(apply);
|
|
42
|
+
mo.observe(root, { childList: true });
|
|
43
|
+
return () => {
|
|
44
|
+
mo.disconnect();
|
|
45
|
+
for (const el of Array.from(root.children)) {
|
|
46
|
+
if (el instanceof HTMLElement) {
|
|
47
|
+
el.style.removeProperty("--bento-i");
|
|
48
|
+
el.style.removeProperty("--bento-delay");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}, [node]);
|
|
53
|
+
useInView(node, {
|
|
54
|
+
enabled: revealing && !shown,
|
|
55
|
+
once: true,
|
|
56
|
+
// Any pixel, not a fraction: a grid taller than ~12 viewports could never
|
|
57
|
+
// show 8% of itself at once, and would stay hidden for good.
|
|
58
|
+
threshold: 0,
|
|
59
|
+
rootMargin: "0px 0px -6% 0px",
|
|
60
|
+
onChange: (v) => {
|
|
61
|
+
if (v) setShown(true);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
function handleFocus(event) {
|
|
65
|
+
setShown(true);
|
|
66
|
+
onFocus?.(event);
|
|
67
|
+
}
|
|
68
|
+
const mergedStyle = accent === void 0 ? style : { ...style, "--bento-accent": accent };
|
|
5
69
|
return /* @__PURE__ */ jsx(
|
|
6
70
|
"div",
|
|
7
71
|
{
|
|
72
|
+
ref: nodeRef,
|
|
8
73
|
className: cn(
|
|
9
|
-
"mx-auto grid max-w-7xl grid-cols-1 gap-4 md:auto-rows-[18rem] md:grid-cols-3",
|
|
74
|
+
"fancy-bento mx-auto grid max-w-7xl grid-cols-1 gap-4 md:auto-rows-[18rem] md:grid-cols-3",
|
|
75
|
+
revealing && "bento-reveal",
|
|
10
76
|
className
|
|
11
77
|
),
|
|
78
|
+
"data-state": revealing ? shown ? "shown" : "armed" : void 0,
|
|
79
|
+
style: mergedStyle,
|
|
80
|
+
onFocus: handleFocus,
|
|
12
81
|
...rest,
|
|
13
82
|
children
|
|
14
83
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { BentoFrame } from "./BentoFrame.js";
|
|
4
4
|
function BentoGridCard({
|
|
5
5
|
name,
|
|
6
6
|
description: desc,
|
|
@@ -12,34 +12,42 @@ function BentoGridCard({
|
|
|
12
12
|
...rest
|
|
13
13
|
}) {
|
|
14
14
|
return /* @__PURE__ */ jsxs(
|
|
15
|
-
|
|
15
|
+
BentoFrame,
|
|
16
16
|
{
|
|
17
|
-
className:
|
|
18
|
-
|
|
19
|
-
"bg-white [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]",
|
|
20
|
-
"transform-gpu dark:bg-black dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset] dark:[border:1px_solid_rgba(255,255,255,.1)]",
|
|
21
|
-
className
|
|
22
|
-
),
|
|
17
|
+
className: ["group col-span-3", className].join(" "),
|
|
18
|
+
panelClass: "justify-end",
|
|
23
19
|
...rest,
|
|
24
20
|
children: [
|
|
25
|
-
background,
|
|
26
|
-
/* @__PURE__ */ jsxs("div", { className: "pointer-events-none z-10 flex
|
|
27
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
28
|
-
/* @__PURE__ */ jsx("h3", { className: "text-
|
|
29
|
-
/* @__PURE__ */ jsx("p", { className: "max-w-lg text-neutral-400", children: desc })
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
21
|
+
background && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 -z-10 overflow-hidden", children: background }),
|
|
22
|
+
/* @__PURE__ */ jsxs("div", { className: "bento-lift pointer-events-none relative z-10 flex flex-col gap-1 p-6", children: [
|
|
23
|
+
icon && /* @__PURE__ */ jsx("div", { className: "bento-icon mb-3 size-11 [&_svg]:size-5", children: icon }),
|
|
24
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-medium tracking-tight text-neutral-900 dark:text-neutral-100", children: name }),
|
|
25
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-lg text-sm leading-relaxed text-neutral-500 dark:text-neutral-400", children: desc }),
|
|
26
|
+
/* @__PURE__ */ jsxs(
|
|
27
|
+
"a",
|
|
28
|
+
{
|
|
29
|
+
href,
|
|
30
|
+
className: "bento-cta pointer-events-auto mt-3 inline-flex w-fit items-center gap-1.5 rounded-md text-sm font-medium text-neutral-900 outline-none focus-visible:ring-2 focus-visible:ring-[var(--bento-accent,#8e9cff)] focus-visible:ring-offset-2 focus-visible:ring-offset-transparent dark:text-neutral-100",
|
|
31
|
+
children: [
|
|
32
|
+
cta,
|
|
33
|
+
/* @__PURE__ */ jsx(
|
|
34
|
+
"svg",
|
|
35
|
+
{
|
|
36
|
+
"aria-hidden": "true",
|
|
37
|
+
viewBox: "0 0 16 16",
|
|
38
|
+
className: "size-3.5 text-[var(--bento-accent,#8e9cff)]",
|
|
39
|
+
fill: "none",
|
|
40
|
+
stroke: "currentColor",
|
|
41
|
+
strokeWidth: "1.6",
|
|
42
|
+
strokeLinecap: "round",
|
|
43
|
+
strokeLinejoin: "round",
|
|
44
|
+
children: /* @__PURE__ */ jsx("path", { d: "M3 8h9.5M8.5 4l4 4-4 4" })
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
] })
|
|
43
51
|
]
|
|
44
52
|
}
|
|
45
53
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { BentoFrame } from "./BentoFrame.js";
|
|
4
4
|
function BentoGridItem({
|
|
5
5
|
className = "",
|
|
6
6
|
header,
|
|
@@ -10,19 +10,19 @@ function BentoGridItem({
|
|
|
10
10
|
...rest
|
|
11
11
|
}) {
|
|
12
12
|
return /* @__PURE__ */ jsxs(
|
|
13
|
-
|
|
13
|
+
BentoFrame,
|
|
14
14
|
{
|
|
15
|
-
className:
|
|
16
|
-
|
|
17
|
-
className
|
|
18
|
-
),
|
|
15
|
+
className: ["group/bento row-span-1", className].join(" "),
|
|
16
|
+
panelClass: "justify-between gap-4 p-4",
|
|
19
17
|
...rest,
|
|
20
18
|
children: [
|
|
21
19
|
header,
|
|
22
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
23
|
-
icon,
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
/* @__PURE__ */ jsxs("div", { className: "bento-lift relative", children: [
|
|
21
|
+
(icon || title) && /* @__PURE__ */ jsxs("div", { className: "mb-1.5 flex items-center gap-2.5", children: [
|
|
22
|
+
icon && /* @__PURE__ */ jsx("div", { className: "bento-icon size-8 [&_svg]:size-4", children: icon }),
|
|
23
|
+
title && /* @__PURE__ */ jsx("div", { className: "min-w-0 font-sans text-[15px] font-medium tracking-tight text-neutral-900 dark:text-neutral-100", children: title })
|
|
24
|
+
] }),
|
|
25
|
+
description && /* @__PURE__ */ jsx("div", { className: "font-sans text-[13px] leading-relaxed text-neutral-500 dark:text-neutral-400", children: description })
|
|
26
26
|
] })
|
|
27
27
|
]
|
|
28
28
|
}
|
|
@@ -1,25 +1,70 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* BorderBeam -
|
|
2
|
+
* BorderBeam - a single comet of light riding a container's border
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* The
|
|
4
|
+
* A white-hot head leads a long, feathered tail that fades from `colorFrom`
|
|
5
|
+
* to `colorTo` to nothing. The hairline under it stays dim except within
|
|
6
|
+
* the comet's reach, a soft bloom sits on the edge, and a faint spill of
|
|
7
|
+
* light falls just inside the container.
|
|
8
|
+
*
|
|
9
|
+
* Place it as the last child of a container with `position: relative`
|
|
10
|
+
* (and usually `overflow: hidden`). It inherits the container's
|
|
11
|
+
* `border-radius`; set `--border-beam-radius` if the path needs a radius
|
|
12
|
+
* the browser cannot read from the box itself.
|
|
6
13
|
*/
|
|
7
14
|
export interface BorderBeamProps {
|
|
8
15
|
/** Additional CSS classes */
|
|
9
16
|
className?: string;
|
|
10
|
-
/**
|
|
17
|
+
/** Diameter of the pool of light around the comet's head, in pixels */
|
|
11
18
|
size?: number;
|
|
12
|
-
/**
|
|
19
|
+
/** Time for one full lap of the border, in seconds */
|
|
13
20
|
duration?: number;
|
|
14
21
|
/** Border width in pixels */
|
|
15
22
|
borderWidth?: number;
|
|
16
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Where along the border the comet starts, as a percentage of the
|
|
25
|
+
* perimeter (0-100, clockwise from the top-left corner). With reduced
|
|
26
|
+
* motion, this is where it rests.
|
|
27
|
+
*/
|
|
17
28
|
anchor?: number;
|
|
18
|
-
/**
|
|
29
|
+
/** Colour of the comet's head and the start of its tail */
|
|
19
30
|
colorFrom?: string;
|
|
20
|
-
/**
|
|
31
|
+
/** Colour the tail fades into */
|
|
21
32
|
colorTo?: string;
|
|
22
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Delay in seconds. The comet runs from the first frame, already
|
|
35
|
+
* `delay` seconds behind where it would otherwise be, so stacked beams
|
|
36
|
+
* spread out along the border.
|
|
37
|
+
*/
|
|
23
38
|
delay?: number;
|
|
39
|
+
/** Length of the tail as a share of the perimeter (0-1) */
|
|
40
|
+
tail?: number;
|
|
41
|
+
/** Strength of the bloom and the inner spill of light (0-1) */
|
|
42
|
+
glow?: number;
|
|
43
|
+
/** Travel counter-clockwise instead of clockwise */
|
|
44
|
+
reverse?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** Number of overlapping pieces the sharp tail is drawn with. More pieces
|
|
47
|
+
* bend around the corners more smoothly. */
|
|
48
|
+
export declare const TAIL_SEGMENTS = 18;
|
|
49
|
+
/** The blurred bloom is forgiving: fewer, longer pieces are enough. */
|
|
50
|
+
export declare const BLOOM_SEGMENTS = 8;
|
|
51
|
+
export interface TailSegment {
|
|
52
|
+
/** Position along the tail, 0 = right behind the head, 1 = tail end */
|
|
53
|
+
t: number;
|
|
54
|
+
/** Opacity of this piece */
|
|
55
|
+
alpha: number;
|
|
56
|
+
/** Thickness factor (1 at the head, tapering towards the end) */
|
|
57
|
+
thickness: number;
|
|
24
58
|
}
|
|
25
|
-
|
|
59
|
+
/** Evenly spaced pieces with an eased fade and a tapering thickness. */
|
|
60
|
+
export declare function tailSegments(count: number): TailSegment[];
|
|
61
|
+
/**
|
|
62
|
+
* Turn `delay` into a phase: how far into its lap the comet is at first
|
|
63
|
+
* paint (a negative `animation-delay`), and where it rests when motion is
|
|
64
|
+
* reduced (a percentage of the perimeter).
|
|
65
|
+
*/
|
|
66
|
+
export declare function beamPhase(duration: number, delay: number, anchor: number, reverse: boolean): {
|
|
67
|
+
offset: number;
|
|
68
|
+
park: number;
|
|
69
|
+
};
|
|
70
|
+
export declare function BorderBeam({ className, size, duration, borderWidth, anchor, colorFrom, colorTo, delay, tail, glow, reverse, }: BorderBeamProps): import("react").JSX.Element;
|