@geomak/ui 5.0.1 → 5.0.3

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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { colors_default } from './chunk-GKXP6OJJ.js';
2
2
  export { colors_default as COLORS, PALETTE as palette, semanticTokens, vars } from './chunk-GKXP6OJJ.js';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
- import React8, { createContext, useState, useEffect, useMemo, useContext, useRef, useId, useCallback, useLayoutEffect } from 'react';
4
+ import React8, { createContext, useState, useEffect, useMemo, useCallback, useContext, useRef, useId, useLayoutEffect } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  import * as Dialog from '@radix-ui/react-dialog';
7
7
  import { useReducedMotion, AnimatePresence, motion } from 'framer-motion';
@@ -9,7 +9,6 @@ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
9
9
  import * as TabsPrimitive from '@radix-ui/react-tabs';
10
10
  import * as Accordion from '@radix-ui/react-accordion';
11
11
  import * as ToggleGroup from '@radix-ui/react-toggle-group';
12
- import * as Toast from '@radix-ui/react-toast';
13
12
  import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';
14
13
  import * as Popover from '@radix-ui/react-popover';
15
14
  import * as SwitchPrimitive from '@radix-ui/react-switch';
@@ -783,12 +782,12 @@ var VIEWPORT_CLASSES = {
783
782
  function getInitialMotion(pos, reduced) {
784
783
  if (reduced) return { opacity: 0, y: 0, scale: 1 };
785
784
  const bottom = pos.startsWith("bottom");
786
- return {
787
- opacity: 0,
788
- y: bottom ? 24 : -24,
789
- // rise from below (bottom) or drop from above (top)
790
- scale: 0.92
791
- };
785
+ return { opacity: 0, y: bottom ? 28 : -28, scale: 0.92 };
786
+ }
787
+ function getExitMotion(pos, reduced) {
788
+ if (reduced) return { opacity: 0, y: 0, scale: 1 };
789
+ const bottom = pos.startsWith("bottom");
790
+ return { opacity: 0, y: bottom ? 18 : -18, scale: 0.94 };
792
791
  }
793
792
  function TypeIcon({ type }) {
794
793
  if (type === "success") {
@@ -820,66 +819,87 @@ function NotificationItem({
820
819
  onClose,
821
820
  reduced
822
821
  }) {
823
- const [hovered, setHovered] = useState(false);
824
- const initial = getInitialMotion(pos, reduced);
825
- const center = pos.endsWith("center");
822
+ const [paused, setPaused] = useState(false);
826
823
  const duration = n.duration ?? 4e3;
827
- const showProgress = !reduced && isFinite(duration) && duration > 0;
824
+ const isAutoDismissing = isFinite(duration) && duration > 0;
825
+ const showProgress = !reduced && isAutoDismissing;
826
+ const timerRef = useRef(null);
827
+ const startTimeRef = useRef(0);
828
+ const remainingRef = useRef(duration);
829
+ const clearTimer = useCallback(() => {
830
+ if (timerRef.current !== null) {
831
+ clearTimeout(timerRef.current);
832
+ timerRef.current = null;
833
+ }
834
+ }, []);
835
+ const scheduleDismiss = useCallback((ms) => {
836
+ clearTimer();
837
+ if (!isAutoDismissing) return;
838
+ startTimeRef.current = Date.now();
839
+ timerRef.current = setTimeout(() => onClose(n.id), ms);
840
+ }, [clearTimer, isAutoDismissing, n.id, onClose]);
841
+ useEffect(() => {
842
+ if (paused || !isAutoDismissing) return;
843
+ scheduleDismiss(remainingRef.current);
844
+ return clearTimer;
845
+ }, [paused, isAutoDismissing, scheduleDismiss, clearTimer]);
846
+ const onPauseStart = () => {
847
+ if (!isAutoDismissing) return;
848
+ const elapsed = Date.now() - startTimeRef.current;
849
+ remainingRef.current = Math.max(0, remainingRef.current - elapsed);
850
+ setPaused(true);
851
+ };
852
+ const onPauseEnd = () => {
853
+ if (!isAutoDismissing) return;
854
+ setPaused(false);
855
+ };
828
856
  return /* @__PURE__ */ jsx(
829
857
  motion.div,
830
858
  {
859
+ layout: true,
831
860
  className: "pointer-events-auto",
832
- initial,
861
+ initial: getInitialMotion(pos, reduced),
833
862
  animate: { opacity: 1, y: 0, scale: 1 },
834
- exit: {
835
- opacity: 0,
836
- y: pos.startsWith("bottom") ? 16 : -16,
837
- scale: 0.94,
838
- transition: reduced ? { duration: 0 } : {
839
- opacity: { duration: 0.14, delay: 0.06 },
840
- y: { type: "tween", duration: 0.22, ease: [0.4, 0, 1, 1] },
841
- scale: { type: "tween", duration: 0.22, ease: [0.4, 0, 1, 1] }
842
- }
843
- },
863
+ exit: getExitMotion(pos, reduced),
844
864
  transition: reduced ? { duration: 0 } : {
845
- // Opacity finishes in 0.15 s card is fully opaque while y/scale
846
- // still have ~55 % of their travel left → movement is clearly visible.
865
+ // Opacity finishes in 0.15 s; y/scale take 0.34 s.
866
+ // Card is opaque while still travelling movement
867
+ // is clearly visible to the user.
847
868
  opacity: { duration: 0.15 },
848
869
  y: { type: "tween", duration: 0.34, ease: [0.16, 1, 0.3, 1] },
849
- scale: { type: "tween", duration: 0.34, ease: [0.16, 1, 0.3, 1] }
870
+ scale: { type: "tween", duration: 0.34, ease: [0.16, 1, 0.3, 1] },
871
+ layout: { duration: 0.22, ease: [0.16, 1, 0.3, 1] }
850
872
  },
851
- onMouseEnter: () => setHovered(true),
852
- onMouseLeave: () => setHovered(false),
873
+ onMouseEnter: onPauseStart,
874
+ onMouseLeave: onPauseEnd,
875
+ onFocus: onPauseStart,
876
+ onBlur: onPauseEnd,
877
+ role: n.type === "danger" || n.type === "warning" ? "alert" : "status",
878
+ "aria-live": n.type === "danger" || n.type === "warning" ? "assertive" : "polite",
853
879
  children: /* @__PURE__ */ jsxs(
854
- Toast.Root,
880
+ "div",
855
881
  {
856
- open: true,
857
- duration,
858
- onOpenChange: (o) => {
859
- if (!o) onClose(n.id);
860
- },
861
882
  className: [
862
- "w-[300px] rounded-md shadow-lg overflow-hidden",
863
- center ? "mx-auto" : "",
864
- "focus:outline-none",
883
+ "w-[300px] rounded-md shadow-lg overflow-hidden focus:outline-none",
865
884
  TYPE_BG[n.type ?? "info"]
866
885
  ].join(" "),
867
886
  children: [
868
887
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-3 pr-2.5", children: [
869
888
  /* @__PURE__ */ jsx("span", { className: "mt-0.5 flex-shrink-0 text-white/90", children: /* @__PURE__ */ jsx(TypeIcon, { type: n.type ?? "info" }) }),
870
889
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
871
- /* @__PURE__ */ jsx(Toast.Title, { className: "text-sm font-semibold text-white leading-snug", children: n.title }),
872
- n.description && /* @__PURE__ */ jsx(Toast.Description, { className: "mt-0.5 text-xs text-white/75 leading-relaxed", children: n.description })
890
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-white leading-snug", children: n.title }),
891
+ n.description && /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-white/75 leading-relaxed", children: n.description })
873
892
  ] }),
874
- /* @__PURE__ */ jsx(Toast.Action, { asChild: true, altText: "Close", children: /* @__PURE__ */ jsx(
893
+ /* @__PURE__ */ jsx(
875
894
  "button",
876
895
  {
877
- "aria-label": "Close",
896
+ type: "button",
897
+ "aria-label": "Close notification",
878
898
  onClick: () => onClose(n.id),
879
899
  className: "flex-shrink-0 mt-0.5 rounded p-1 text-white/60 hover:text-white hover:bg-white/15 transition-colors duration-100 focus:outline-none focus-visible:ring-1 focus-visible:ring-white/50",
880
900
  children: /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M9 3L3 9M3 3l6 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
881
901
  }
882
- ) })
902
+ )
883
903
  ] }),
884
904
  showProgress && /* @__PURE__ */ jsx("div", { className: "relative h-[3px] bg-white/20 overflow-hidden", children: /* @__PURE__ */ jsx(
885
905
  "div",
@@ -887,7 +907,7 @@ function NotificationItem({
887
907
  className: "absolute inset-0 bg-white/60 [transform-origin:left]",
888
908
  style: {
889
909
  animation: `notification-progress ${duration}ms linear forwards`,
890
- animationPlayState: hovered ? "paused" : "running"
910
+ animationPlayState: paused ? "paused" : "running"
891
911
  }
892
912
  }
893
913
  ) })
@@ -903,26 +923,27 @@ function NotificationProvider({
903
923
  }) {
904
924
  const [notifications, setNotifications] = useState([]);
905
925
  const reduced = useReducedMotion();
906
- const open = (payload) => {
926
+ const open = useCallback((payload) => {
907
927
  setNotifications((prev) => [
908
928
  ...prev,
909
929
  { duration: 4e3, ...payload, id: Date.now() + Math.random() }
910
930
  ]);
911
- };
912
- const close = (id) => {
931
+ }, []);
932
+ const close = useCallback((id) => {
913
933
  setNotifications((prev) => prev.filter((n) => n.id !== id));
914
- };
915
- return /* @__PURE__ */ jsx(NotificationContext.Provider, { value: { open, close }, children: /* @__PURE__ */ jsxs(Toast.Provider, { swipeDirection: position.endsWith("right") ? "right" : position.endsWith("left") ? "left" : "up", children: [
934
+ }, []);
935
+ return /* @__PURE__ */ jsxs(NotificationContext.Provider, { value: { open, close }, children: [
916
936
  children,
917
937
  /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
918
- Toast.Viewport,
938
+ "ul",
919
939
  {
920
- asChild: true,
940
+ role: "region",
941
+ "aria-label": "Notifications",
921
942
  className: [
922
943
  VIEWPORT_CLASSES[position],
923
- "z-[500000] gap-2 w-[332px] outline-none pointer-events-none"
944
+ "z-[500000] gap-2 w-[316px] outline-none pointer-events-none m-0 p-0 list-none"
924
945
  ].join(" "),
925
- children: /* @__PURE__ */ jsx("ul", { children: /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: notifications.map((n) => /* @__PURE__ */ jsx(
946
+ children: /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: notifications.map((n) => /* @__PURE__ */ jsx(
926
947
  NotificationItem,
927
948
  {
928
949
  n,
@@ -931,10 +952,10 @@ function NotificationProvider({
931
952
  reduced
932
953
  },
933
954
  n.id
934
- )) }) })
955
+ )) })
935
956
  }
936
957
  ) })
937
- ] }) });
958
+ ] });
938
959
  }
