@almadar/ui 6.59.0 → 6.61.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 (90) hide show
  1. package/dist/BehaviorValueFrame-3ORZHPWQ.css +1252 -0
  2. package/dist/BehaviorValueFrame-QTD7BWOI.js +18 -0
  3. package/dist/TraitValueFrame-2WMYJOA5.css +1252 -0
  4. package/dist/TraitValueFrame-WOXNLLMM.js +17 -0
  5. package/dist/Typography-BY4A2NBY.js +5 -0
  6. package/dist/{UISlotRenderer-YNY7V2UJ.js → UISlotRenderer-EEOXSJCI.js} +10 -8
  7. package/dist/avl/index.cjs +31974 -27856
  8. package/dist/avl/index.d.ts +36 -13
  9. package/dist/avl/index.js +296 -6128
  10. package/dist/{avl-3d-context-BpBKZIbc.d.ts → avl-3d-context-Dxun3-ri.d.ts} +2 -0
  11. package/dist/{chunk-E6MWYQVZ.js → chunk-6PV4LASE.js} +9493 -641
  12. package/dist/{chunk-723MJUU3.js → chunk-6ZCU3BQN.js} +1 -1
  13. package/dist/{chunk-7CVXM2KS.js → chunk-7NIU5GDC.js} +2 -2
  14. package/dist/{chunk-YYYHH5BA.js → chunk-CB64IOYD.js} +0 -4
  15. package/dist/{chunk-MZIDOJMD.js → chunk-EIIQGOZW.js} +2 -1
  16. package/dist/chunk-F7NZ3KZQ.js +434 -0
  17. package/dist/{chunk-LZRW6GXG.js → chunk-FYX64CDL.js} +0 -2
  18. package/dist/{chunk-4ZGXIW3B.js → chunk-KZRAMPM6.js} +1 -1
  19. package/dist/chunk-LUREMDI3.js +49 -0
  20. package/dist/{chunk-J755QADM.js → chunk-MGN6B6OQ.js} +318 -18
  21. package/dist/{chunk-Q47ZN2DW.js → chunk-N23SVGTH.js} +83 -1
  22. package/dist/{chunk-KJGVLXEN.js → chunk-N4IJS5DD.js} +50 -116
  23. package/dist/{chunk-I5QOYW2Z.js → chunk-UTSDPJUP.js} +205 -2
  24. package/dist/{chunk-V3FGTQUE.js → chunk-ZR45FGW3.js} +7 -1
  25. package/dist/{cn-DIql5232.d.ts → cn-CDIhHCUC.d.ts} +40 -16
  26. package/dist/components/index.cjs +14423 -4100
  27. package/dist/components/index.d.ts +160 -172
  28. package/dist/components/index.js +14 -15
  29. package/dist/context/index.cjs +9 -1
  30. package/dist/context/index.js +3 -1
  31. package/dist/hooks/index.cjs +72 -6
  32. package/dist/hooks/index.d.ts +43 -2
  33. package/dist/hooks/index.js +6 -7
  34. package/dist/lib/drawable/three/index.cjs +350 -26
  35. package/dist/lib/drawable/three/index.d.ts +2 -2
  36. package/dist/lib/drawable/three/index.js +28 -7
  37. package/dist/lib/index.cjs +90 -4
  38. package/dist/lib/index.d.ts +2 -2
  39. package/dist/lib/index.js +74 -6
  40. package/dist/locales/index.cjs +0 -6
  41. package/dist/locales/index.js +2 -2
  42. package/dist/providers/index.cjs +13846 -1062
  43. package/dist/providers/index.d.ts +36 -4
  44. package/dist/providers/index.js +35 -15
  45. package/dist/runtime/index.cjs +28395 -18308
  46. package/dist/runtime/index.d.ts +65 -3
  47. package/dist/runtime/index.js +13 -379
  48. package/dist/ssr/index.cjs +506 -133
  49. package/dist/ssr/index.d.cts +56 -4
  50. package/dist/ssr/index.d.ts +56 -4
  51. package/dist/ssr/index.js +444 -71
  52. package/dist/{verificationRegistry-B3dj1Zl8.d.ts → verificationRegistry-B5aGl7WF.d.ts} +2 -1
  53. package/locales/ar.json +0 -2
  54. package/locales/en.json +0 -2
  55. package/locales/sl.json +0 -2
  56. package/package.json +7 -7
  57. package/tailwind-preset.cjs +78 -8
  58. package/themes/_contract.md +6 -0
  59. package/themes/_fonts.css +4 -4
  60. package/themes/_foundation.css +11 -0
  61. package/themes/_presets.css +2 -0
  62. package/themes/almadar-website.css +10 -0
  63. package/themes/almadar.css +10 -0
  64. package/themes/atelier.css +10 -0
  65. package/themes/bauhaus.css +10 -0
  66. package/themes/bloomberg-dense.css +10 -0
  67. package/themes/clay.css +10 -0
  68. package/themes/corporate.css +10 -0
  69. package/themes/gazette.css +10 -0
  70. package/themes/glass.css +10 -0
  71. package/themes/kflow.css +5 -0
  72. package/themes/kiosk.css +10 -0
  73. package/themes/linear-clean.css +10 -0
  74. package/themes/material.css +10 -0
  75. package/themes/minimalist.css +10 -0
  76. package/themes/nata.css +304 -0
  77. package/themes/neon.css +10 -0
  78. package/themes/notion-editorial.css +10 -0
  79. package/themes/orb.css +10 -0
  80. package/themes/prism.css +10 -0
  81. package/themes/rawi.css +304 -0
  82. package/themes/retro.css +10 -0
  83. package/themes/risograph.css +10 -0
  84. package/themes/scandi.css +10 -0
  85. package/themes/solarpunk.css +10 -0
  86. package/themes/terminal.css +138 -128
  87. package/dist/Typography-LZTP72Y4.js +0 -5
  88. package/dist/chunk-D2E44ODB.js +0 -350
  89. package/dist/chunk-N7JIG57B.js +0 -138
  90. package/dist/chunk-QJIAJ6EH.js +0 -2256
package/dist/ssr/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import * as React17 from 'react';
2
- import React17__default, { createContext, useCallback, useState, useRef, useEffect, useContext, useMemo, useSyncExternalStore, useLayoutEffect, useId } from 'react';
1
+ import * as React18 from 'react';
2
+ import React18__default, { createContext, useRef, useCallback, useState, useEffect, useContext, useMemo, useSyncExternalStore, useLayoutEffect, useId } from 'react';
3
3
  import { createLogger } from '@almadar/logger';
4
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { LOADING_STATE_MARKER, isSystemUiEvent } from '@almadar/core';
4
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
5
+ import { actionTestId, LOADING_STATE_MARKER, isSystemUiEvent } from '@almadar/core';
6
6
  import * as LucideIcons2 from 'lucide-react';
