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.
Files changed (86) hide show
  1. package/README.md +52 -19
  2. package/dist/components/animated-beam/AnimatedBeam.d.ts +47 -11
  3. package/dist/components/animated-beam/AnimatedBeam.js +293 -118
  4. package/dist/components/animated-tooltip/AnimatedTooltip.d.ts +10 -1
  5. package/dist/components/animated-tooltip/AnimatedTooltip.js +170 -101
  6. package/dist/components/bento-grid/BentoFrame.d.ts +16 -0
  7. package/dist/components/bento-grid/BentoFrame.js +28 -0
  8. package/dist/components/bento-grid/BentoGrid.d.ts +5 -1
  9. package/dist/components/bento-grid/BentoGrid.js +71 -2
  10. package/dist/components/bento-grid/BentoGridCard.js +34 -26
  11. package/dist/components/bento-grid/BentoGridItem.js +10 -10
  12. package/dist/components/border-beam/BorderBeam.d.ts +55 -10
  13. package/dist/components/border-beam/BorderBeam.js +93 -16
  14. package/dist/components/compare/Compare.d.ts +32 -8
  15. package/dist/components/compare/Compare.js +213 -95
  16. package/dist/components/container-scroll/ContainerScroll.d.ts +26 -4
  17. package/dist/components/container-scroll/ContainerScroll.js +88 -49
  18. package/dist/components/dock/Dock.d.ts +10 -7
  19. package/dist/components/dock/Dock.js +76 -31
  20. package/dist/components/dock/DockIcon.d.ts +14 -3
  21. package/dist/components/dock/DockIcon.js +23 -19
  22. package/dist/components/dock/DockSeparator.d.ts +4 -1
  23. package/dist/components/dock/DockSeparator.js +5 -2
  24. package/dist/components/dock/types.d.ts +13 -6
  25. package/dist/components/dropdown-menu/menu-shared.d.ts +11 -8
  26. package/dist/components/editorial-engine/engine.js +1 -14
  27. package/dist/components/fireworks-hdr/webgl2-renderer.js +0 -19
  28. package/dist/components/fireworks-hdr/webgpu-renderer.js +3 -22
  29. package/dist/components/flip-card/FlipCard.d.ts +42 -1
  30. package/dist/components/flip-card/FlipCard.js +178 -25
  31. package/dist/components/flip-card/index.d.ts +1 -1
  32. package/dist/components/fluid-cursor/FluidCursor.js +5 -4
  33. package/dist/components/fluid-cursor/dev.d.ts +1 -0
  34. package/dist/components/fluid-cursor/dev.js +8 -0
  35. package/dist/components/fluid-cursor/fluid-shared.d.ts +0 -2
  36. package/dist/components/fluid-cursor/fluid-shared.js +0 -2
  37. package/dist/components/fluid-cursor/webgpu-engine.js +10 -24
  38. package/dist/components/glow-border/GlowBorder.d.ts +41 -1
  39. package/dist/components/glow-border/GlowBorder.js +96 -16
  40. package/dist/components/glow-border/index.d.ts +2 -2
  41. package/dist/components/gradient-button/GradientButton.d.ts +10 -5
  42. package/dist/components/gradient-button/GradientButton.js +25 -18
  43. package/dist/components/image-trail-cursor/ImageTrailCursor.js +1 -0
  44. package/dist/components/image-trail-cursor/trail-variants.d.ts +37 -1
  45. package/dist/components/image-trail-cursor/trail-variants.js +331 -8
  46. package/dist/components/interactive-hover-button/InteractiveHoverButton.js +30 -35
  47. package/dist/components/marquee/Marquee.d.ts +7 -3
  48. package/dist/components/marquee/Marquee.js +134 -2
  49. package/dist/components/marquee/ReviewCard.d.ts +2 -1
  50. package/dist/components/marquee/ReviewCard.js +20 -9
  51. package/dist/components/meteors/Meteors.d.ts +32 -10
  52. package/dist/components/meteors/Meteors.js +53 -20
  53. package/dist/components/mosaic-glow/mosaic-glow-core.d.ts +98 -10
  54. package/dist/components/neon-border/NeonBorder.d.ts +24 -5
  55. package/dist/components/neon-border/NeonBorder.js +37 -7
  56. package/dist/components/pagination/Pagination.js +220 -82
  57. package/dist/components/rainbow-button/RainbowButton.js +13 -5
  58. package/dist/components/ripple-button/RippleButton.d.ts +20 -1
  59. package/dist/components/ripple-button/RippleButton.js +44 -21
  60. package/dist/components/shimmer-button/ShimmerButton.d.ts +5 -5
  61. package/dist/components/shimmer-button/ShimmerButton.js +17 -19
  62. package/dist/components/smooth-cursor/SmoothCursor.d.ts +13 -9
  63. package/dist/components/smooth-cursor/SmoothCursor.js +114 -163
  64. package/dist/components/smooth-cursor/smooth-cursor-core.d.ts +85 -0
  65. package/dist/components/smooth-cursor/smooth-cursor-core.js +327 -0
  66. package/dist/components/stepper/Step.js +32 -19
  67. package/dist/components/stepper/Stepper.js +18 -2
  68. package/dist/components/stepper/types.d.ts +13 -0
  69. package/dist/components/timeline/Timeline.d.ts +8 -1
  70. package/dist/components/timeline/Timeline.js +165 -76
  71. package/dist/index.js +2 -1
  72. package/dist/internals/markdown.d.ts +5 -0
  73. package/dist/internals/motion/haptics.d.ts +0 -4
  74. package/dist/internals/motion/presence-core.d.ts +59 -0
  75. package/dist/internals/motion/presence-core.js +162 -0
  76. package/dist/internals/motion/presence.d.ts +2 -4
  77. package/dist/internals/motion/presence.js +1 -156
  78. package/dist/internals/motion/raf.d.ts +1 -1
  79. package/dist/internals/motion/stagger.d.ts +3 -4
  80. package/dist/internals/motion/tokens.js +4 -0
  81. package/dist/internals/motion/transitions.d.ts +17 -17
  82. package/dist/internals/motion/types.d.ts +6 -7
  83. package/dist/internals/relative-time.d.ts +1 -1
  84. package/dist/styles.css +3470 -203
  85. package/package.json +18 -2
  86. package/dist/components/fireworks-hdr/webgpu-types.d.ts +0 -159