939
960
  function useNotification() {
940
961
  const { open } = useContext(NotificationContext);
@@ -946,6 +967,9 @@ function useNotification() {
946
967
  };
947
968
  }
948
969
  var SIZE_MAP = {
970
+ // xs is sized to fit beside button text (~14px) — async AutoComplete,
971
+ // Button loading prop, inline status badges, etc.
972
+ xs: { outer: "w-3.5 h-3.5", inner: "w-1.5 h-1.5", dot: "w-0.5 h-0.5", stroke: "border-[1.5px]", text: "text-[10px]" },
949
973
  sm: { outer: "w-8 h-8", inner: "w-4 h-4", dot: "w-1 h-1", stroke: "border-2", text: "text-xs" },
950
974
  md: { outer: "w-20 h-20", inner: "w-12 h-12", dot: "w-2 h-2", stroke: "border-[3px]", text: "text-2xl" },
951
975
  lg: { outer: "w-32 h-32", inner: "w-20 h-20", dot: "w-3 h-3", stroke: "border-4", text: "text-4xl" }
@@ -1106,53 +1130,82 @@ function List2({ items, onItemClick, activeKey }) {
1106
1130
  )
1107
1131
  )) });
1108
1132
  }
1133
+ var TOGGLE_POSITION_CLASS = {
1134
+ "top-left": "top-2 left-2",
1135
+ "top-right": "top-2 right-2",
1136
+ "bottom-left": "bottom-2 left-2",
1137
+ "bottom-right": "bottom-2 right-2"
1138
+ };
1109
1139
  function ScalableContainer({
1110
- width,
1111
- height,
1140
+ width = "100%",
1141
+ height = "auto",
1112
1142
  children,
1113
- assignClassOnClick
1143
+ assignClassOnClick,
1144
+ expandIcon,
1145
+ collapseIcon,
1146
+ togglePosition = "top-right"
1114
1147
  }) {
1115
1148
  const containerRef = useRef(null);
1116
1149
  const [isScaled, setScaled] = useState(false);
1117
- const [wrapperClass, setWrapperClass] = useState("");
1118
- const onClick = () => {
1150
+ const reduced = useReducedMotion();
1151
+ const onToggle = () => {
1119
1152
  const next = !isScaled;
1120
1153
  setScaled(next);
1121
- setTimeout(() => {
1122
- containerRef.current?.scrollIntoView({ behavior: "smooth" });
1123
- if (assignClassOnClick) {
1124
- setWrapperClass(next ? assignClassOnClick : "");
1125
- }
1126
- }, 200);
1154
+ requestAnimationFrame(() => containerRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" }));
1127
1155
  };
1156
+ const wrapperClass = isScaled ? assignClassOnClick : void 0;
1128
1157
  return /* @__PURE__ */ jsxs(
1129
- "div",
1158
+ motion.div,
1130
1159
  {
1131
1160
  ref: containerRef,
1132
- style: {
1161
+ layout: true,
1162
+ animate: {
1133
1163
  width: isScaled ? "100%" : width,
1134
1164
  height: isScaled ? "100%" : height
1135
1165
  },
1136
- className: "rounded-lg bg-surface-raised flex flex-col transition-all duration-300 origin-center",
1166
+ transition: reduced ? { duration: 0 } : {
1167
+ width: { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] },
1168
+ height: { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] },
1169
+ layout: { duration: 0.32, ease: [0.16, 1, 0.3, 1] }
1170
+ },
1171
+ className: [
1172
+ "relative rounded-lg overflow-hidden",
1173
+ // OS-window aesthetic: subtle elevation at rest, lifted shadow
1174
+ // when expanded. No background colour change.
1175
+ isScaled ? "shadow-2xl" : "shadow-md",
1176
+ "transition-shadow duration-300"
1177
+ ].join(" "),
1137
1178
  children: [
1138
- /* @__PURE__ */ jsx("div", { className: "p-2 w-max", children: /* @__PURE__ */ jsx(Tooltip, { placement: "right", title: isScaled ? "Collapse" : "Expand", children: /* @__PURE__ */ jsx(
1139
- IconButton,
1179
+ /* @__PURE__ */ jsx(Tooltip, { placement: "bottom", title: isScaled ? "Collapse" : "Expand", children: /* @__PURE__ */ jsx(
1180
+ "button",
1140
1181
  {
1141
- onClick,
1142
- icon: isScaled ? (
1143
- /* Collapse (arrows-pointing-in) */
1144
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M3.22 3.22a.75.75 0 011.06 0l3.97 3.97V4.5a.75.75 0 011.5 0V9a.75.75 0 01-.75.75H4.5a.75.75 0 010-1.5h2.69L3.22 4.28a.75.75 0 010-1.06zm17.56 0a.75.75 0 010 1.06l-3.97 3.97h2.69a.75.75 0 010 1.5H15a.75.75 0 01-.75-.75V4.5a.75.75 0 011.5 0v2.69l3.97-3.97a.75.75 0 011.06 0zM3.75 15a.75.75 0 01.75-.75H9a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-2.69l-3.97 3.97a.75.75 0 01-1.06-1.06l3.97-3.97H4.5a.75.75 0 01-.75-.75zm10.5 0a.75.75 0 01.75-.75h4.5a.75.75 0 01.75.75 .75.75 0 01-.75.75h-2.69l3.97 3.97a.75.75 0 11-1.06 1.06l-3.97-3.97v2.69a.75.75 0 01-1.5 0V15z", clipRule: "evenodd" }) })
1145
- ) : (
1146
- /* Expand (arrows-pointing-out) */
1147
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M15 3a.75.75 0 01.75-.75h5.25A.75.75 0 0121 3v5.25a.75.75 0 01-1.5 0V4.81l-5.72 5.72a.75.75 0 11-1.06-1.06L18.19 3.75H15.75A.75.75 0 0115 3zM3 15a.75.75 0 01.75-.75h2.44l5.72-5.72a.75.75 0 111.06 1.06l-5.72 5.72v2.44a.75.75 0 01-1.5 0V15.75A.75.75 0 013 15zm0-11.25A.75.75 0 013.75 3h5.25a.75.75 0 010 1.5H4.81l5.72 5.72a.75.75 0 11-1.06 1.06L3.75 5.56V8.25a.75.75 0 01-1.5 0V3.75A.75.75 0 013 3zm18 12a.75.75 0 01-.75.75h-5.25a.75.75 0 010-1.5h2.44l-5.72-5.72a.75.75 0 111.06-1.06l5.72 5.72v-2.44a.75.75 0 011.5 0V15z", clipRule: "evenodd" }) })
1148
- )
1182
+ type: "button",
1183
+ onClick: onToggle,
1184
+ "aria-label": isScaled ? "Collapse container" : "Expand container",
1185
+ "aria-expanded": isScaled,
1186
+ className: [
1187
+ "absolute z-10",
1188
+ TOGGLE_POSITION_CLASS[togglePosition],
1189
+ "w-7 h-7 inline-flex items-center justify-center",
1190
+ "rounded-md bg-surface/80 backdrop-blur-sm border border-border",
1191
+ "text-foreground-secondary hover:text-foreground hover:bg-surface",
1192
+ "shadow-sm transition-colors duration-150",
1193
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
1194
+ ].join(" "),
1195
+ children: isScaled ? collapseIcon ?? /* @__PURE__ */ jsx(CollapseIcon, {}) : expandIcon ?? /* @__PURE__ */ jsx(ExpandIcon, {})
1149
1196
  }
1150
- ) }) }),
1197
+ ) }),
1151
1198
  /* @__PURE__ */ jsx("div", { className: wrapperClass, children })
1152
1199
  ]
1153
1200
  }
1154
1201
  );
1155
1202
  }
1203
+ function CollapseIcon() {
1204
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 9L4 4M9 9V4M9 9H4M15 9L20 4M15 9V4M15 9H20M9 15L4 20M9 15V20M9 15H4M15 15L20 20M15 15V20M15 15H20" }) });
1205
+ }
1206
+ function ExpandIcon() {
1207
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4 8V4h4M20 8V4h-4M4 16v4h4M20 16v4h-4" }) });
1208
+ }
1156
1209
  function GridCard({ item, buttonText = "Open Application", onOpen }) {
1157
1210
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col w-[200px] h-[250px] rounded-lg bg-ice dark:bg-independence items-center justify-between p-2 shadow-2xl", children: [
1158
1211
  /* @__PURE__ */ jsx("div", { className: "text-prussian-blue dark:text-white text-lg font-bold text-center h-1/4", children: /* @__PURE__ */ jsx("h2", { children: item.title }) }),
@@ -1313,49 +1366,6 @@ function CatalogCarousel({ items, buttonText, onOpen }) {
1313
1366
  function Catalog({ display = "grid", items = [], buttonText, onOpen }) {
1314
1367
  return /* @__PURE__ */ jsx("div", { className: "w-full h-full", children: display === "grid" ? /* @__PURE__ */ jsx(CatalogGrid, { items, buttonText, onOpen }) : /* @__PURE__ */ jsx(CatalogCarousel, { items, buttonText, onOpen }) });
1315
1368
  }
1316
- function MenuBarItem({ icon, isActive, title, onClick }) {
1317
- return /* @__PURE__ */ jsx(Tooltip, { title, placement: "right", children: /* @__PURE__ */ jsx(
1318
- "div",
1319
- {
1320
- role: "button",
1321
- "aria-label": title,
1322
- "aria-current": isActive ? "page" : void 0,
1323
- className: `transition duration-300 hover:bg-accent hover:text-accent-fg ${isActive ? "bg-accent text-accent-fg" : "text-foreground-secondary"} rounded-lg p-2 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-accent`,
1324
- onClick,
1325
- tabIndex: 0,
1326
- onKeyDown: (e) => {
1327
- if (e.key === "Enter" || e.key === " ") {
1328
- e.preventDefault();
1329
- onClick?.();
1330
- }
1331
- },
1332
- children: icon
1333
- }
1334
- ) });
1335
- }
1336
- function MenuBar({ items }) {
1337
- return (
1338
- // `calculated-height` was an orphaned CSS class. Replaced with `h-full`
1339
- // so the MenuBar fills whatever vertical space its parent gives it.
1340
- /* @__PURE__ */ jsx(
1341
- "nav",
1342
- {
1343
- "aria-label": "Main navigation",
1344
- className: "w-16 h-full bg-surface-raised rounded-tr-lg rounded-br-lg flex flex-col gap-2 items-center p-2 z-50",
1345
- children: items.map((item) => /* @__PURE__ */ jsx(
1346
- MenuBarItem,
1347
- {
1348
- icon: item.icon,
1349
- title: item.title,
1350
- isActive: item.isActive,
1351
- onClick: item.onClick
1352
- },
1353
- item.key
1354
- ))
1355
- }
1356
- )
1357
- );
1358
- }
1359
1369
  function ContextMenu({ items, children }) {
1360
1370
  return /* @__PURE__ */ jsxs(ContextMenuPrimitive.Root, { children: [
1361
1371
  /* @__PURE__ */ jsx(ContextMenuPrimitive.Trigger, { asChild: true, children }),
@@ -1523,6 +1533,7 @@ function Wizard({
1523
1533
  const tooltipRef = useRef(null);
1524
1534
  const tooltipTitleId = useId();
1525
1535
  const tooltipBodyId = useId();
1536
+ const reduced = useReducedMotion();
1526
1537
  const [open, setOpen] = useState(() => steps.length > 0 && !readDismissed(storageKey));
1527
1538
  const [activeIndex, setActiveIndex] = useState(0);
1528
1539
  const step = steps[activeIndex];
@@ -1566,24 +1577,36 @@ function Wizard({
1566
1577
  const isLast = activeIndex === steps.length - 1;
1567
1578
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1568
1579
  children,
1569
- open && step && /* @__PURE__ */ jsxs(Portal, { children: [
1580
+ /* @__PURE__ */ jsx(AnimatePresence, { children: open && step && /* @__PURE__ */ jsxs(Portal, { children: [
1570
1581
  /* @__PURE__ */ jsx(
1571
- "div",
1582
+ motion.div,
1572
1583
  {
1573
1584
  className: "fixed inset-0 z-[7000000] bg-foreground/40 backdrop-blur-[1px] pointer-events-auto",
1585
+ initial: { opacity: 0 },
1586
+ animate: { opacity: 1 },
1587
+ exit: { opacity: 0 },
1588
+ transition: { duration: reduced ? 0 : 0.18, ease: "easeOut" },
1574
1589
  "aria-hidden": "true"
1575
1590
  }
1576
1591
  ),
1577
1592
  /* @__PURE__ */ jsx(
1578
- "div",
1593
+ motion.div,
1579
1594
  {
1580
- className: "fixed z-[7000001] pointer-events-none rounded-md ring-2 ring-accent ring-offset-2 ring-offset-background transition-all duration-200",
1595
+ className: "fixed z-[7000001] pointer-events-none rounded-md ring-2 ring-accent ring-offset-2 ring-offset-background",
1581
1596
  style: highlightStyle,
1597
+ initial: { opacity: 0, scale: 1.08 },
1598
+ animate: { opacity: 1, scale: 1 },
1599
+ exit: { opacity: 0, scale: 1.08 },
1600
+ transition: {
1601
+ duration: reduced ? 0 : 0.32,
1602
+ ease: [0.16, 1, 0.3, 1]
1603
+ // ease-out-expo — settles softly
1604
+ },
1582
1605
  "aria-hidden": "true"
1583
1606
  }
1584
1607
  ),
1585
1608
  /* @__PURE__ */ jsxs(
1586
- "div",
1609
+ motion.div,
1587
1610
  {
1588
1611
  ref: tooltipRef,
1589
1612
  role: "dialog",
@@ -1592,6 +1615,14 @@ function Wizard({
1592
1615
  "aria-describedby": tooltipBodyId,
1593
1616
  className: "fixed z-[7000002] rounded-lg bg-surface text-foreground border border-border shadow-xl p-4 pointer-events-auto",
1594
1617
  style: tooltipStyle,
1618
+ initial: { opacity: 0, scale: 0.96, y: 6 },
1619
+ animate: { opacity: 1, scale: 1, y: 0 },
1620
+ exit: { opacity: 0, scale: 0.97, y: 4 },
1621
+ transition: reduced ? { duration: 0 } : {
1622
+ opacity: { duration: 0.18 },
1623
+ scale: { type: "tween", duration: 0.26, ease: [0.16, 1, 0.3, 1] },
1624
+ y: { type: "tween", duration: 0.26, ease: [0.16, 1, 0.3, 1] }
1625
+ },
1595
1626
  children: [
1596
1627
  step.title && /* @__PURE__ */ jsx("h3", { id: tooltipTitleId, className: "text-sm font-semibold text-foreground mb-1", children: step.title }),
1597
1628
  /* @__PURE__ */ jsx("div", { id: tooltipBodyId, className: "text-sm text-foreground-secondary leading-relaxed", children: step.description }),
@@ -1631,9 +1662,10 @@ function Wizard({
1631
1662
  ] })
1632
1663
  ] })
1633
1664
  ]
1634
- }
1665
+ },
1666
+ activeIndex
1635
1667
  )
1636
- ] })
1668
+ ] }) })
1637
1669
  ] });
1638
1670
  }
1639
1671
  var SearchInput = React8.forwardRef(function SearchInput2({
@@ -1655,7 +1687,7 @@ var SearchInput = React8.forwardRef(function SearchInput2({
1655
1687
  style: style ?? {},
1656
1688
  children: [
1657
1689
  label && /* @__PURE__ */ jsx("label", { className: "text-sm font-medium ml-1 max-content text-foreground", htmlFor, children: label }),
1658
- /* @__PURE__ */ jsxs("div", { className: "bg-surface text-foreground flex items-center gap-1 rounded-lg border border-border pr-2 focus-within:ring-2 focus-within:ring-accent transition-colors", children: [
1690
+ /* @__PURE__ */ jsxs("div", { className: "bg-surface text-foreground flex items-center gap-1 rounded-lg border border-border pr-2 focus-within:border-transparent focus-within:ring-2 focus-within:ring-accent transition-colors", children: [
1659
1691
  /* @__PURE__ */ jsx(
1660
1692
  "input",
1661
1693
  {
@@ -1702,7 +1734,8 @@ function Dropdown({
1702
1734
  htmlFor,
1703
1735
  items = [],
1704
1736
  labelStyle = {},
1705
- placeholder
1737
+ placeholder,
1738
+ showSelectedCount = false
1706
1739
  }) {
1707
1740
  const [open, setOpen] = useState(false);
1708
1741
  const [selectedItems, setSelectedItems] = useState([]);
@@ -1765,7 +1798,7 @@ function Dropdown({
1765
1798
  "aria-invalid": hasError || void 0,
1766
1799
  "aria-describedby": hasError ? errorId : void 0,
1767
1800
  style,
1768
- className: `flex items-center justify-between relative h-9 rounded-lg border border-border cursor-pointer select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${disabled ? "cursor-not-allowed bg-surface-raised text-foreground-muted" : "bg-surface text-foreground"} ${hasError ? "border-status-error" : ""}`,
1801
+ className: `flex items-center justify-between relative h-9 rounded-lg border border-border cursor-pointer select-none focus:outline-none focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-accent ${disabled ? "cursor-not-allowed bg-surface-raised text-foreground-muted" : "bg-surface text-foreground"} ${hasError ? "border-status-error" : ""}`,
1769
1802
  tabIndex: disabled ? -1 : 0,
1770
1803
  onKeyDown: (e) => {
1771
1804
  if (disabled) return;
@@ -1788,7 +1821,7 @@ function Dropdown({
1788
1821
  },
1789
1822
  String(val)
1790
1823
  )),
1791
- value.length > 1 && /* @__PURE__ */ jsx(DropdownPill, { value: `+${value.length - 1} more` })
1824
+ showSelectedCount && value.length > 1 && /* @__PURE__ */ jsx(DropdownPill, { value: `+${value.length - 1} more` })
1792
1825
  ] }) : /* @__PURE__ */ jsx(DropdownPill, { value: innerItems.find((it) => it.key === value)?.label })
1793
1826
  }
1794
1827
  ),
@@ -2587,10 +2620,13 @@ function ThemeProvider({
2587
2620
  ] });
2588
2621
  }
2589
2622
  var SHIMMER = [
2590
- "animate-shimmer rounded-sm",
2591
- "bg-[length:400%_100%]",
2592
- "bg-gradient-to-r",
2593
- "from-border via-border-strong/40 to-border"
2623
+ "relative overflow-hidden rounded-sm bg-surface-raised",
2624
+ 'before:absolute before:inset-0 before:content-[""]',
2625
+ "before:bg-gradient-to-r before:from-transparent before:via-white/30 before:to-transparent",
2626
+ "before:animate-[shimmer_1.6s_linear_infinite]",
2627
+ // Respect prefers-reduced-motion — the resting bg-surface-raised is still
2628
+ // a perfectly legible placeholder for users who have animations off.
2629
+ "motion-reduce:before:hidden"
2594
2630
  ].join(" ");
2595
2631
  function SkeletonBox({ width, height = 16, radius, className = "", style }) {
2596
2632
  return /* @__PURE__ */ jsx(
@@ -2728,7 +2764,7 @@ function TextInput({
2728
2764
  id: htmlFor,
2729
2765
  "aria-invalid": hasError || void 0,
2730
2766
  "aria-describedby": hasError ? errorId : void 0,
2731
- className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-60 mt-1 rounded-lg disabled:bg-surface-raised disabled:text-foreground-muted disabled:cursor-not-allowed focus:outline-none focus-visible:ring-2 focus-visible:ring-accent transition-colors`,
2767
+ className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-60 mt-1 rounded-lg disabled:bg-surface-raised disabled:text-foreground-muted disabled:cursor-not-allowed focus:outline-none focus:border-transparent focus:ring-2 focus:ring-accent transition-colors`,
2732
2768
  style: inputStyle ?? {},
2733
2769
  placeholder: placeholder ?? ""
2734
2770
  }
@@ -2804,7 +2840,7 @@ function NumberInput({
2804
2840
  "div",
2805
2841
  {
2806
2842
  style,
2807
- className: `flex items-center rounded-lg border overflow-hidden ${hasError ? "border-status-error" : "border-border"} ${disabled ? "bg-surface-raised text-foreground-muted cursor-not-allowed" : "bg-surface text-foreground"} focus-within:ring-2 focus-within:ring-accent transition-colors`,
2843
+ className: `flex items-center rounded-lg border overflow-hidden ${hasError ? "border-status-error" : "border-border"} ${disabled ? "bg-surface-raised text-foreground-muted cursor-not-allowed" : "bg-surface text-foreground"} focus-within:border-transparent focus-within:ring-2 focus-within:ring-accent transition-colors`,
2808
2844
  children: [
2809
2845
  /* @__PURE__ */ jsx(
2810
2846
  "input",
@@ -2904,7 +2940,7 @@ function Password({
2904
2940
  id: htmlFor,
2905
2941
  "aria-invalid": hasError || void 0,
2906
2942
  "aria-describedby": hasError ? errorId : void 0,
2907
- className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-52 mt-1 rounded-lg disabled:bg-surface-raised disabled:text-foreground-muted disabled:cursor-not-allowed focus:outline-none focus-visible:ring-2 focus-visible:ring-accent transition-colors`,
2943
+ className: `${hasError ? "border border-status-error" : "border border-border"} bg-surface text-foreground p-2 h-9 w-52 mt-1 rounded-lg disabled:bg-surface-raised disabled:text-foreground-muted disabled:cursor-not-allowed focus:outline-none focus:border-transparent focus:ring-2 focus:ring-accent transition-colors`,
2908
2944
  style: inputStyle ?? {},
2909
2945
  placeholder: placeholder ?? ""
2910
2946
  }
@@ -3071,7 +3107,7 @@ function AutoComplete({
3071
3107
  children: [
3072
3108
  label && /* @__PURE__ */ jsx("label", { className: "text-sm font-medium ml-1 max-content text-foreground", children: label }),
3073
3109
  /* @__PURE__ */ jsxs(Popover.Root, { open: open && !disabled, onOpenChange: (o) => !disabled && setOpen(o), children: [
3074
- /* @__PURE__ */ jsx(Popover.Anchor, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "bg-surface text-foreground flex items-center gap-1 rounded-lg border border-border pr-2 focus-within:ring-2 focus-within:ring-accent transition-colors", children: [
3110
+ /* @__PURE__ */ jsx(Popover.Anchor, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "bg-surface text-foreground flex items-center gap-1 rounded-lg border border-border pr-2 focus-within:border-transparent focus-within:ring-2 focus-within:ring-accent transition-colors", children: [
3075
3111
  /* @__PURE__ */ jsx(
3076
3112
  "input",
3077
3113
  {
@@ -3272,7 +3308,7 @@ function TreeSelect({
3272
3308
  "aria-invalid": hasError || void 0,
3273
3309
  "aria-describedby": hasError ? errorId : void 0,
3274
3310
  disabled,
3275
- className: `flex items-center justify-between h-9 rounded-lg border ${hasError ? "border-status-error" : "border-border"} px-3 cursor-pointer select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${disabled ? "cursor-not-allowed bg-surface-raised text-foreground-muted" : "bg-surface text-foreground"} ${!style?.width ? "min-w-[240px]" : ""}`,
3311
+ className: `flex items-center justify-between h-9 rounded-lg border ${hasError ? "border-status-error" : "border-border"} px-3 cursor-pointer select-none focus:outline-none focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-accent ${disabled ? "cursor-not-allowed bg-surface-raised text-foreground-muted" : "bg-surface text-foreground"} ${!style?.width ? "min-w-[240px]" : ""}`,
3276
3312
  children: [
3277
3313
  /* @__PURE__ */ jsx("span", { className: "text-sm truncate text-left", children: selectedNode ? selectedNode.label : /* @__PURE__ */ jsx("span", { className: "text-foreground-muted", children: placeholder }) }),
3278
3314
  /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: `h-4 w-4 flex-shrink-0 transition-transform duration-200 ${open ? "rotate-180" : ""}`, "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
@@ -3565,12 +3601,14 @@ function DatePicker({
3565
3601
  const [open, setOpen] = useState(false);
3566
3602
  const [viewMonth, setViewMonth] = useState(() => startOfMonth(value ?? /* @__PURE__ */ new Date()));
3567
3603
  const [focusDate, setFocusDate] = useState(() => value ?? /* @__PURE__ */ new Date());
3604
+ const [view, setView] = useState("days");
3568
3605
  const gridRef = useRef(null);
3569
3606
  useEffect(() => {
3570
3607
  if (!open) return;
3571
3608
  const target = value ?? /* @__PURE__ */ new Date();
3572
3609
  setViewMonth(startOfMonth(target));
3573
3610
  setFocusDate(target);
3611
+ setView("days");
3574
3612
  }, [open, value]);
3575
3613
  useEffect(() => {
3576
3614
  if (!open) return;
@@ -3659,7 +3697,7 @@ function DatePicker({
3659
3697
  "aria-describedby": hasError ? errorId : void 0,
3660
3698
  "aria-haspopup": "dialog",
3661
3699
  "aria-expanded": open,
3662
- className: `flex items-center justify-between h-9 rounded-lg border px-3 cursor-pointer select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${hasError ? "border-status-error" : "border-border"} ${disabled ? "cursor-not-allowed bg-surface-raised text-foreground-muted" : "bg-surface text-foreground"} ${!style?.width ? "min-w-[200px]" : ""}`,
3700
+ className: `flex items-center justify-between h-9 rounded-lg border px-3 cursor-pointer select-none focus:outline-none focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-accent ${hasError ? "border-status-error" : "border-border"} ${disabled ? "cursor-not-allowed bg-surface-raised text-foreground-muted" : "bg-surface text-foreground"} ${!style?.width ? "min-w-[200px]" : ""}`,
3663
3701
  children: [
3664
3702
  /* @__PURE__ */ jsx("span", { className: `text-sm truncate ${displayValue ? "" : "text-foreground-muted"}`, children: displayValue || placeholder }),
3665
3703
  /* @__PURE__ */ jsx(CalendarIcon, {})
@@ -3681,29 +3719,89 @@ function DatePicker({
3681
3719
  "button",
3682
3720
  {
3683
3721
  type: "button",
3684
- onClick: () => setViewMonth(addMonths(viewMonth, -1)),
3685
- "aria-label": "Previous month",
3722
+ onClick: () => {
3723
+ if (view === "days") setViewMonth(addMonths(viewMonth, -1));
3724
+ else if (view === "months") setViewMonth(new Date(viewMonth.getFullYear() - 1, viewMonth.getMonth(), 1));
3725
+ else setViewMonth(new Date(viewMonth.getFullYear() - 10, viewMonth.getMonth(), 1));
3726
+ },
3727
+ "aria-label": view === "days" ? "Previous month" : view === "months" ? "Previous year" : "Previous decade",
3686
3728
  className: "w-7 h-7 inline-flex items-center justify-center rounded-md hover:bg-surface-raised focus:outline-none focus-visible:ring-2 focus-visible:ring-accent transition-colors",
3687
3729
  children: /* @__PURE__ */ jsx(ChevronLeft, {})
3688
3730
  }
3689
3731
  ),
3690
- /* @__PURE__ */ jsxs("div", { className: "text-sm font-semibold select-none", children: [
3691
- MONTH_NAMES[viewMonth.getMonth()],
3692
- " ",
3693
- viewMonth.getFullYear()
3694
- ] }),
3732
+ /* @__PURE__ */ jsxs(
3733
+ "button",
3734
+ {
3735
+ type: "button",
3736
+ onClick: () => {
3737
+ if (view === "days") setView("months");
3738
+ else if (view === "months") setView("years");
3739
+ },
3740
+ disabled: view === "years",
3741
+ "aria-label": "Change view",
3742
+ className: "text-sm font-semibold select-none rounded-md px-2 py-0.5 hover:bg-surface-raised focus:outline-none focus-visible:ring-2 focus-visible:ring-accent transition-colors disabled:cursor-default disabled:hover:bg-transparent",
3743
+ children: [
3744
+ view === "days" && `${MONTH_NAMES[viewMonth.getMonth()]} ${viewMonth.getFullYear()}`,
3745
+ view === "months" && `${viewMonth.getFullYear()}`,
3746
+ view === "years" && (() => {
3747
+ const decadeStart = Math.floor(viewMonth.getFullYear() / 10) * 10;
3748
+ return `${decadeStart} \u2013 ${decadeStart + 11}`;
3749
+ })()
3750
+ ]
3751
+ }
3752
+ ),
3695
3753
  /* @__PURE__ */ jsx(
3696
3754
  "button",
3697
3755
  {
3698
3756
  type: "button",
3699
- onClick: () => setViewMonth(addMonths(viewMonth, 1)),
3700
- "aria-label": "Next month",
3757
+ onClick: () => {
3758
+ if (view === "days") setViewMonth(addMonths(viewMonth, 1));
3759
+ else if (view === "months") setViewMonth(new Date(viewMonth.getFullYear() + 1, viewMonth.getMonth(), 1));
3760
+ else setViewMonth(new Date(viewMonth.getFullYear() + 10, viewMonth.getMonth(), 1));
3761
+ },
3762
+ "aria-label": view === "days" ? "Next month" : view === "months" ? "Next year" : "Next decade",
3701
3763
  className: "w-7 h-7 inline-flex items-center justify-center rounded-md hover:bg-surface-raised focus:outline-none focus-visible:ring-2 focus-visible:ring-accent transition-colors",
3702
3764
  children: /* @__PURE__ */ jsx(ChevronRight3, {})
3703
3765
  }
3704
3766
  )
3705
3767
  ] }),
3706
- /* @__PURE__ */ jsxs(
3768
+ view === "months" && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-1 min-w-[224px]", children: MONTH_NAMES.map((name, idx) => {
3769
+ const isCurrent = value && value.getFullYear() === viewMonth.getFullYear() && value.getMonth() === idx;
3770
+ return /* @__PURE__ */ jsx(
3771
+ "button",
3772
+ {
3773
+ type: "button",
3774
+ onClick: () => {
3775
+ setViewMonth(new Date(viewMonth.getFullYear(), idx, 1));
3776
+ setView("days");
3777
+ },
3778
+ className: `px-2 py-2 rounded-md text-xs font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${isCurrent ? "bg-accent text-accent-fg" : "text-foreground hover:bg-surface-raised"}`,
3779
+ children: name.slice(0, 3)
3780
+ },
3781
+ name
3782
+ );
3783
+ }) }),
3784
+ view === "years" && (() => {
3785
+ const decadeStart = Math.floor(viewMonth.getFullYear() / 10) * 10;
3786
+ const years = Array.from({ length: 12 }, (_, i) => decadeStart + i);
3787
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-1 min-w-[224px]", children: years.map((y) => {
3788
+ const isCurrent = value?.getFullYear() === y;
3789
+ return /* @__PURE__ */ jsx(
3790
+ "button",
3791
+ {
3792
+ type: "button",
3793
+ onClick: () => {
3794
+ setViewMonth(new Date(y, viewMonth.getMonth(), 1));
3795
+ setView("months");
3796
+ },
3797
+ className: `px-2 py-2 rounded-md text-xs font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${isCurrent ? "bg-accent text-accent-fg" : "text-foreground hover:bg-surface-raised"}`,
3798
+ children: y
3799
+ },
3800
+ y
3801
+ );
3802
+ }) });
3803
+ })(),
3804
+ view === "days" && /* @__PURE__ */ jsxs(
3707
3805
  "table",
3708
3806
  {
3709
3807
  ref: gridRef,
@@ -3785,6 +3883,6 @@ function ChevronRight3() {
3785
3883
  return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
3786
3884
  }
3787
3885
 
3788
- export { AppShell, AutoComplete, Button, Catalog, CatalogCarousel, CatalogGrid, Checkbox, ContextMenu, Drawer, Dropdown, DropdownPill, FadingBase, FileInput, GridCard, icons_default as Icon, IconButton, List2 as List, LoadingSpinner, MenuBar, MenuBarItem, Modal, NotificationProvider, NumberInput, OpaqueGridCard, Password, Portal, ScalableContainer, SearchInput_default as SearchInput, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, Switch, Table, Tabs, DatePicker as Temporal, TextInput, ThemeProvider, ThemeSwitch, ToggleButton, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Wizard, useNotification };
3886
+ export { AppShell, AutoComplete, Button, Catalog, CatalogCarousel, CatalogGrid, Checkbox, ContextMenu, Drawer, Dropdown, FadingBase, FileInput, GridCard, icons_default as Icon, IconButton, List2 as List, LoadingSpinner, Modal, NotificationProvider, NumberInput, OpaqueGridCard, Password, Portal, ScalableContainer, SearchInput_default as SearchInput, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, Switch, Table, Tabs, DatePicker as Temporal, TextInput, ThemeProvider, ThemeSwitch, ToggleButton, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Wizard, useNotification };
3789
3887
  //# sourceMappingURL=index.js.map
3790
3888
  //# sourceMappingURL=index.js.map