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,77 +1,122 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useRef, useEffect,
|
|
2
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useRef, useEffect, useMemo } from "react";
|
|
4
4
|
import { cn } from "../../utils.js";
|
|
5
5
|
import { useReducedMotion, useMediaQuery } from "../../internals/motion/media-query.js";
|
|
6
6
|
import { DOCK_CONTEXT_KEY } from "./types.js";
|
|
7
|
+
/* empty css */
|
|
7
8
|
function Dock({
|
|
8
9
|
className = "",
|
|
9
10
|
magnification = 60,
|
|
10
11
|
distance = 140,
|
|
11
12
|
direction = "middle",
|
|
12
13
|
orientation = "horizontal",
|
|
14
|
+
spotlight = true,
|
|
15
|
+
reflection = true,
|
|
13
16
|
ariaLabel,
|
|
14
17
|
children
|
|
15
18
|
}) {
|
|
16
|
-
const [
|
|
19
|
+
const [tracked, setTracked] = useState({
|
|
17
20
|
x: Infinity,
|
|
18
|
-
y: Infinity
|
|
21
|
+
y: Infinity,
|
|
22
|
+
spotX: 0,
|
|
23
|
+
spotY: 0,
|
|
24
|
+
hovering: false
|
|
19
25
|
});
|
|
26
|
+
const shelf = useRef(null);
|
|
20
27
|
const reduced = useReducedMotion();
|
|
21
28
|
const coarse = useMediaQuery("(any-hover: none)");
|
|
22
29
|
const magnify = !reduced && !coarse;
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
const track = !coarse;
|
|
31
|
+
const frame = useRef(0);
|
|
32
|
+
const pending = useRef({ x: Infinity, y: Infinity });
|
|
33
|
+
useEffect(
|
|
34
|
+
() => () => {
|
|
35
|
+
if (frame.current && typeof cancelAnimationFrame === "function") {
|
|
36
|
+
cancelAnimationFrame(frame.current);
|
|
37
|
+
}
|
|
38
|
+
frame.current = 0;
|
|
39
|
+
},
|
|
40
|
+
[]
|
|
41
|
+
);
|
|
42
|
+
function flush() {
|
|
43
|
+
frame.current = 0;
|
|
44
|
+
const { x, y } = pending.current;
|
|
45
|
+
if (Number.isFinite(x) && Number.isFinite(y)) {
|
|
46
|
+
const rect = shelf.current?.getBoundingClientRect();
|
|
47
|
+
setTracked((prev) => ({
|
|
48
|
+
x,
|
|
49
|
+
y,
|
|
50
|
+
spotX: rect ? x - rect.left : prev.spotX,
|
|
51
|
+
spotY: rect ? y - rect.top : prev.spotY,
|
|
52
|
+
hovering: true
|
|
53
|
+
}));
|
|
54
|
+
} else {
|
|
55
|
+
setTracked((prev) => ({ ...prev, x, y, hovering: false }));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function queue(x, y) {
|
|
59
|
+
pending.current = { x, y };
|
|
60
|
+
if (!frame.current) frame.current = requestAnimationFrame(flush);
|
|
61
|
+
}
|
|
38
62
|
const context = useMemo(
|
|
39
63
|
() => ({
|
|
40
|
-
mouseX: { current:
|
|
41
|
-
mouseY: { current:
|
|
64
|
+
mouseX: { current: tracked.x },
|
|
65
|
+
mouseY: { current: tracked.y },
|
|
42
66
|
magnification,
|
|
43
67
|
distance,
|
|
44
68
|
orientation,
|
|
45
|
-
magnify
|
|
69
|
+
magnify,
|
|
70
|
+
reflection
|
|
46
71
|
}),
|
|
47
|
-
[
|
|
72
|
+
[tracked.x, tracked.y, magnification, distance, orientation, magnify, reflection]
|
|
48
73
|
);
|
|
49
74
|
function onPointerMove(e) {
|
|
50
|
-
if (!
|
|
75
|
+
if (!track) return;
|
|
51
76
|
if (e.pointerType === "touch" || !e.isPrimary) return;
|
|
52
|
-
|
|
53
|
-
schedule(clientX, clientY);
|
|
77
|
+
queue(e.clientX, e.clientY);
|
|
54
78
|
}
|
|
55
79
|
function onPointerLeave() {
|
|
56
|
-
|
|
80
|
+
queue(Infinity, Infinity);
|
|
57
81
|
}
|
|
58
82
|
const directionClass = direction === "top" ? "items-start" : direction === "bottom" ? "items-end" : "items-center";
|
|
59
|
-
|
|
83
|
+
const style = {
|
|
84
|
+
"--dock-x": `${tracked.spotX}px`,
|
|
85
|
+
"--dock-y": `${tracked.spotY}px`
|
|
86
|
+
};
|
|
87
|
+
return /* @__PURE__ */ jsx(DOCK_CONTEXT_KEY.Provider, { value: context, children: /* @__PURE__ */ jsxs(
|
|
60
88
|
"div",
|
|
61
89
|
{
|
|
90
|
+
ref: shelf,
|
|
62
91
|
className: cn(
|
|
63
|
-
"mx-auto flex h-[58px] w-max gap-
|
|
92
|
+
"dock-shelf relative isolate mx-auto flex h-[58px] w-max gap-3 rounded-2xl border p-2 backdrop-blur-md backdrop-saturate-150",
|
|
93
|
+
"border-black/[0.08] bg-white/75 shadow-[inset_0_1px_0_rgba(255,255,255,0.9),0_1px_2px_rgba(0,0,0,0.05),0_14px_32px_-14px_rgba(0,0,0,0.22)]",
|
|
94
|
+
"dark:border-white/[0.08] dark:bg-[rgba(20,20,22,0.7)] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.08),0_1px_0_rgba(0,0,0,0.5),0_18px_40px_-14px_rgba(0,0,0,0.8)]",
|
|
64
95
|
orientation === "vertical" && "h-max w-[58px] flex-col",
|
|
65
96
|
directionClass,
|
|
66
97
|
className
|
|
67
98
|
),
|
|
99
|
+
style,
|
|
100
|
+
"data-orientation": orientation,
|
|
101
|
+
"data-spotlight": spotlight || void 0,
|
|
102
|
+
"data-reflection": reflection || void 0,
|
|
103
|
+
"data-hover": tracked.hovering || void 0,
|
|
68
104
|
onPointerMove,
|
|
69
105
|
onPointerLeave,
|
|
70
106
|
role: "toolbar",
|
|
71
|
-
"aria-orientation": orientation,
|
|
72
107
|
"aria-label": ariaLabel,
|
|
108
|
+
"aria-orientation": orientation,
|
|
73
109
|
tabIndex: 0,
|
|
74
|
-
children
|
|
110
|
+
children: [
|
|
111
|
+
/* @__PURE__ */ jsxs("span", { className: "dock-deco", "aria-hidden": "true", children: [
|
|
112
|
+
spotlight && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
113
|
+
/* @__PURE__ */ jsx("span", { className: "dock-spot" }),
|
|
114
|
+
/* @__PURE__ */ jsx("span", { className: "dock-rim", children: /* @__PURE__ */ jsx("span", { className: "dock-rim-light" }) })
|
|
115
|
+
] }),
|
|
116
|
+
/* @__PURE__ */ jsx("span", { className: "dock-frame" })
|
|
117
|
+
] }),
|
|
118
|
+
children
|
|
119
|
+
]
|
|
75
120
|
}
|
|
76
121
|
) });
|
|
77
122
|
}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
/** Resting edge length of every icon, in pixels. */
|
|
3
|
+
export declare const DOCK_BASE_SIZE = 40;
|
|
4
|
+
/**
|
|
5
|
+
* The magnification curve: a cosine bell rather than a straight line.
|
|
6
|
+
* Full `magnification` under the pointer, easing smoothly to nothing at
|
|
7
|
+
* `distance`, with a flat top and flat shoulders — so neighbours swell and
|
|
8
|
+
* settle instead of forming a tent. Pure, so every framework shares the exact
|
|
9
|
+
* numbers.
|
|
10
|
+
*/
|
|
11
|
+
export declare function dockIconSize(offset: number, magnification: number, distance: number): number;
|
|
2
12
|
export interface DockIconProps {
|
|
3
13
|
/** Additional CSS classes */
|
|
4
14
|
className?: string;
|
|
@@ -6,11 +16,12 @@ export interface DockIconProps {
|
|
|
6
16
|
children?: ReactNode;
|
|
7
17
|
}
|
|
8
18
|
/**
|
|
9
|
-
* One item in a `Dock`, sized from its distance to the pointer
|
|
19
|
+
* One item in a `Dock`, sized from its distance to the pointer, with a floor
|
|
20
|
+
* reflection and an indicator dot when it is the icon under the pointer.
|
|
10
21
|
*
|
|
11
22
|
* The node arrives through `useElementRef` rather than a plain `useRef`: the
|
|
12
|
-
*
|
|
13
|
-
* element actually exists. Before it does, the
|
|
23
|
+
* size is computed while rendering, so the component has to re-render once the
|
|
24
|
+
* element actually exists. Before it does, the offset is `Infinity` and the
|
|
14
25
|
* icon renders at its resting 40px — which is also the size it has at mount,
|
|
15
26
|
* with the pointer still at `Infinity`, so nothing moves on that extra render.
|
|
16
27
|
*/
|
|
@@ -2,37 +2,41 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useElementRef } from "../../internals/dom/use-element-ref.js";
|
|
4
4
|
import { useDockContext } from "./types.js";
|
|
5
|
+
/* empty css */
|
|
6
|
+
const DOCK_BASE_SIZE = 40;
|
|
7
|
+
function dockIconSize(offset, magnification, distance) {
|
|
8
|
+
if (!distance || !magnification || !Number.isFinite(offset)) return DOCK_BASE_SIZE;
|
|
9
|
+
const t = Math.min(Math.max(Math.abs(offset) / distance, 0), 1);
|
|
10
|
+
return DOCK_BASE_SIZE + magnification * 0.5 * (1 + Math.cos(Math.PI * t));
|
|
11
|
+
}
|
|
5
12
|
function DockIcon({ className = "", children }) {
|
|
6
13
|
const context = useDockContext();
|
|
7
14
|
const [node, iconRef] = useElementRef();
|
|
8
|
-
function
|
|
9
|
-
|
|
15
|
+
function measure() {
|
|
16
|
+
const vertical = context.orientation === "vertical";
|
|
17
|
+
const pointer = vertical ? context.mouseY.current : context.mouseX.current;
|
|
18
|
+
if (!node || !Number.isFinite(pointer)) return { offset: Infinity, half: 0 };
|
|
10
19
|
const bounds = node.getBoundingClientRect();
|
|
11
|
-
|
|
12
|
-
return context.mouseY.current - bounds.y - bounds.height / 2;
|
|
13
|
-
}
|
|
14
|
-
return context.mouseX.current - bounds.x - bounds.width / 2;
|
|
15
|
-
}
|
|
16
|
-
function computeIconWidth() {
|
|
17
|
-
if (!context.magnify) return 40;
|
|
18
|
-
const distanceCalc = calculateDistance();
|
|
19
|
-
if (!context.distance || !context.magnification) return 40;
|
|
20
|
-
if (Math.abs(distanceCalc) < context.distance) {
|
|
21
|
-
return (1 - Math.abs(distanceCalc) / context.distance) * context.magnification + 40;
|
|
22
|
-
}
|
|
23
|
-
return 40;
|
|
20
|
+
return vertical ? { offset: pointer - bounds.y - bounds.height / 2, half: bounds.height / 2 } : { offset: pointer - bounds.x - bounds.width / 2, half: bounds.width / 2 };
|
|
24
21
|
}
|
|
25
|
-
const
|
|
22
|
+
const probe = measure();
|
|
23
|
+
const iconSize = context.magnify ? dockIconSize(probe.offset, context.magnification, context.distance) : DOCK_BASE_SIZE;
|
|
24
|
+
const active = Math.abs(probe.offset) <= probe.half && probe.half > 0;
|
|
26
25
|
return /* @__PURE__ */ jsx(
|
|
27
26
|
"div",
|
|
28
27
|
{
|
|
29
28
|
ref: iconRef,
|
|
30
|
-
className: `flex aspect-square cursor-pointer items-center justify-center rounded-full
|
|
31
|
-
style: { width: `${
|
|
29
|
+
className: `dock-icon relative z-[1] flex aspect-square cursor-pointer items-center justify-center rounded-full ${className}`,
|
|
30
|
+
style: { width: `${iconSize}px`, height: `${iconSize}px` },
|
|
31
|
+
"data-orientation": context.orientation,
|
|
32
|
+
"data-reflection": context.reflection || void 0,
|
|
33
|
+
"data-dock-active": active || void 0,
|
|
32
34
|
children
|
|
33
35
|
}
|
|
34
36
|
);
|
|
35
37
|
}
|
|
36
38
|
export {
|
|
37
|
-
|
|
39
|
+
DOCK_BASE_SIZE,
|
|
40
|
+
DockIcon,
|
|
41
|
+
dockIconSize
|
|
38
42
|
};
|
|
@@ -2,5 +2,8 @@ export interface DockSeparatorProps {
|
|
|
2
2
|
/** Additional CSS classes */
|
|
3
3
|
className?: string;
|
|
4
4
|
}
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* A hairline that fades out at both ends, so it reads as a seam in the glass
|
|
7
|
+
* rather than a bar laid on top of it. Laid across the dock's own axis.
|
|
8
|
+
*/
|
|
6
9
|
export declare function DockSeparator({ className }: DockSeparatorProps): import("react").JSX.Element;
|
|
@@ -7,9 +7,12 @@ function DockSeparator({ className = "" }) {
|
|
|
7
7
|
return /* @__PURE__ */ jsx(
|
|
8
8
|
"div",
|
|
9
9
|
{
|
|
10
|
+
role: "separator",
|
|
11
|
+
"aria-orientation": context.orientation === "vertical" ? "horizontal" : "vertical",
|
|
10
12
|
className: cn(
|
|
11
|
-
"
|
|
12
|
-
context.orientation === "vertical" ? "h-
|
|
13
|
+
"relative z-[1] block shrink-0 self-center",
|
|
14
|
+
context.orientation === "vertical" ? "h-px w-4/5 bg-gradient-to-r" : "h-4/5 w-px bg-gradient-to-b",
|
|
15
|
+
"from-transparent via-black/15 to-transparent dark:via-white/15",
|
|
13
16
|
className
|
|
14
17
|
)
|
|
15
18
|
}
|
|
@@ -2,8 +2,11 @@ export type DataOrientation = "vertical" | "horizontal";
|
|
|
2
2
|
export type Direction = "top" | "middle" | "bottom";
|
|
3
3
|
export interface DockContext {
|
|
4
4
|
/**
|
|
5
|
-
* The pointer's
|
|
6
|
-
* the
|
|
5
|
+
* The pointer's VIEWPORT X (`clientX`), `Infinity` when the pointer is
|
|
6
|
+
* outside the dock. Viewport, not page, because `DockIcon` measures itself
|
|
7
|
+
* with `getBoundingClientRect()`, which is viewport-relative. In a box
|
|
8
|
+
* rather than as a bare number — the shape the source publishes, kept
|
|
9
|
+
* verbatim so the exported type is unchanged.
|
|
7
10
|
*
|
|
8
11
|
* There the box exists so children can read a reactive update through a
|
|
9
12
|
* stable object; here the box is REBUILT by `Dock` on every pointer frame,
|
|
@@ -13,7 +16,7 @@ export interface DockContext {
|
|
|
13
16
|
mouseX: {
|
|
14
17
|
current: number;
|
|
15
18
|
};
|
|
16
|
-
/** The pointer's
|
|
19
|
+
/** The pointer's viewport Y (`clientY`). Same box treatment as `mouseX`. */
|
|
17
20
|
mouseY: {
|
|
18
21
|
current: number;
|
|
19
22
|
};
|
|
@@ -23,11 +26,15 @@ export interface DockContext {
|
|
|
23
26
|
/**
|
|
24
27
|
* False when the visitor asked for reduced motion, or when the device has no
|
|
25
28
|
* real pointer to track. `Dock` owns the two media queries behind it and
|
|
26
|
-
* `DockIcon` reads it before
|
|
27
|
-
*
|
|
28
|
-
* frame. Read-only: only `Dock` may write it.
|
|
29
|
+
* `DockIcon` reads it before sizing itself. Read-only: only `Dock` may
|
|
30
|
+
* write it.
|
|
29
31
|
*/
|
|
30
32
|
readonly magnify: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Whether each icon casts its soft floor reflection (the contact shadow /
|
|
35
|
+
* glow ellipse under it). Mirrors the `reflection` prop on `Dock`.
|
|
36
|
+
*/
|
|
37
|
+
readonly reflection: boolean;
|
|
31
38
|
}
|
|
32
39
|
/**
|
|
33
40
|
* The context `DockIcon` and `DockSeparator` read to find their dock. The
|
|
@@ -5,18 +5,21 @@ export interface MenuContentKeydownOptions {
|
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
7
|
* Wires ArrowUp/Down, Home/End and single-character typeahead to a level's
|
|
8
|
-
*
|
|
9
|
-
* the panel is a `DropdownMenuContent
|
|
10
|
-
*
|
|
11
|
-
* that level's panel was opened or
|
|
8
|
+
* `MenuFocus` core, and Tab to `options.onTab`. Identical semantics whether
|
|
9
|
+
* the panel is a `DropdownMenuContent`, a `ContextMenuContent`, or either
|
|
10
|
+
* one's nested `*SubContent` — the keys navigate whatever level currently
|
|
11
|
+
* holds real DOM focus, regardless of how that level's panel was opened or
|
|
12
|
+
* positioned.
|
|
12
13
|
*
|
|
13
14
|
* ArrowRight/ArrowLeft are deliberately not handled here: their key strings
|
|
14
15
|
* are more than one character, so the typeahead fallback below already
|
|
15
16
|
* ignores them, and `*SubTrigger`/`*SubContent` each own their half of that
|
|
16
17
|
* pair directly on the element the key is actually about.
|
|
17
18
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
19
|
+
* The parameter is typed structurally (the handful of members this function
|
|
20
|
+
* actually reads) rather than as the concrete DOM `KeyboardEvent`, so a
|
|
21
|
+
* caller can pass any event shape that satisfies it — including a
|
|
22
|
+
* framework's own synthetic event — with no unwrap or cast.
|
|
20
23
|
*/
|
|
21
24
|
export declare function handleMenuContentKeydown(event: Pick<KeyboardEvent, "key" | "ctrlKey" | "metaKey" | "altKey"> & {
|
|
22
25
|
preventDefault(): void;
|
|
@@ -30,8 +33,8 @@ export declare function handleMenuContentKeydown(event: Pick<KeyboardEvent, "key
|
|
|
30
33
|
* shared across levels, which is what keeps a deeply nested submenu from
|
|
31
34
|
* closing an unrelated one two levels up.
|
|
32
35
|
*
|
|
33
|
-
* Allocation-only
|
|
34
|
-
*
|
|
36
|
+
* Allocation-only: it installs no listener and starts no timer, so building
|
|
37
|
+
* one is safe to do exactly once per component instance.
|
|
35
38
|
*/
|
|
36
39
|
export declare function createOpenSubRegistry(): {
|
|
37
40
|
registerOpenSub(triggerEl: HTMLElement, close: () => void): () => void;
|
|
@@ -140,13 +140,11 @@ function createEditorialEngine(stage, options) {
|
|
|
140
140
|
function makeLineEl(className) {
|
|
141
141
|
const element = document.createElement("span");
|
|
142
142
|
element.className = className;
|
|
143
|
-
element.setAttribute("aria-hidden", "true");
|
|
144
143
|
return element;
|
|
145
144
|
}
|
|
146
145
|
const orbEls = ORB_DEFS.map((def) => {
|
|
147
146
|
const element = document.createElement("div");
|
|
148
147
|
element.className = "ee-orb";
|
|
149
|
-
element.setAttribute("aria-hidden", "true");
|
|
150
148
|
const [r, g, b] = def.color;
|
|
151
149
|
element.style.background = `radial-gradient(circle at 35% 35%, rgba(${r},${g},${b},0.35), rgba(${r},${g},${b},0.12) 55%, transparent 72%)`;
|
|
152
150
|
element.style.boxShadow = `0 0 60px 15px rgba(${r},${g},${b},0.18), 0 0 120px 40px rgba(${r},${g},${b},0.07)`;
|
|
@@ -155,7 +153,6 @@ function createEditorialEngine(stage, options) {
|
|
|
155
153
|
});
|
|
156
154
|
const dropCapEl = document.createElement("div");
|
|
157
155
|
dropCapEl.className = "ee-drop-cap";
|
|
158
|
-
dropCapEl.setAttribute("aria-hidden", "true");
|
|
159
156
|
stage.appendChild(dropCapEl);
|
|
160
157
|
const preparedBody = prepareWithSegments(body, bodyFont, { whiteSpace: "pre-wrap" });
|
|
161
158
|
const preparedPullquotes = pullquotes.map((text) => prepareWithSegments(text, pullquoteFont));
|
|
@@ -439,7 +436,6 @@ function createEditorialEngine(stage, options) {
|
|
|
439
436
|
syncPool(pullquoteBoxPool, pullquoteRects.length, () => {
|
|
440
437
|
const element = document.createElement("div");
|
|
441
438
|
element.className = "ee-pullquote-box";
|
|
442
|
-
element.setAttribute("aria-hidden", "true");
|
|
443
439
|
return element;
|
|
444
440
|
});
|
|
445
441
|
for (let i = 0; i < pullquoteRects.length; i++) {
|
|
@@ -541,16 +537,7 @@ function createEditorialEngine(stage, options) {
|
|
|
541
537
|
window.removeEventListener("pointerup", onPointerUp);
|
|
542
538
|
window.removeEventListener("pointercancel", onPointerUp);
|
|
543
539
|
document.removeEventListener("selectionchange", onSelectionChange);
|
|
544
|
-
|
|
545
|
-
...bodyLinePool,
|
|
546
|
-
...headlinePool,
|
|
547
|
-
...pullquoteLinePool,
|
|
548
|
-
...pullquoteBoxPool,
|
|
549
|
-
...orbEls,
|
|
550
|
-
dropCapEl
|
|
551
|
-
]) {
|
|
552
|
-
element.remove();
|
|
553
|
-
}
|
|
540
|
+
stage.replaceChildren();
|
|
554
541
|
};
|
|
555
542
|
}
|
|
556
543
|
export {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { SDR_CORE_SCALE, SDR_KNEE_START, SDR_SAT_BOOST } from "./fireworks-shared.js";
|
|
3
3
|
import { STRETCH } from "./fireworks-shared.js";
|
|
4
4
|
import { TRAIL_FADE_RATE } from "./webgpu-renderer.js";
|
|
5
|
-
import { isDev } from "./dev.js";
|
|
6
5
|
const MAX_DPR = 2;
|
|
7
6
|
const INSTANCE_FLOATS = 8;
|
|
8
7
|
const INSTANCE_STRIDE = INSTANCE_FLOATS * 4;
|
|
@@ -167,9 +166,6 @@ function compileShader(gl, type, source) {
|
|
|
167
166
|
gl.shaderSource(shader, source);
|
|
168
167
|
gl.compileShader(shader);
|
|
169
168
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
170
|
-
if (isDev()) {
|
|
171
|
-
console.warn("[FireworksHdr] WebGL2 shader compile failed:", gl.getShaderInfoLog(shader));
|
|
172
|
-
}
|
|
173
169
|
gl.deleteShader(shader);
|
|
174
170
|
return null;
|
|
175
171
|
}
|
|
@@ -195,9 +191,6 @@ function linkProgram(gl, vsSrc, fsSrc) {
|
|
|
195
191
|
gl.deleteShader(vs);
|
|
196
192
|
gl.deleteShader(fs);
|
|
197
193
|
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
198
|
-
if (isDev()) {
|
|
199
|
-
console.warn("[FireworksHdr] WebGL2 program link failed:", gl.getProgramInfoLog(program));
|
|
200
|
-
}
|
|
201
194
|
gl.deleteProgram(program);
|
|
202
195
|
return null;
|
|
203
196
|
}
|
|
@@ -232,18 +225,8 @@ function startWebGl2Fireworks(canvas, opts) {
|
|
|
232
225
|
} catch {
|
|
233
226
|
}
|
|
234
227
|
}
|
|
235
|
-
if (isDev()) {
|
|
236
|
-
console.info(
|
|
237
|
-
renderLevel === "webgl-p3" ? "[FireworksHdr] WebGL2 fallback: webgl-p3 (wide-gamut drawing buffer)" : "[FireworksHdr] WebGL2 fallback: webgl-sdr"
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
228
|
const floatAccum = !!glc.getExtension("EXT_color_buffer_float");
|
|
241
229
|
if (wantFloatAccum && !floatAccum) {
|
|
242
|
-
if (isDev()) {
|
|
243
|
-
console.warn(
|
|
244
|
-
"[FireworksHdr] WebGL2 float-accum probe/context mismatch; bailing to static fallback"
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
230
|
return null;
|
|
248
231
|
}
|
|
249
232
|
let renderScale = opts.renderScale ?? 1;
|
|
@@ -253,7 +236,6 @@ function startWebGl2Fireworks(canvas, opts) {
|
|
|
253
236
|
function onContextLost(e) {
|
|
254
237
|
e.preventDefault();
|
|
255
238
|
lost = true;
|
|
256
|
-
if (isDev()) console.warn("[FireworksHdr] WebGL2 context lost");
|
|
257
239
|
}
|
|
258
240
|
canvas.addEventListener("webglcontextlost", onContextLost, false);
|
|
259
241
|
let accum = null;
|
|
@@ -285,7 +267,6 @@ function startWebGl2Fireworks(canvas, opts) {
|
|
|
285
267
|
fadeProgram = floatAccum ? null : linkProgram(glc, FULLSCREEN_VS, FADE_FS);
|
|
286
268
|
const programsOk = floatAccum ? particleProgram && decayProgram && displayProgram : particleProgram && fadeProgram;
|
|
287
269
|
if (!programsOk) {
|
|
288
|
-
if (isDev()) console.warn("[FireworksHdr] WebGL2 program setup failed");
|
|
289
270
|
cleanupPartial();
|
|
290
271
|
return null;
|
|
291
272
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { EXPOSURE_AMBIENT } from "./fireworks-shared.js";
|
|
3
3
|
import { STRETCH } from "./fireworks-shared.js";
|
|
4
|
-
import { isDev } from "./dev.js";
|
|
5
4
|
const TRAIL_FADE_RATE = 7;
|
|
6
5
|
const MAX_DPR = 2;
|
|
7
6
|
const ACCUM_FORMAT = "rgba16float";
|
|
@@ -268,7 +267,6 @@ async function startWebGpuFireworks(canvas, opts) {
|
|
|
268
267
|
const adapter = await gpu.requestAdapter();
|
|
269
268
|
if (!adapter) return null;
|
|
270
269
|
const device = await adapter.requestDevice();
|
|
271
|
-
const { GPUShaderStage, GPUBufferUsage, GPUTextureUsage } = globalThis;
|
|
272
270
|
const configuration = {
|
|
273
271
|
device,
|
|
274
272
|
format: ACCUM_FORMAT,
|
|
@@ -284,11 +282,8 @@ async function startWebGpuFireworks(canvas, opts) {
|
|
|
284
282
|
}
|
|
285
283
|
probe.configure(configuration);
|
|
286
284
|
probe.unconfigure();
|
|
287
|
-
} catch
|
|
285
|
+
} catch {
|
|
288
286
|
device.destroy();
|
|
289
|
-
if (isDev()) {
|
|
290
|
-
console.warn("[FireworksHdr] WebGPU canvas configuration rejected:", error);
|
|
291
|
-
}
|
|
292
287
|
return null;
|
|
293
288
|
}
|
|
294
289
|
const maybeContext = canvas.getContext("webgpu");
|
|
@@ -308,20 +303,9 @@ async function startWebGpuFireworks(canvas, opts) {
|
|
|
308
303
|
const hdrOutput = extendedToneMapping && hdrDisplay;
|
|
309
304
|
let destroyed = false;
|
|
310
305
|
let lost = false;
|
|
311
|
-
device.lost.then((
|
|
306
|
+
device.lost.then(() => {
|
|
312
307
|
lost = true;
|
|
313
|
-
if (!destroyed && isDev()) {
|
|
314
|
-
console.warn(`[FireworksHdr] WebGPU device lost (${info.reason}): ${info.message}`);
|
|
315
|
-
}
|
|
316
308
|
});
|
|
317
|
-
if (isDev()) {
|
|
318
|
-
device.addEventListener("uncapturederror", (event) => {
|
|
319
|
-
console.error(
|
|
320
|
-
"[FireworksHdr] WebGPU uncaptured error:",
|
|
321
|
-
event.error.message
|
|
322
|
-
);
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
309
|
const module = device.createShaderModule({ code: WGSL });
|
|
326
310
|
let renderScale = opts.renderScale ?? 1;
|
|
327
311
|
const instanceCapacity = Math.max(1, opts.maxInstances);
|
|
@@ -443,10 +427,7 @@ async function startWebGpuFireworks(canvas, opts) {
|
|
|
443
427
|
device.destroy();
|
|
444
428
|
}
|
|
445
429
|
};
|
|
446
|
-
} catch
|
|
447
|
-
if (isDev()) {
|
|
448
|
-
console.warn("[FireworksHdr] WebGPU init failed:", error);
|
|
449
|
-
}
|
|
430
|
+
} catch {
|
|
450
431
|
return null;
|
|
451
432
|
}
|
|
452
433
|
}
|
|
@@ -1,8 +1,49 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
export type FlipCardTrigger = "hover" | "click";
|
|
2
3
|
export interface FlipCardProps {
|
|
4
|
+
/** Axis of rotation */
|
|
3
5
|
rotate?: "x" | "y";
|
|
6
|
+
/**
|
|
7
|
+
* What flips the card. `hover` (default) flips on pointer hover and on
|
|
8
|
+
* keyboard focus; `click` makes the card a toggle button (click, tap,
|
|
9
|
+
* Enter or Space).
|
|
10
|
+
*/
|
|
11
|
+
trigger?: FlipCardTrigger;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the back is showing. Seeds the card on mount; changing it later
|
|
14
|
+
* turns the card the rest of the way. Pair with `onFlip` to keep it in sync
|
|
15
|
+
* (the React counterpart of the Svelte source's `bind:flipped`).
|
|
16
|
+
*/
|
|
17
|
+
flipped?: boolean;
|
|
18
|
+
/** Called with the new state after every flip, however it happened. */
|
|
19
|
+
onFlip?: (flipped: boolean) => void;
|
|
20
|
+
/** Length of one flip in milliseconds */
|
|
21
|
+
duration?: number;
|
|
22
|
+
/** Light the faces as they turn: a sheen sweeping across and shading edge-on */
|
|
23
|
+
glare?: boolean;
|
|
24
|
+
/** Accessible name for the card (announced in click mode) */
|
|
25
|
+
label?: string;
|
|
26
|
+
/** Additional CSS classes on the card */
|
|
4
27
|
className?: string;
|
|
28
|
+
/** Front face content */
|
|
5
29
|
children?: ReactNode;
|
|
30
|
+
/** Back face content */
|
|
6
31
|
back?: ReactNode;
|
|
7
32
|
}
|
|
8
|
-
|
|
33
|
+
/** Whether an accumulated angle (a whole number of half turns) shows the back. */
|
|
34
|
+
export declare function showsBack(angle: number): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Which way to turn, from where the pointer crosses the card's edge: the
|
|
37
|
+
* card turns the way the pointer travels, so it seems pushed by it.
|
|
38
|
+
* `entering` is true on enter, false on leave.
|
|
39
|
+
*/
|
|
40
|
+
export declare function flipDirection(rect: {
|
|
41
|
+
left: number;
|
|
42
|
+
top: number;
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
}, point: {
|
|
46
|
+
x: number;
|
|
47
|
+
y: number;
|
|
48
|
+
}, axis: "x" | "y", entering: boolean): 1 | -1;
|
|
49
|
+
export declare function FlipCard({ rotate, trigger, flipped, onFlip, duration, glare, label, className, children, back, }: FlipCardProps): import("react").JSX.Element;
|