7
7
  import { Loader2, X } from 'lucide-react';
8
8
  import 'react-router-dom';
@@ -2555,6 +2555,44 @@ var twMerge = extendTailwindMerge({
2555
2555
  function cn(...inputs) {
2556
2556
  return twMerge(clsx(inputs));
2557
2557
  }
2558
+
2559
+ // lib/bandShapes.ts
2560
+ var shape = (d) => `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 100' preserveAspectRatio='none'><path d='${d}'/></svg>")`;
2561
+ var BAND_SHAPES = {
2562
+ wave: shape("M0 60 C240 100 480 100 720 60 C960 20 1200 20 1440 60 V100 H0 Z"),
2563
+ arc: shape("M0 100 Q720 0 1440 100 Z"),
2564
+ tilt: shape("M0 100 L1440 0 V100 Z"),
2565
+ step: shape("M0 100 V75 H360 V50 H720 V25 H1080 V0 H1440 V100 Z"),
2566
+ scallop: shape("M0 100 V40 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 V100 Z")
2567
+ };
2568
+ var BAND_EDGE_HEIGHT = "clamp(24px, 6vw, 96px)";
2569
+ var BAND_ACCENT_FALLBACK = "linear-gradient(180deg, color-mix(in oklab, var(--color-primary) 12%, var(--color-background)), var(--color-background))";
2570
+ var BAND_EDGE_FILL = {
2571
+ background: "var(--surface-page-image, none), var(--color-background)",
2572
+ muted: "var(--color-muted)",
2573
+ surface: "var(--color-card)",
2574
+ gradient: `var(--surface-accent-image, ${BAND_ACCENT_FALLBACK}), var(--color-muted)`,
2575
+ inverse: "var(--color-foreground)"
2576
+ };
2577
+ function bandEdgeMask(edge) {
2578
+ return edge === "curve" ? "var(--surface-edge-mask, none)" : BAND_SHAPES[edge];
2579
+ }
2580
+ function bandEdgeHeight(edge) {
2581
+ return edge === "curve" ? `var(--surface-edge-height, ${BAND_EDGE_HEIGHT})` : BAND_EDGE_HEIGHT;
2582
+ }
2583
+ function bandEdgeLayerStyle(edge, side, flip, color) {
2584
+ const mask = bandEdgeMask(edge);
2585
+ const turn = side === "top" ? flip ? "scaleY(-1)" : "rotate(180deg)" : flip ? "scaleX(-1)" : void 0;
2586
+ const style = { height: bandEdgeHeight(edge), background: BAND_EDGE_FILL[color], maskImage: mask, WebkitMaskImage: mask, transform: turn };
2587
+ if (color === "background") {
2588
+ style.backgroundSize = "var(--surface-page-image-size, auto), auto";
2589
+ style.backgroundAttachment = "fixed";
2590
+ }
2591
+ return style;
2592
+ }
2593
+ function bandPadding(basePad, edge) {
2594
+ return edge === "none" ? void 0 : `calc(${basePad} + ${bandEdgeHeight(edge)})`;
2595
+ }
2558
2596
  createLogger("almadar:eventbus");
2559
2597
  createLogger("almadar:eventbus:subscribe");
2560
2598
  var EventBusContext = globalThis.__almadarUiEventBusContext ?? (globalThis.__almadarUiEventBusContext = createContext(null));
@@ -2759,7 +2797,237 @@ function pressableProps(onPress, options = {}) {
2759
2797
  };
2760
2798
  }
2761
2799
 
2762
- // components/core/atoms/Box.tsx
2800
+ // lib/domPassthrough.ts
2801
+ var GLOBAL_ATTRIBUTES = /* @__PURE__ */ new Set(["role", "id", "lang", "dir", "tabIndex"]);
2802
+ function domPassthrough(props) {
2803
+ const out = {};
2804
+ for (const [key, value] of Object.entries(props)) {
2805
+ if (key.startsWith("aria-") || key.startsWith("data-") || GLOBAL_ATTRIBUTES.has(key)) {
2806
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === void 0) {
2807
+ out[key] = value;
2808
+ }
2809
+ }
2810
+ }
2811
+ return out;
2812
+ }
2813
+ var motionEnabledCache = null;
2814
+ function isMotionEnabled() {
2815
+ if (typeof document === "undefined") return true;
2816
+ if (motionEnabledCache !== null) return motionEnabledCache;
2817
+ const v = getComputedStyle(document.documentElement).getPropertyValue("--motion-enable").trim().toLowerCase();
2818
+ motionEnabledCache = v !== "off";
2819
+ return motionEnabledCache;
2820
+ }
2821
+
2822
+ // lib/motion-tokens.ts
2823
+ function cssTimeMs(value) {
2824
+ const v = value.trim();
2825
+ if (v.endsWith("ms")) return Number.parseFloat(v);
2826
+ if (v.endsWith("s")) return Number.parseFloat(v) * 1e3;
2827
+ return Number.NaN;
2828
+ }
2829
+ function tokenAt(el, name) {
2830
+ for (let at = el; at !== null; at = at.parentElement) {
2831
+ const value = getComputedStyle(at).getPropertyValue(name).trim();
2832
+ if (value !== "") return value;
2833
+ }
2834
+ return "";
2835
+ }
2836
+ function motionTiming(durationToken, easingToken, scope) {
2837
+ const duration = cssTimeMs(tokenAt(scope ?? document.documentElement, durationToken));
2838
+ const easing = tokenAt(scope ?? document.documentElement, easingToken);
2839
+ const reduced = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2840
+ if (!isMotionEnabled() || reduced || !Number.isFinite(duration) || easing === "") return void 0;
2841
+ return { duration, easing };
2842
+ }
2843
+ function layoutScale(el) {
2844
+ const layoutWidth = el instanceof HTMLElement ? el.offsetWidth : 0;
2845
+ const screenWidth = el.getBoundingClientRect().width;
2846
+ return layoutWidth > 0 && screenWidth > 0 ? screenWidth / layoutWidth : 1;
2847
+ }
2848
+
2849
+ // lib/animate-changes.ts
2850
+ function ownMotion(el) {
2851
+ return el.closest("[data-item-move]") !== null || /\balmadar-enter-/.test(el.getAttribute("class") ?? "");
2852
+ }
2853
+ function boxesOf(el) {
2854
+ return getComputedStyle(el).display === "contents" ? boxedChildren(el) : [el];
2855
+ }
2856
+ function boxedChildren(el) {
2857
+ return [...el.children].flatMap(boxesOf);
2858
+ }
2859
+ var GHOST_STYLE = ["font", "letterSpacing", "lineHeight", "color", "textAlign", "whiteSpace", "textTransform"];
2860
+ function roll(el, oldText, timing) {
2861
+ const box = el.getBoundingClientRect();
2862
+ const scale2 = layoutScale(el);
2863
+ const computed = getComputedStyle(el);
2864
+ const holder = document.createElement("span");
2865
+ holder.setAttribute("aria-hidden", "true");
2866
+ Object.assign(holder.style, {
2867
+ position: "fixed",
2868
+ left: `${box.left}px`,
2869
+ top: `${box.top}px`,
2870
+ width: `${box.width / scale2}px`,
2871
+ height: `${box.height / scale2}px`,
2872
+ transform: scale2 === 1 ? "" : `scale(${scale2})`,
2873
+ transformOrigin: "top left",
2874
+ pointerEvents: "none",
2875
+ zIndex: "2147483647"
2876
+ });
2877
+ const ghost = document.createElement("span");
2878
+ ghost.setAttribute("data-change-ghost", "");
2879
+ ghost.setAttribute("aria-hidden", "true");
2880
+ ghost.textContent = oldText;
2881
+ for (const prop of GHOST_STYLE) ghost.style[prop] = computed[prop];
2882
+ Object.assign(ghost.style, { display: "block", width: "100%", height: "100%", overflow: "visible" });
2883
+ holder.appendChild(ghost);
2884
+ document.body.appendChild(holder);
2885
+ const out = ghost.animate(
2886
+ [{ opacity: 1, transform: "none" }, { opacity: 0, transform: "translateY(-40%)", offset: 0.5 }, { opacity: 0, transform: "translateY(-40%)" }],
2887
+ timing
2888
+ );
2889
+ if (out === void 0) holder.remove();
2890
+ else {
2891
+ out.addEventListener("finish", () => holder.remove());
2892
+ out.addEventListener("cancel", () => holder.remove());
2893
+ }
2894
+ el.animate(
2895
+ [{ opacity: 0, transform: "translateY(40%)" }, { opacity: 0, transform: "translateY(40%)", offset: 0.4 }, { opacity: 1, transform: "none" }],
2896
+ timing
2897
+ );
2898
+ }
2899
+ var textOnly = (el) => el instanceof HTMLElement && el.childNodes.length > 0 && [...el.childNodes].every((n) => n.nodeType === Node.TEXT_NODE);
2900
+ function settle(root, added, changed) {
2901
+ const timing = motionTiming("--duration-normal", "--easing-emphasized", root);
2902
+ if (timing === void 0) return;
2903
+ for (const el of added) {
2904
+ if (ownMotion(el) || el.parentElement !== null && [...added].some((other) => other !== el && other.contains(el))) continue;
2905
+ for (const box of boxesOf(el)) {
2906
+ if (ownMotion(box)) continue;
2907
+ box.animate([{ opacity: 0, transform: "translateY(6px)" }, { opacity: 1, transform: "none" }], timing);
2908
+ }
2909
+ }
2910
+ for (const [el, oldText] of changed) {
2911
+ if (ownMotion(el) || [...added].some((a) => a.contains(el))) continue;
2912
+ if (oldText !== void 0 && oldText !== el.textContent && textOnly(el)) roll(el, oldText, timing);
2913
+ else el.animate([{ opacity: 0.25 }, { opacity: 1 }], timing);
2914
+ }
2915
+ }
2916
+ function blocks(root) {
2917
+ const seen = /* @__PURE__ */ new Set();
2918
+ for (const wrapper of root.querySelectorAll("[data-pattern]")) {
2919
+ for (const box of boxesOf(wrapper)) if (!ownMotion(box)) seen.add(box);
2920
+ }
2921
+ return [...seen];
2922
+ }
2923
+ function glidePushed(root, places) {
2924
+ const timing = motionTiming("--duration-normal", "--easing-emphasized", root);
2925
+ const origin = root.getBoundingClientRect();
2926
+ const applied = /* @__PURE__ */ new Map();
2927
+ const present = /* @__PURE__ */ new Set();
2928
+ for (const el of blocks(root)) {
2929
+ present.add(el);
2930
+ if ((el.getAnimations?.() ?? []).length > 0) {
2931
+ places.delete(el);
2932
+ continue;
2933
+ }
2934
+ const box = el.getBoundingClientRect();
2935
+ const now = { x: box.left - origin.left, y: box.top - origin.top };
2936
+ const was = places.get(el);
2937
+ places.set(el, now);
2938
+ if (was === void 0 || timing === void 0) continue;
2939
+ let inherited = { x: 0, y: 0 };
2940
+ for (let up = el.parentElement; up !== null && up !== root; up = up.parentElement) {
2941
+ const a = applied.get(up);
2942
+ if (a !== void 0) {
2943
+ inherited = a;
2944
+ break;
2945
+ }
2946
+ }
2947
+ const dx = was.x - now.x - inherited.x;
2948
+ const dy = was.y - now.y - inherited.y;
2949
+ if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) {
2950
+ if (inherited.x !== 0 || inherited.y !== 0) applied.set(el, inherited);
2951
+ continue;
2952
+ }
2953
+ const scale2 = layoutScale(el);
2954
+ el.animate([{ transform: `translate(${dx / scale2}px, ${dy / scale2}px)` }, { transform: "none" }], { ...timing, composite: "add" });
2955
+ applied.set(el, { x: inherited.x + dx, y: inherited.y + dy });
2956
+ }
2957
+ for (const el of places.keys()) if (!present.has(el)) places.delete(el);
2958
+ }
2959
+ function easeHeight(root) {
2960
+ if (typeof ResizeObserver === "undefined") return () => void 0;
2961
+ let rest;
2962
+ let glide;
2963
+ const sizes = new ResizeObserver(() => {
2964
+ const scale2 = layoutScale(root);
2965
+ const shown = root.getBoundingClientRect().height / scale2;
2966
+ const gliding = glide !== void 0 && glide.playState === "running";
2967
+ glide?.cancel();
2968
+ glide = void 0;
2969
+ const height = root.getBoundingClientRect().height / scale2;
2970
+ const from = gliding ? shown : rest;
2971
+ rest = height;
2972
+ const timing = motionTiming("--duration-normal", "--easing-emphasized", root);
2973
+ if (from === void 0 || timing === void 0 || Math.abs(from - height) < 1) return;
2974
+ glide = root.animate(
2975
+ [{ height: `${from}px`, overflow: "clip" }, { height: `${height}px`, overflow: "clip" }],
2976
+ timing
2977
+ ) ?? void 0;
2978
+ });
2979
+ let watched = [];
2980
+ const watch = () => {
2981
+ const next = boxedChildren(root);
2982
+ if (next.length === watched.length && next.every((el, i) => el === watched[i])) return;
2983
+ watched = next;
2984
+ sizes.disconnect();
2985
+ for (const el of next) sizes.observe(el);
2986
+ };
2987
+ watch();
2988
+ const children = new MutationObserver(watch);
2989
+ children.observe(root, { childList: true, subtree: true });
2990
+ return () => {
2991
+ children.disconnect();
2992
+ sizes.disconnect();
2993
+ };
2994
+ }
2995
+ function useAnimateChanges(ref, enabled) {
2996
+ useEffect(() => {
2997
+ const root = ref.current;
2998
+ if (!enabled || root === null) return;
2999
+ return easeHeight(root);
3000
+ }, [ref, enabled]);
3001
+ useEffect(() => {
3002
+ const root = ref.current;
3003
+ if (!enabled || root === null || typeof MutationObserver === "undefined") return;
3004
+ const observer2 = new MutationObserver((records) => {
3005
+ const added = /* @__PURE__ */ new Set();
3006
+ const changed = /* @__PURE__ */ new Map();
3007
+ for (const record of records) {
3008
+ if (record.type === "childList") {
3009
+ for (const node of record.addedNodes) {
3010
+ if (node instanceof Element) added.add(node);
3011
+ else if (node.nodeType === Node.TEXT_NODE && node.parentElement !== null && changed.get(node.parentElement) === void 0) {
3012
+ const removed = [...record.removedNodes].filter((n) => n.nodeType === Node.TEXT_NODE);
3013
+ changed.set(node.parentElement, removed.length > 0 ? removed.map((n) => n.textContent ?? "").join("") : void 0);
3014
+ }
3015
+ }
3016
+ } else if (record.type === "characterData") {
3017
+ const el = record.target.parentElement;
3018
+ if (el === null || changed.get(el) !== void 0) continue;
3019
+ changed.set(el, [...el.childNodes].map((n) => n === record.target ? record.oldValue ?? "" : n.textContent ?? "").join(""));
3020
+ }
3021
+ }
3022
+ glidePushed(root, places);
3023
+ settle(root, added, changed);
3024
+ });
3025
+ const places = /* @__PURE__ */ new Map();
3026
+ glidePushed(root, places);
3027
+ observer2.observe(root, { subtree: true, childList: true, characterData: true, characterDataOldValue: true });
3028
+ return () => observer2.disconnect();
3029
+ }, [ref, enabled]);
3030
+ }
2763
3031
  var paddingStyles = {
2764
3032
  none: "p-0",
2765
3033
  xs: "p-1",
@@ -2824,7 +3092,18 @@ var bgStyles = {
2824
3092
  muted: "bg-muted text-foreground",
2825
3093
  accent: "bg-accent text-accent-foreground",
2826
3094
  surface: "bg-card",
2827
- overlay: "bg-card/80 backdrop-blur-sm"
3095
+ overlay: "bg-card/80 backdrop-blur-sm",
3096
+ gradient: "band-gradient",
3097
+ inverse: "band-inverse"
3098
+ };
3099
+ var paddingRem = {
3100
+ none: "0rem",
3101
+ xs: "0.25rem",
3102
+ sm: "0.5rem",
3103
+ md: "1rem",
3104
+ lg: "1.5rem",
3105
+ xl: "2rem",
3106
+ "2xl": "3rem"
2828
3107
  };
2829
3108
  var roundedStyles = {
2830
3109
  none: "rounded-none",
@@ -2862,7 +3141,7 @@ var positionStyles = {
2862
3141
  fixed: "fixed",
2863
3142
  sticky: "sticky"
2864
3143
  };
2865
- var Box = React17__default.forwardRef(
3144
+ var Box = React18__default.forwardRef(
2866
3145
  ({
2867
3146
  padding,
2868
3147
  paddingX,
@@ -2887,6 +3166,13 @@ var Box = React17__default.forwardRef(
2887
3166
  hoverEvent,
2888
3167
  tapReveal = true,
2889
3168
  maxWidth,
3169
+ edgeTop = "none",
3170
+ edgeBottom = "none",
3171
+ edgeColor = "background",
3172
+ edgeFlip = false,
3173
+ texture = false,
3174
+ animateChanges = false,
3175
+ backdrop,
2890
3176
  onClick,
2891
3177
  onMouseEnter,
2892
3178
  onMouseLeave,
@@ -2894,6 +3180,13 @@ var Box = React17__default.forwardRef(
2894
3180
  ...rest
2895
3181
  }, ref) => {
2896
3182
  const eventBus = useEventBus();
3183
+ const ownRef = useRef(null);
3184
+ useAnimateChanges(ownRef, animateChanges);
3185
+ const setRef = useCallback((node) => {
3186
+ ownRef.current = node;
3187
+ if (typeof ref === "function") ref(node);
3188
+ else if (ref !== null) ref.current = node;
3189
+ }, [ref]);
2897
3190
  const { override: themeOverride } = useThemeScope();
2898
3191
  const declaredTheme = rest["data-theme"];
2899
3192
  const dataTheme = declaredTheme !== void 0 && themeOverride ? themeOverride : declaredTheme;
@@ -2929,12 +3222,25 @@ var Box = React17__default.forwardRef(
2929
3222
  if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
2930
3223
  onPointerDown?.(e);
2931
3224
  }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
3225
+ const isBand = backdrop != null || texture || edgeTop !== "none" || edgeBottom !== "none";
3226
+ const padKey = paddingY ?? padding;
3227
+ const bandStyle = {};
3228
+ const padTop = padKey ? bandPadding(paddingRem[padKey], edgeTop) : void 0;
3229
+ const padBottom = padKey ? bandPadding(paddingRem[padKey], edgeBottom) : void 0;
3230
+ if (padTop) bandStyle.paddingTop = padTop;
3231
+ if (padBottom) bandStyle.paddingBottom = padBottom;
3232
+ const bandLayers = isBand ? /* @__PURE__ */ jsxs(Fragment, { children: [
3233
+ backdrop != null && /* @__PURE__ */ jsx(Box, { as: "span", "data-band-layer": "", "aria-hidden": true, className: "band-backdrop", children: backdrop }),
3234
+ texture && /* @__PURE__ */ jsx(Box, { as: "span", "data-band-layer": "", "aria-hidden": true, className: "band-texture" }),
3235
+ edgeTop !== "none" && /* @__PURE__ */ jsx(Box, { as: "span", "data-band-layer": "", "aria-hidden": true, className: "band-edge band-edge-top", style: bandEdgeLayerStyle(edgeTop, "top", edgeFlip, edgeColor) }),
3236
+ edgeBottom !== "none" && /* @__PURE__ */ jsx(Box, { as: "span", "data-band-layer": "", "aria-hidden": true, className: "band-edge band-edge-bottom", style: bandEdgeLayerStyle(edgeBottom, "bottom", edgeFlip, edgeColor) })
3237
+ ] }) : null;
2932
3238
  const isClickable = action || onClick;
2933
3239
  const actionControl = action && rest.role === void 0 && rest.tabIndex === void 0 ? pressableProps(handleClick) : {};
2934
- return React17__default.createElement(
3240
+ return React18__default.createElement(
2935
3241
  Component,
2936
3242
  {
2937
- ref,
3243
+ ref: setRef,
2938
3244
  className: cn(
2939
3245
  padding && paddingStyles[padding],
2940
3246
  paddingX && paddingXStyles[paddingX],
@@ -2952,6 +3258,7 @@ var Box = React17__default.forwardRef(
2952
3258
  overflow && overflowStyles[overflow],
2953
3259
  position && positionStyles[position],
2954
3260
  isClickable && "cursor-pointer",
3261
+ isBand && "band",
2955
3262
  className
2956
3263
  ),
2957
3264
  onClick: isClickable ? handleClick : void 0,
@@ -2959,29 +3266,19 @@ var Box = React17__default.forwardRef(
2959
3266
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
2960
3267
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
2961
3268
  onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
2962
- style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
3269
+ style: maxWidth || isBand ? { ...maxWidth ? { maxWidth } : {}, ...bandStyle, ...rest.style } : rest.style,
2963
3270
  ...rest,
2964
- "data-theme": dataTheme
3271
+ "data-theme": dataTheme,
3272
+ "data-motion": animateChanges ? "presentation" : void 0
2965
3273
  },
2966
- children
3274
+ isBand ? /* @__PURE__ */ jsxs(Fragment, { children: [
3275
+ bandLayers,
3276
+ children
3277
+ ] }) : children
2967
3278
  );
2968
3279
  }
2969
3280
  );
2970
3281
  Box.displayName = "Box";
2971
-
2972
- // lib/domPassthrough.ts
2973
- var GLOBAL_ATTRIBUTES = /* @__PURE__ */ new Set(["role", "id", "lang", "dir", "tabIndex"]);
2974
- function domPassthrough(props) {
2975
- const out = {};
2976
- for (const [key, value] of Object.entries(props)) {
2977
- if (key.startsWith("aria-") || key.startsWith("data-") || GLOBAL_ATTRIBUTES.has(key)) {
2978
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === void 0) {
2979
- out[key] = value;
2980
- }
2981
- }
2982
- }
2983
- return out;
2984
- }
2985
3282
  var gapStyles = {
2986
3283
  none: "gap-0",
2987
3284
  xs: "gap-1",
@@ -3036,7 +3333,7 @@ var Stack = ({
3036
3333
  };
3037
3334
  const isHorizontal = direction === "horizontal";
3038
3335
  const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
3039
- return React17__default.createElement(
3336
+ return React18__default.createElement(
3040
3337
  Component,
3041
3338
  {
3042
3339
  ...domPassthrough(rest),
@@ -3198,7 +3495,6 @@ var en_default = {
3198
3495
  "aria.upvote": "Upvote",
3199
3496
  "aria.downvote": "Downvote",
3200
3497
  "aria.qrScanner": "QR scanner",
3201
- "aria.mockScanDev": "Mock scan (dev)",
3202
3498
  "aria.openMenu": "Open menu",
3203
3499
  "aria.closeMenu": "Close menu",
3204
3500
  "aria.openSidebar": "Open sidebar",
@@ -3396,7 +3692,6 @@ var en_default = {
3396
3692
  "qrScanner.pauseScanning": "Pause scanning",
3397
3693
  "qrScanner.switchToFrontCamera": "Switch to front camera",
3398
3694
  "qrScanner.switchToRearCamera": "Switch to rear camera",
3399
- "qrScanner.mockScan": "Mock Scan",
3400
3695
  "docSearch.placeholder": "Search documentation...",
3401
3696
  "stateMachine.loading": "Loading state machine\u2026",
3402
3697
  "stateMachine.noStateMachine": "No state machine to visualize",
@@ -4509,7 +4804,7 @@ var Typography = ({
4509
4804
  if (format !== void 0 && format !== "none" && (typeof body === "string" || typeof body === "number" || body instanceof Date)) {
4510
4805
  body = formatValue(body, format, fmt);
4511
4806
  }
4512
- return React17__default.createElement(
4807
+ return React18__default.createElement(
4513
4808
  Component,
4514
4809
  {
4515
4810
  ...domPassthrough(rest),
@@ -4721,7 +5016,7 @@ function resolveLucide(name) {
4721
5016
  }
4722
5017
  function makeLucideAdapter(name) {
4723
5018
  const LucideComp = resolveLucide(name);
4724
- const Adapter = React17__default.forwardRef((props, ref) => /* @__PURE__ */ jsx(
5019
+ const Adapter = React18__default.forwardRef((props, ref) => /* @__PURE__ */ jsx(
4725
5020
  LucideComp,
4726
5021
  {
4727
5022
  ref,
@@ -4809,9 +5104,9 @@ var Icon = ({
4809
5104
  const effectiveName = typeof icon === "string" && icon !== "" ? icon : name;
4810
5105
  const effectiveStrokeWidth = strokeWidth != null && strokeWidth > 0 ? strokeWidth : void 0;
4811
5106
  const family = useIconFamily();
4812
- const svgRef = React17__default.useRef(null);
5107
+ const svgRef = React18__default.useRef(null);
4813
5108
  const themeStroke = useThemeIconStrokeApplies(svgRef);
4814
- const RenderedComponent = React17__default.useMemo(() => {
5109
+ const RenderedComponent = React18__default.useMemo(() => {
4815
5110
  if (directIcon) return null;
4816
5111
  return effectiveName ? resolveIconForFamily(effectiveName) : null;
4817
5112
  }, [directIcon, effectiveName, family]);
@@ -4953,13 +5248,13 @@ function AtlasImage({
4953
5248
  "aria-hidden": ariaHidden,
4954
5249
  ...rest
4955
5250
  }) {
4956
- const [, bump] = React17.useReducer((x) => x + 1, 0);
4957
- const canvasRef = React17.useRef(null);
5251
+ const [, bump] = React18.useReducer((x) => x + 1, 0);
5252
+ const canvasRef = React18.useRef(null);
4958
5253
  const sliced = isAtlasAsset(asset);
4959
5254
  const atlas = sliced ? getAtlas(asset.atlas, bump) : void 0;
4960
5255
  const img = sliced && asset?.url ? getSheetImage(asset.url, bump) : null;
4961
5256
  const rect = sliced && atlas ? subRectFor(atlas, asset.sprite) : null;
4962
- React17.useEffect(() => {
5257
+ React18.useEffect(() => {
4963
5258
  const canvas = canvasRef.current;
4964
5259
  if (!canvas || !img || !rect) return;
4965
5260
  canvas.width = rect.sw;
@@ -5125,7 +5420,7 @@ function resolveIconProp(value, sizeClass) {
5125
5420
  const IconComp = value;
5126
5421
  return /* @__PURE__ */ jsx(IconComp, { className: sizeClass });
5127
5422
  }
5128
- if (React17__default.isValidElement(value)) {
5423
+ if (React18__default.isValidElement(value)) {
5129
5424
  return value;
5130
5425
  }
5131
5426
  if (typeof value === "object" && value !== null && isIconLike(value)) {
@@ -5134,7 +5429,7 @@ function resolveIconProp(value, sizeClass) {
5134
5429
  }
5135
5430
  return value;
5136
5431
  }
5137
- var Button = React17__default.forwardRef(
5432
+ var Button = React18__default.forwardRef(
5138
5433
  ({
5139
5434
  className,
5140
5435
  variant = "primary",
@@ -5161,7 +5456,7 @@ var Button = React17__default.forwardRef(
5161
5456
  const navStack = useNavStack();
5162
5457
  const { pending, activate } = usePendingAction();
5163
5458
  const busy = isLoading || pending;
5164
- const [focused, setFocused] = React17__default.useState(false);
5459
+ const [focused, setFocused] = React18__default.useState(false);
5165
5460
  const inert = Boolean(disabled) || busy;
5166
5461
  const emitAction = () => {
5167
5462
  if (!action) return;
@@ -5203,7 +5498,7 @@ var Button = React17__default.forwardRef(
5203
5498
  disabled && focused && "opacity-50 cursor-not-allowed",
5204
5499
  className
5205
5500
  );
5206
- const testId = dataTestId ?? (action ? `action-${action}` : void 0);
5501
+ const testId = dataTestId ?? (action ? actionTestId(action) : void 0);
5207
5502
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
5208
5503
  busy && /* @__PURE__ */ jsx("span", { className: "almadar-busy-indicator absolute inset-0 flex items-center justify-center", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Loader2, { className: "h-icon-default w-icon-default animate-spin" }) }),
5209
5504
  /* @__PURE__ */ jsxs("span", { className: cn("inline-flex flex-1 min-w-0 items-center [justify-content:inherit] gap-2", busy && "almadar-busy-content"), children: [
@@ -5219,6 +5514,8 @@ var Button = React17__default.forwardRef(
5219
5514
  return;
5220
5515
  }
5221
5516
  emitAction();
5517
+ onClick?.(e);
5518
+ if (e.defaultPrevented) return;
5222
5519
  if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) return;
5223
5520
  if (!href.startsWith("#") && isInertNavStack(navStack)) return;
5224
5521
  e.preventDefault();
@@ -5299,7 +5596,7 @@ var sizeStyles2 = {
5299
5596
  md: "px-2.5 py-1 text-sm",
5300
5597
  lg: "px-3 py-1.5 text-base"
5301
5598
  };
5302
- var Badge = React17__default.forwardRef(
5599
+ var Badge = React18__default.forwardRef(
5303
5600
  ({ className, variant = "default", size = "sm", amount, label, icon, iconAsset, children, onRemove, removeLabel, onClick, ...props }, ref) => {
5304
5601
  const { t } = useTranslate();
5305
5602
  const iconSizes = {
@@ -5363,12 +5660,12 @@ var Badge = React17__default.forwardRef(
5363
5660
  }
5364
5661
  );
5365
5662
  Badge.displayName = "Badge";
5366
- var SurfaceContext = React17__default.createContext(false);
5663
+ var SurfaceContext = React18__default.createContext(false);
5367
5664
  function SurfaceBoundary({ children }) {
5368
5665
  return /* @__PURE__ */ jsx(SurfaceContext.Provider, { value: true, children });
5369
5666
  }
5370
5667
  function useContentSurface(surface = "auto") {
5371
- const onSurface = React17__default.useContext(SurfaceContext);
5668
+ const onSurface = React18__default.useContext(SurfaceContext);
5372
5669
  const paints = surface === "auto" && !onSurface;
5373
5670
  return {
5374
5671
  className: paints ? "surface-content" : void 0,
@@ -5381,7 +5678,7 @@ var sizeStyles3 = {
5381
5678
  md: "h-6 w-6",
5382
5679
  lg: "h-8 w-8"
5383
5680
  };
5384
- var Spinner = React17__default.forwardRef(
5681
+ var Spinner = React18__default.forwardRef(
5385
5682
  ({ className, size = "md", overlay, ...props }, ref) => {
5386
5683
  const { t } = useTranslate();
5387
5684
  if (overlay) {
@@ -5466,7 +5763,7 @@ var lookStyles = {
5466
5763
  chip: "shadow-none rounded-pill border-[length:var(--border-width)] border-border",
5467
5764
  "tile-image-first": "p-0 overflow-hidden"
5468
5765
  };
5469
- var Card = React17__default.forwardRef(
5766
+ var Card = React18__default.forwardRef(
5470
5767
  ({
5471
5768
  className,
5472
5769
  variant = "bordered",
@@ -5516,9 +5813,9 @@ var Card = React17__default.forwardRef(
5516
5813
  }
5517
5814
  );
5518
5815
  Card.displayName = "Card";
5519
- var CardHeader = React17__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), ...props }));
5816
+ var CardHeader = React18__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), ...props }));
5520
5817
  CardHeader.displayName = "CardHeader";
5521
- var CardTitle = React17__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5818
+ var CardTitle = React18__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5522
5819
  "h3",
5523
5820
  {
5524
5821
  ref,
@@ -5531,11 +5828,11 @@ var CardTitle = React17__default.forwardRef(({ className, ...props }, ref) => /*
5531
5828
  }
5532
5829
  ));
5533
5830
  CardTitle.displayName = "CardTitle";
5534
- var CardContent = React17__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("", className), ...props }));
5831
+ var CardContent = React18__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("", className), ...props }));
5535
5832
  CardContent.displayName = "CardContent";
5536
5833
  var CardBody = CardContent;
5537
5834
  CardBody.displayName = "CardBody";
5538
- var CardFooter = React17__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5835
+ var CardFooter = React18__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5539
5836
  "div",
5540
5837
  {
5541
5838
  ref,
@@ -5632,7 +5929,7 @@ var Center = ({
5632
5929
  ...rest
5633
5930
  }) => {
5634
5931
  const mergedStyle = minHeight ? { minHeight, ...style } : style;
5635
- return React17__default.createElement(Component, {
5932
+ return React18__default.createElement(Component, {
5636
5933
  ...domPassthrough(rest),
5637
5934
  className: cn(
5638
5935
  inline ? "inline-flex" : "flex",
@@ -5739,8 +6036,8 @@ var Avatar = ({
5739
6036
  }) => {
5740
6037
  const eventBus = useEventBus();
5741
6038
  const { t } = useTranslate();
5742
- const [imgFailed, setImgFailed] = React17__default.useState(false);
5743
- React17__default.useEffect(() => {
6039
+ const [imgFailed, setImgFailed] = React18__default.useState(false);
6040
+ React18__default.useEffect(() => {
5744
6041
  setImgFailed(false);
5745
6042
  }, [src]);
5746
6043
  const initials = providedInitials ?? (name ? generateInitials(name) : void 0);
@@ -5858,6 +6155,13 @@ var roundedClass = {
5858
6155
  "2xl": "rounded-[calc(var(--radius-xl)*1.5)]",
5859
6156
  full: "rounded-full"
5860
6157
  };
6158
+ var positionClass = {
6159
+ center: "object-center",
6160
+ top: "object-top",
6161
+ bottom: "object-bottom",
6162
+ left: "object-left",
6163
+ right: "object-right"
6164
+ };
5861
6165
  var Image2 = ({
5862
6166
  src,
5863
6167
  alt,
@@ -5866,6 +6170,10 @@ var Image2 = ({
5866
6170
  rounded = "md",
5867
6171
  bordered = false,
5868
6172
  loading = "lazy",
6173
+ position = "center",
6174
+ srcSet,
6175
+ sizes,
6176
+ priority = false,
5869
6177
  className,
5870
6178
  ...rest
5871
6179
  }) => {
@@ -5874,14 +6182,18 @@ var Image2 = ({
5874
6182
  "img",
5875
6183
  {
5876
6184
  src,
6185
+ srcSet,
6186
+ sizes,
6187
+ fetchPriority: priority ? "high" : void 0,
5877
6188
  alt,
5878
6189
  ...domPassthrough(rest),
5879
6190
  role: alt === "" ? "presentation" : rest.role,
5880
- loading,
6191
+ loading: priority ? "eager" : loading,
5881
6192
  decoding: "async",
5882
6193
  className: cn(
5883
6194
  "block w-full h-auto max-w-full",
5884
6195
  fitClass[fit],
6196
+ positionClass[position],
5885
6197
  aspectClass[aspect],
5886
6198
  roundedClass[rounded],
5887
6199
  bordered && "border border-border",
@@ -6097,7 +6409,7 @@ var Container = ({
6097
6409
  ...rest
6098
6410
  }) => {
6099
6411
  const resolvedSize = maxWidth ?? size ?? "lg";
6100
- return React17__default.createElement(
6412
+ return React18__default.createElement(
6101
6413
  Component,
6102
6414
  {
6103
6415
  ...domPassthrough(rest),
@@ -6208,10 +6520,10 @@ function ListSkeleton({ rows = 5, className }) {
6208
6520
  ] }, i)) });
6209
6521
  }
6210
6522
  function GridSkeleton({ rows = 6, className }) {
6211
- return /* @__PURE__ */ jsx(SimpleGrid, { minChildWidth: "260px", gap: "md", className, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx(CardSkeleton, {}, i)) });
6523
+ return /* @__PURE__ */ jsx(SimpleGrid, { minChildWidth: "256px", gap: "md", className, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx(CardSkeleton, {}, i)) });
6212
6524
  }
6213
6525
  function StatsSkeleton({ columns = 4, className }) {
6214
- return /* @__PURE__ */ jsx(SimpleGrid, { minChildWidth: "180px", gap: "md", className, children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: "p-4 border border-border rounded-container", children: [
6526
+ return /* @__PURE__ */ jsx(SimpleGrid, { minChildWidth: "176px", gap: "md", className, children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: "p-4 border border-border rounded-container", children: [
6215
6527
  /* @__PURE__ */ jsx(SkeletonLine, { className: "w-1/2 h-3" }),
6216
6528
  /* @__PURE__ */ jsx(SkeletonBlock, { className: "h-7 w-2/3" })
6217
6529
  ] }, i)) });
@@ -6233,7 +6545,7 @@ function DetailSkeleton({ rows = 6, className }) {
6233
6545
  /* @__PURE__ */ jsx(SkeletonBlock, { className: "h-20 flex-1" }),
6234
6546
  /* @__PURE__ */ jsx(SkeletonBlock, { className: "h-20 flex-1" })
6235
6547
  ] }),
6236
- /* @__PURE__ */ jsx(SimpleGrid, { minChildWidth: "200px", maxCols: 3, gap: "md", children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
6548
+ /* @__PURE__ */ jsx(SimpleGrid, { minChildWidth: "192px", maxCols: 3, gap: "md", children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
6237
6549
  /* @__PURE__ */ jsx(SkeletonLine, { className: "w-1/3 h-3" }),
6238
6550
  /* @__PURE__ */ jsx(SkeletonBlock, { className: "h-5 w-2/3" })
6239
6551
  ] }, i)) }),
@@ -6250,8 +6562,8 @@ function Skeleton({
6250
6562
  ...rest
6251
6563
  }) {
6252
6564
  const { t } = useTranslate();
6253
- const shape = spec !== void 0 ? shapeOfSpec(spec) : { variant, rows, columns, fields };
6254
- if (shape === null) return null;
6565
+ const shape2 = spec !== void 0 ? shapeOfSpec(spec) : { variant, rows, columns, fields };
6566
+ if (shape2 === null) return null;
6255
6567
  return /* @__PURE__ */ jsx(
6256
6568
  Box,
6257
6569
  {
@@ -6259,10 +6571,10 @@ function Skeleton({
6259
6571
  role: "status",
6260
6572
  "aria-busy": "true",
6261
6573
  "aria-label": rest["aria-label"] ?? t("common.loading"),
6262
- "data-skeleton": shape.variant,
6574
+ "data-skeleton": shape2.variant,
6263
6575
  ...{ [LOADING_STATE_MARKER]: "" },
6264
6576
  className: "w-full",
6265
- children: renderVariant(shape.variant, shape.rows, shape.columns, shape.fields, className)
6577
+ children: renderVariant(shape2.variant, shape2.rows, shape2.columns, shape2.fields, className)
6266
6578
  }
6267
6579
  );
6268
6580
  }
@@ -6609,6 +6921,35 @@ var AnimatedCounter = ({
6609
6921
  ] });
6610
6922
  };
6611
6923
  AnimatedCounter.displayName = "AnimatedCounter";
6924
+ function useMediaQuery(query) {
6925
+ const subscribe = useCallback(
6926
+ (onChange) => {
6927
+ if (!hasMatchMedia()) return () => void 0;
6928
+ const mql = window.matchMedia(query);
6929
+ mql.addEventListener("change", onChange);
6930
+ return () => mql.removeEventListener("change", onChange);
6931
+ },
6932
+ [query]
6933
+ );
6934
+ return useSyncExternalStore(
6935
+ subscribe,
6936
+ () => hasMatchMedia() && window.matchMedia(query).matches,
6937
+ () => false
6938
+ );
6939
+ }
6940
+ function hasMatchMedia() {
6941
+ return typeof window !== "undefined" && typeof window.matchMedia === "function";
6942
+ }
6943
+
6944
+ // hooks/usePrefersReducedMotion.ts
6945
+ function usePrefersReducedMotion() {
6946
+ return useMediaQuery("(prefers-reduced-motion: reduce)");
6947
+ }
6948
+ var revealLog = createLogger("almadar:ui:animated-reveal");
6949
+ function revealThresholds(threshold) {
6950
+ const steps = Array.from({ length: 20 }, (_, i) => i * threshold / 20);
6951
+ return [...steps, threshold];
6952
+ }
6612
6953
  var initialStyles = {
6613
6954
  "fade-up": { opacity: 0, transform: "translateY(24px)" },
6614
6955
  "fade-down": { opacity: 0, transform: "translateY(-24px)" },
@@ -6629,7 +6970,7 @@ var animatedStyles = {
6629
6970
  "scale-up": { opacity: 1, transform: "scale(1) translateY(0)" },
6630
6971
  "none": {}
6631
6972
  };
6632
- var AnimatedReveal = React17__default.forwardRef(
6973
+ var AnimatedReveal = React18__default.forwardRef(
6633
6974
  ({
6634
6975
  trigger = "scroll",
6635
6976
  animation = "fade-up",
@@ -6637,6 +6978,9 @@ var AnimatedReveal = React17__default.forwardRef(
6637
6978
  delay = 0,
6638
6979
  threshold = 0.15,
6639
6980
  once = true,
6981
+ revealEvent,
6982
+ enterEvent,
6983
+ leaveEvent,
6640
6984
  animate: manualAnimate,
6641
6985
  easing = "cubic-bezier(0.16, 1, 0.3, 1)",
6642
6986
  children,
@@ -6645,8 +6989,12 @@ var AnimatedReveal = React17__default.forwardRef(
6645
6989
  ...props
6646
6990
  }, forwardedRef) => {
6647
6991
  const [isAnimated, setIsAnimated] = useState(false);
6992
+ const reducedMotion = usePrefersReducedMotion();
6993
+ const eventBus = useEventBus();
6994
+ const revealEmitted = useRef(false);
6648
6995
  const internalRef = useRef(null);
6649
6996
  const hasAnimated = useRef(false);
6997
+ const inView = useRef(false);
6650
6998
  const setRef = useCallback(
6651
6999
  (node) => {
6652
7000
  internalRef.current = node;
@@ -6661,19 +7009,42 @@ var AnimatedReveal = React17__default.forwardRef(
6661
7009
  if (!el) return;
6662
7010
  const observer2 = new IntersectionObserver(
6663
7011
  ([entry]) => {
6664
- if (entry.isIntersecting) {
7012
+ if (entry.isIntersecting !== inView.current) {
7013
+ const event = entry.isIntersecting ? enterEvent : leaveEvent;
7014
+ inView.current = entry.isIntersecting;
7015
+ if (event) eventBus.emit(`UI:${event}`, {});
7016
+ }
7017
+ const seenEnough = entry.isIntersecting && (entry.intersectionRatio >= threshold || entry.rootBounds !== null && entry.intersectionRect.height >= threshold * entry.rootBounds.height);
7018
+ if (seenEnough) {
7019
+ if (revealEvent && !reducedMotion && !revealEmitted.current) {
7020
+ revealEmitted.current = true;
7021
+ revealLog.debug("reveal event", { event: revealEvent });
7022
+ eventBus.emit(`UI:${revealEvent}`, {});
7023
+ }
6665
7024
  if (once && hasAnimated.current) return;
6666
7025
  hasAnimated.current = true;
6667
7026
  setIsAnimated(true);
6668
- } else if (!once) {
7027
+ } else if (!entry.isIntersecting && !once) {
6669
7028
  setIsAnimated(false);
6670
7029
  }
6671
7030
  },
6672
- { threshold }
7031
+ { threshold: revealThresholds(threshold) }
7032
+ );
7033
+ observer2.observe(el);
7034
+ return () => observer2.disconnect();
7035
+ }, [trigger, threshold, once, revealEvent, enterEvent, leaveEvent, reducedMotion, eventBus]);
7036
+ useEffect(() => {
7037
+ const el = internalRef.current;
7038
+ if (!el || typeof IntersectionObserver === "undefined") return;
7039
+ const observer2 = new IntersectionObserver(
7040
+ ([entry]) => {
7041
+ el.dataset.onScreen = entry.isIntersecting ? "true" : "false";
7042
+ },
7043
+ { rootMargin: "200px" }
6673
7044
  );
6674
7045
  observer2.observe(el);
6675
7046
  return () => observer2.disconnect();
6676
- }, [trigger, threshold, once]);
7047
+ }, []);
6677
7048
  const handleMouseEnter = trigger === "hover" ? () => setIsAnimated(true) : void 0;
6678
7049
  const handleMouseLeave = trigger === "hover" ? () => {
6679
7050
  if (!once || !hasAnimated.current) {
@@ -6686,17 +7057,19 @@ var AnimatedReveal = React17__default.forwardRef(
6686
7057
  setIsAnimated(manualAnimate);
6687
7058
  }
6688
7059
  }, [trigger, manualAnimate]);
6689
- const active = isAnimated;
7060
+ const active = isAnimated || reducedMotion;
6690
7061
  const currentStyle = active ? animatedStyles[animation] : initialStyles[animation];
6691
7062
  return /* @__PURE__ */ jsx(
6692
7063
  "div",
6693
7064
  {
6694
7065
  ref: setRef,
6695
- className: cn("will-change-[opacity,transform]", className),
7066
+ className: cn(className),
6696
7067
  style: {
6697
7068
  ...currentStyle,
7069
+ // A revealed band is page-sized: keeping the layer hint after the reveal holds a huge GPU layer.
7070
+ willChange: active ? "auto" : "opacity, transform",
6698
7071
  transitionProperty: "opacity, transform",
6699
- transitionDuration: `${duration}ms`,
7072
+ transitionDuration: reducedMotion ? "0ms" : `${duration}ms`,
6700
7073
  transitionDelay: `${delay}ms`,
6701
7074
  transitionTimingFunction: easing,
6702
7075
  ...style
@@ -6782,7 +7155,7 @@ function applyMorphAnimation(container, animate, duration, delay, easing) {
6782
7155
  el.style.opacity = animate ? "1" : "0";
6783
7156
  });
6784
7157
  }
6785
- var AnimatedGraphic = React17__default.forwardRef(
7158
+ var AnimatedGraphic = React18__default.forwardRef(
6786
7159
  ({
6787
7160
  src,
6788
7161
  svgContent,
@@ -6805,7 +7178,7 @@ var AnimatedGraphic = React17__default.forwardRef(
6805
7178
  const fetchedSvg = useFetchedSvg(svgContent ? void 0 : src);
6806
7179
  const resolvedSvg = svgContent ?? fetchedSvg;
6807
7180
  const prevAnimateRef = useRef(animate);
6808
- const setRef = React17__default.useCallback(
7181
+ const setRef = React18__default.useCallback(
6809
7182
  (node) => {
6810
7183
  containerRef.current = node;
6811
7184
  if (typeof ref === "function") ref(node);