@@ -2,73 +2,112 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useRef, useState } from "react";
4
4
  import { useIsomorphicLayoutEffect } from "../../internals/dom/ssr.js";
5
+ import { useReducedMotion } from "../../internals/motion/media-query.js";
6
+ import { rafThrottle } from "../../internals/motion/raf.js";
5
7
  import { cn } from "../../utils.js";
8
+ /* empty css */
9
+ function apertureProgress(top, height, viewport) {
10
+ if (viewport <= 0) return 1;
11
+ const centre = top + height / 2;
12
+ const p = (viewport * 0.92 - centre) / (viewport * 0.4);
13
+ return p < 0 ? 0 : p > 1 ? 1 : p;
14
+ }
15
+ function apertureEase(p) {
16
+ const t = p < 0 ? 0 : p > 1 ? 1 : p;
17
+ return t * t * (3 - 2 * t);
18
+ }
6
19
  function ContainerScroll({
7
20
  className = "",
8
21
  titleContent,
9
- cardContent
22
+ cardContent,
23
+ accent,
24
+ accentSecondary
10
25
  }) {
11
- const containerRef = useRef(null);
12
- const [isMobile, setIsMobile] = useState(false);
13
- const [scrollYProgress, setScrollYProgress] = useState(0);
14
- const scaleDimensions = isMobile ? [0.7, 0.9] : [1.05, 1];
15
- const rotate = 20 * (1 - scrollYProgress);
16
- const scale = scaleDimensions[0] + (scaleDimensions[1] - scaleDimensions[0]) * scrollYProgress;
17
- const translateY = -100 * scrollYProgress;
26
+ const reduced = useReducedMotion();
27
+ const trackRef = useRef(null);
28
+ const [raw, setRaw] = useState(0);
29
+ const progress = reduced ? 1 : raw;
30
+ const open = apertureEase(progress);
31
+ const styleVars = {
32
+ "--cs-progress": progress.toFixed(4),
33
+ "--cs-open": open.toFixed(4)
34
+ };
35
+ if (accent) styleVars["--cs-accent"] = accent;
36
+ if (accentSecondary) styleVars["--cs-accent-2"] = accentSecondary;
18
37
  useIsomorphicLayoutEffect(() => {
19
- function updateIsMobile() {
20
- setIsMobile(window.innerWidth <= 768);
21
- }
22
- function updateScroll() {
23
- if (!containerRef.current) return;
24
- const rect = containerRef.current.getBoundingClientRect();
25
- const windowHeight = window.innerHeight;
26
- const progress = 1 - Math.max(0, rect.bottom - window.scrollY) / windowHeight;
27
- setScrollYProgress(Math.max(0, Math.min(1, progress)));
38
+ const track = trackRef.current;
39
+ if (!track || reduced) return;
40
+ function measure() {
41
+ if (!track) return;
42
+ const rect = track.getBoundingClientRect();
43
+ setRaw(apertureProgress(rect.top, rect.height, window.innerHeight));
28
44
  }
29
- updateIsMobile();
30
- updateScroll();
31
- window.addEventListener("resize", updateIsMobile);
32
- window.addEventListener("scroll", updateScroll, { passive: true });
33
- window.addEventListener("resize", updateScroll, { passive: true });
45
+ const update = rafThrottle(measure);
46
+ measure();
47
+ const opts = { capture: true, passive: true };
48
+ window.addEventListener("scroll", update, opts);
49
+ window.addEventListener("resize", update, { passive: true });
34
50
  return () => {
35
- window.removeEventListener("resize", updateIsMobile);
36
- window.removeEventListener("scroll", updateScroll);
37
- window.removeEventListener("resize", updateScroll);
51
+ update.cancel();
52
+ window.removeEventListener("scroll", update, opts);
53
+ window.removeEventListener("resize", update);
38
54
  };
39
- }, []);
55
+ }, [reduced]);
40
56
  return /* @__PURE__ */ jsx(
41
57
  "div",
42
58
  {
43
- ref: containerRef,
44
59
  className: cn(
45
- "relative flex h-[60rem] items-center justify-center p-2 md:h-[80rem] md:p-20",
60
+ "cs-root relative flex h-[48rem] w-full items-start justify-center p-2 md:h-[64rem] md:p-10",
46
61
  className
47
62
  ),
48
- children: /* @__PURE__ */ jsxs("div", { className: "relative w-full py-10 md:py-40", style: { perspective: "1000px" }, children: [
49
- /* @__PURE__ */ jsx(
50
- "div",
51
- {
52
- style: { transform: `translateY(${translateY}px)` },
53
- className: "mx-auto max-w-5xl text-center",
54
- children: titleContent
55
- }
56
- ),
57
- /* @__PURE__ */ jsx(
58
- "div",
59
- {
60
- style: {
61
- transform: `rotateX(${rotate}deg) scale(${scale})`,
62
- boxShadow: "0 0 #0000004d, 0 9px 20px #0000004a, 0 37px 37px #00000042, 0 84px 50px #00000026, 0 149px 60px #0000000a, 0 233px 65px #00000003"
63
- },
64
- className: "mx-auto -mt-12 h-[30rem] w-full max-w-5xl rounded-[30px] border-4 border-[#6C6C6C] bg-[#222222] p-2 shadow-2xl md:h-[40rem] md:p-6",
65
- children: /* @__PURE__ */ jsx("div", { className: "size-full overflow-hidden rounded-2xl bg-gray-100 md:rounded-2xl md:p-4 dark:bg-zinc-900", children: cardContent })
66
- }
67
- )
63
+ style: styleVars,
64
+ "data-reduced-motion": reduced ? "" : void 0,
65
+ children: /* @__PURE__ */ jsxs("div", { className: "relative w-full py-10 md:py-16", children: [
66
+ /* @__PURE__ */ jsx("div", { className: "cs-title mx-auto max-w-5xl text-center", children: titleContent }),
67
+ /* @__PURE__ */ jsx("div", { ref: trackRef, className: "cs-track relative mx-auto mt-10 w-full max-w-5xl md:mt-12", children: /* @__PURE__ */ jsxs("div", { className: "cs-stage relative aspect-[4/3] w-full sm:aspect-[16/10]", children: [
68
+ /* @__PURE__ */ jsx(
69
+ "div",
70
+ {
71
+ className: "cs-shadow pointer-events-none absolute inset-0",
72
+ "aria-hidden": "true"
73
+ }
74
+ ),
75
+ /* @__PURE__ */ jsx("div", { className: "cs-card absolute inset-0 p-1.5 md:p-2", children: /* @__PURE__ */ jsxs("div", { className: "cs-surface relative size-full overflow-hidden", children: [
76
+ /* @__PURE__ */ jsx("div", { className: "cs-content size-full", children: cardContent }),
77
+ /* @__PURE__ */ jsx(
78
+ "div",
79
+ {
80
+ className: "cs-veil pointer-events-none absolute inset-0",
81
+ "aria-hidden": "true"
82
+ }
83
+ )
84
+ ] }) }),
85
+ /* @__PURE__ */ jsx(
86
+ "div",
87
+ {
88
+ className: "cs-lip cs-lip-top pointer-events-none absolute",
89
+ "aria-hidden": "true"
90
+ }
91
+ ),
92
+ /* @__PURE__ */ jsx(
93
+ "div",
94
+ {
95
+ className: "cs-lip cs-lip-bottom pointer-events-none absolute",
96
+ "aria-hidden": "true"
97
+ }
98
+ ),
99
+ /* @__PURE__ */ jsxs("div", { className: "cs-seam pointer-events-none absolute", "aria-hidden": "true", children: [
100
+ /* @__PURE__ */ jsx("span", { className: "cs-seam-haze" }),
101
+ /* @__PURE__ */ jsx("span", { className: "cs-seam-glow" }),
102
+ /* @__PURE__ */ jsx("span", { className: "cs-seam-line" })
103
+ ] })
104
+ ] }) })
68
105
  ] })
69
106
  }
70
107
  );
71
108
  }
72
109
  export {
73
- ContainerScroll
110
+ ContainerScroll,
111
+ apertureEase,
112
+ apertureProgress
74
113
  };
@@ -7,22 +7,25 @@ export interface DockProps {
7
7
  magnification?: number;
8
8
  /** Pointer distance over which the magnification falls off. */
9
9
  distance?: number;
10
- /** Vertical alignment of the icons. */
10
+ /** Cross-axis alignment of the icons. */
11
11
  direction?: Direction;
12
12
  /** Dock orientation. */
13
13
  orientation?: DataOrientation;
14
- /**
15
- * Accessible name for the toolbar. The dock spreads no rest props, so this
16
- * is the only way to name one whose icons carry no label of their own.
17
- */
14
+ /** A soft pool of accent light inside the shelf that follows the pointer. */
15
+ spotlight?: boolean;
16
+ /** A soft contact shadow / glow ellipse under each icon that grows with it. */
17
+ reflection?: boolean;
18
+ /** Accessible name for the toolbar. */
18
19
  ariaLabel?: string;
19
20
  /** The `DockIcon`s and `DockSeparator`s. */
20
21
  children?: ReactNode;
21
22
  }
22
23
  /**
23
- * An icon dock whose items magnify smoothly as the pointer approaches.
24
+ * An icon dock on a lit glass shelf: icons swell on a cosine curve as the
25
+ * pointer approaches, a pool of light follows it across the shelf, and the
26
+ * shelf edge brightens near it.
24
27
  *
25
28
  * No `ref` prop: the source exposes no bindable ref, and no rest props either —
26
29
  * it reads only these props and spreads nothing.
27
30
  */
28
- export declare function Dock({ className, magnification, distance, direction, orientation, ariaLabel, children, }: DockProps): import("react").JSX.Element;
31
+ export declare function Dock({ className, magnification, distance, direction, orientation, spotlight, reflection, ariaLabel, children, }: DockProps): import("react").JSX.Element;
@@ -1,77 +1,122 @@
1
1
  "use client";
2
- import { jsx } from "react/jsx-runtime";
3
- import { useState, useRef, useEffect, useCallback, useMemo } from "react";
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 [pointer, setPointer] = useState({
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 frames = useRef(/* @__PURE__ */ new Set());
24
- useEffect(() => {
25
- const pending = frames.current;
26
- return () => {
27
- for (const id of pending) cancelAnimationFrame(id);
28
- pending.clear();
29
- };
30
- }, []);
31
- const schedule = useCallback((x, y) => {
32
- const id = requestAnimationFrame(() => {
33
- frames.current.delete(id);
34
- setPointer({ x, y });
35
- });
36
- frames.current.add(id);
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: pointer.x },
41
- mouseY: { current: pointer.y },
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
- [pointer, magnification, distance, orientation, magnify]
72
+ [tracked.x, tracked.y, magnification, distance, orientation, magnify, reflection]
48
73
  );
49
74
  function onPointerMove(e) {
50
- if (!magnify) return;
75
+ if (!track) return;
51
76
  if (e.pointerType === "touch" || !e.isPrimary) return;
52
- const { clientX, clientY } = e;
53
- schedule(clientX, clientY);
77
+ queue(e.clientX, e.clientY);
54
78
  }
55
79
  function onPointerLeave() {
56
- schedule(Infinity, Infinity);
80
+ queue(Infinity, Infinity);
57
81
  }
58
82
  const directionClass = direction === "top" ? "items-start" : direction === "bottom" ? "items-end" : "items-center";
59
- return /* @__PURE__ */ jsx(DOCK_CONTEXT_KEY.Provider, { value: context, children: /* @__PURE__ */ jsx(
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-4 rounded-2xl border p-2 backdrop-blur-md transition-all supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10",
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
- * width is computed while rendering, so the component has to re-render once the
13
- * element actually exists. Before it does, the distance is `Infinity` and 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 calculateDistance() {
9
- if (!node) return Infinity;
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
- if (context.orientation === "vertical") {
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 iconWidth = computeIconWidth();
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 transition-all duration-200 ease-out ${className}`,
31
- style: { width: `${iconWidth}px`, height: `${iconWidth}px` },
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
- DockIcon
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
- /** A rule between two groups of dock icons, laid across the dock's own axis. */
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
- "bg-secondary relative block",
12
- context.orientation === "vertical" ? "h-0.5 w-4/5" : "h-4/5 w-0.5",
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 page X, in a box rather than as a bare number — the shape
6
- * the source publishes, kept verbatim so the exported type is unchanged.
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 page Y. Same box treatment as `mouseX`. */
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 doing any measuring, so a device that will
27
- * never magnify also never pays for a `getBoundingClientRect()` per icon per
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
- * menu-focus core, and Tab to `options.onTab`. Identical semantics whether
9
- * the panel is a `DropdownMenuContent` or its nested `*SubContent` — the keys
10
- * navigate whatever level currently holds real DOM focus, regardless of how
11
- * that level's panel was opened or positioned.
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
- * Takes the native `KeyboardEvent` shape React's synthetic event satisfies
19
- * structurally, so a caller passes `event` straight through with no unwrap.
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, so a component builds one through `useConstant`: it
34
- * installs no listener and starts no timer.
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
- for (const element of [
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 {