@geomak/ui 5.0.2 → 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
@@ -967,6 +967,9 @@ function useNotification() {
967
967
  };
968
968
  }
969
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]" },
970
973
  sm: { outer: "w-8 h-8", inner: "w-4 h-4", dot: "w-1 h-1", stroke: "border-2", text: "text-xs" },
971
974
  md: { outer: "w-20 h-20", inner: "w-12 h-12", dot: "w-2 h-2", stroke: "border-[3px]", text: "text-2xl" },
972
975
  lg: { outer: "w-32 h-32", inner: "w-20 h-20", dot: "w-3 h-3", stroke: "border-4", text: "text-4xl" }
@@ -1127,53 +1130,82 @@ function List2({ items, onItemClick, activeKey }) {
1127
1130
  )
1128
1131
  )) });
1129
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
+ };
1130
1139
  function ScalableContainer({
1131
- width,
1132
- height,
1140
+ width = "100%",
1141
+ height = "auto",
1133
1142
  children,
1134
- assignClassOnClick
1143
+ assignClassOnClick,
1144
+ expandIcon,
1145
+ collapseIcon,
1146
+ togglePosition = "top-right"
1135
1147
  }) {
1136
1148
  const containerRef = useRef(null);
1137
1149
  const [isScaled, setScaled] = useState(false);
1138
- const [wrapperClass, setWrapperClass] = useState("");
1139
- const onClick = () => {
1150
+ const reduced = useReducedMotion();
1151
+ const onToggle = () => {
1140
1152
  const next = !isScaled;
1141
1153
  setScaled(next);
1142
- setTimeout(() => {
1143
- containerRef.current?.scrollIntoView({ behavior: "smooth" });
1144
- if (assignClassOnClick) {
1145
- setWrapperClass(next ? assignClassOnClick : "");
1146
- }
1147
- }, 200);
1154
+ requestAnimationFrame(() => containerRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" }));
1148
1155
  };
1156
+ const wrapperClass = isScaled ? assignClassOnClick : void 0;
1149
1157
  return /* @__PURE__ */ jsxs(
1150
- "div",
1158
+ motion.div,
1151
1159
  {
1152
1160
  ref: containerRef,
1153
- style: {
1161
+ layout: true,
1162
+ animate: {
1154
1163
  width: isScaled ? "100%" : width,
1155
1164
  height: isScaled ? "100%" : height
1156
1165
  },
1157
- 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(" "),
1158
1178
  children: [
1159
- /* @__PURE__ */ jsx("div", { className: "p-2 w-max", children: /* @__PURE__ */ jsx(Tooltip, { placement: "right", title: isScaled ? "Collapse" : "Expand", children: /* @__PURE__ */ jsx(
1160
- IconButton,
1179
+ /* @__PURE__ */ jsx(Tooltip, { placement: "bottom", title: isScaled ? "Collapse" : "Expand", children: /* @__PURE__ */ jsx(
1180
+ "button",
1161
1181
  {
1162
- onClick,
1163
- icon: isScaled ? (
1164
- /* Collapse (arrows-pointing-in) */
1165
- /* @__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" }) })
1166
- ) : (
1167
- /* Expand (arrows-pointing-out) */
1168
- /* @__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" }) })
1169
- )
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, {})
1170
1196
  }
1171
- ) }) }),
1197
+ ) }),
1172
1198
  /* @__PURE__ */ jsx("div", { className: wrapperClass, children })
1173
1199
  ]
1174
1200
  }
1175
1201
  );
1176
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
+ }
1177
1209
  function GridCard({ item, buttonText = "Open Application", onOpen }) {
1178
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: [
1179
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 }) }),
@@ -1334,49 +1366,6 @@ function CatalogCarousel({ items, buttonText, onOpen }) {
1334
1366
  function Catalog({ display = "grid", items = [], buttonText, onOpen }) {
1335
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 }) });
1336
1368
  }
1337
- function MenuBarItem({ icon, isActive, title, onClick }) {
1338
- return /* @__PURE__ */ jsx(Tooltip, { title, placement: "right", children: /* @__PURE__ */ jsx(
1339
- "div",
1340
- {
1341
- role: "button",
1342
- "aria-label": title,
1343
- "aria-current": isActive ? "page" : void 0,
1344
- 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`,
1345
- onClick,
1346
- tabIndex: 0,
1347
- onKeyDown: (e) => {
1348
- if (e.key === "Enter" || e.key === " ") {
1349
- e.preventDefault();
1350
- onClick?.();
1351
- }
1352
- },
1353
- children: icon
1354
- }
1355
- ) });
1356
- }
1357
- function MenuBar({ items }) {
1358
- return (
1359
- // `calculated-height` was an orphaned CSS class. Replaced with `h-full`
1360
- // so the MenuBar fills whatever vertical space its parent gives it.
1361
- /* @__PURE__ */ jsx(
1362
- "nav",
1363
- {
1364
- "aria-label": "Main navigation",
1365
- 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",
1366
- children: items.map((item) => /* @__PURE__ */ jsx(
1367
- MenuBarItem,
1368
- {
1369
- icon: item.icon,
1370
- title: item.title,
1371
- isActive: item.isActive,
1372
- onClick: item.onClick
1373
- },
1374
- item.key
1375
- ))
1376
- }
1377
- )
1378
- );
1379
- }
1380
1369
  function ContextMenu({ items, children }) {
1381
1370
  return /* @__PURE__ */ jsxs(ContextMenuPrimitive.Root, { children: [
1382
1371
  /* @__PURE__ */ jsx(ContextMenuPrimitive.Trigger, { asChild: true, children }),
@@ -1698,7 +1687,7 @@ var SearchInput = React8.forwardRef(function SearchInput2({
1698
1687
  style: style ?? {},
1699
1688
  children: [
1700
1689
  label && /* @__PURE__ */ jsx("label", { className: "text-sm font-medium ml-1 max-content text-foreground", htmlFor, children: label }),
1701
- /* @__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: [
1702
1691
  /* @__PURE__ */ jsx(
1703
1692
  "input",
1704
1693
  {
@@ -1745,7 +1734,8 @@ function Dropdown({
1745
1734
  htmlFor,
1746
1735
  items = [],
1747
1736
  labelStyle = {},
1748
- placeholder
1737
+ placeholder,
1738
+ showSelectedCount = false
1749
1739
  }) {
1750
1740
  const [open, setOpen] = useState(false);
1751
1741
  const [selectedItems, setSelectedItems] = useState([]);
@@ -1808,7 +1798,7 @@ function Dropdown({
1808
1798
  "aria-invalid": hasError || void 0,
1809
1799
  "aria-describedby": hasError ? errorId : void 0,
1810
1800
  style,
1811
- 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" : ""}`,
1812
1802
  tabIndex: disabled ? -1 : 0,
1813
1803
  onKeyDown: (e) => {
1814
1804
  if (disabled) return;
@@ -1831,7 +1821,7 @@ function Dropdown({
1831
1821
  },
1832
1822
  String(val)
1833
1823
  )),
1834
- value.length > 1 && /* @__PURE__ */ jsx(DropdownPill, { value: `+${value.length - 1} more` })
1824
+ showSelectedCount && value.length > 1 && /* @__PURE__ */ jsx(DropdownPill, { value: `+${value.length - 1} more` })
1835
1825
  ] }) : /* @__PURE__ */ jsx(DropdownPill, { value: innerItems.find((it) => it.key === value)?.label })
1836
1826
  }
1837
1827
  ),
@@ -2630,10 +2620,13 @@ function ThemeProvider({
2630
2620
  ] });
2631
2621
  }
2632
2622
  var SHIMMER = [
2633
- "animate-shimmer rounded-sm",
2634
- "bg-[length:400%_100%]",
2635
- "bg-gradient-to-r",
2636
- "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"
2637
2630
  ].join(" ");
2638
2631
  function SkeletonBox({ width, height = 16, radius, className = "", style }) {
2639
2632
  return /* @__PURE__ */ jsx(
@@ -2771,7 +2764,7 @@ function TextInput({
2771
2764
  id: htmlFor,
2772
2765
  "aria-invalid": hasError || void 0,
2773
2766
  "aria-describedby": hasError ? errorId : void 0,
2774
- 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`,
2775
2768
  style: inputStyle ?? {},
2776
2769
  placeholder: placeholder ?? ""
2777
2770
  }
@@ -2847,7 +2840,7 @@ function NumberInput({
2847
2840
  "div",
2848
2841
  {
2849
2842
  style,
2850
- 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`,
2851
2844
  children: [
2852
2845
  /* @__PURE__ */ jsx(
2853
2846
  "input",
@@ -2947,7 +2940,7 @@ function Password({
2947
2940
  id: htmlFor,
2948
2941
  "aria-invalid": hasError || void 0,
2949
2942
  "aria-describedby": hasError ? errorId : void 0,
2950
- 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`,
2951
2944
  style: inputStyle ?? {},
2952
2945
  placeholder: placeholder ?? ""
2953
2946
  }
@@ -3114,7 +3107,7 @@ function AutoComplete({
3114
3107
  children: [
3115
3108
  label && /* @__PURE__ */ jsx("label", { className: "text-sm font-medium ml-1 max-content text-foreground", children: label }),
3116
3109
  /* @__PURE__ */ jsxs(Popover.Root, { open: open && !disabled, onOpenChange: (o) => !disabled && setOpen(o), children: [
3117
- /* @__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: [
3118
3111
  /* @__PURE__ */ jsx(
3119
3112
  "input",
3120
3113
  {
@@ -3315,7 +3308,7 @@ function TreeSelect({
3315
3308
  "aria-invalid": hasError || void 0,
3316
3309
  "aria-describedby": hasError ? errorId : void 0,
3317
3310
  disabled,
3318
- 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]" : ""}`,
3319
3312
  children: [
3320
3313
  /* @__PURE__ */ jsx("span", { className: "text-sm truncate text-left", children: selectedNode ? selectedNode.label : /* @__PURE__ */ jsx("span", { className: "text-foreground-muted", children: placeholder }) }),
3321
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" }) })
@@ -3608,12 +3601,14 @@ function DatePicker({
3608
3601
  const [open, setOpen] = useState(false);
3609
3602
  const [viewMonth, setViewMonth] = useState(() => startOfMonth(value ?? /* @__PURE__ */ new Date()));
3610
3603
  const [focusDate, setFocusDate] = useState(() => value ?? /* @__PURE__ */ new Date());
3604
+ const [view, setView] = useState("days");
3611
3605
  const gridRef = useRef(null);
3612
3606
  useEffect(() => {
3613
3607
  if (!open) return;
3614
3608
  const target = value ?? /* @__PURE__ */ new Date();
3615
3609
  setViewMonth(startOfMonth(target));
3616
3610
  setFocusDate(target);
3611
+ setView("days");
3617
3612
  }, [open, value]);
3618
3613
  useEffect(() => {
3619
3614
  if (!open) return;
@@ -3702,7 +3697,7 @@ function DatePicker({
3702
3697
  "aria-describedby": hasError ? errorId : void 0,
3703
3698
  "aria-haspopup": "dialog",
3704
3699
  "aria-expanded": open,
3705
- 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]" : ""}`,
3706
3701
  children: [
3707
3702
  /* @__PURE__ */ jsx("span", { className: `text-sm truncate ${displayValue ? "" : "text-foreground-muted"}`, children: displayValue || placeholder }),
3708
3703
  /* @__PURE__ */ jsx(CalendarIcon, {})
@@ -3724,29 +3719,89 @@ function DatePicker({
3724
3719
  "button",
3725
3720
  {
3726
3721
  type: "button",
3727
- onClick: () => setViewMonth(addMonths(viewMonth, -1)),
3728
- "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",
3729
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",
3730
3729
  children: /* @__PURE__ */ jsx(ChevronLeft, {})
3731
3730
  }
3732
3731
  ),
3733
- /* @__PURE__ */ jsxs("div", { className: "text-sm font-semibold select-none", children: [
3734
- MONTH_NAMES[viewMonth.getMonth()],
3735
- " ",
3736
- viewMonth.getFullYear()
3737
- ] }),
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
+ ),
3738
3753
  /* @__PURE__ */ jsx(
3739
3754
  "button",
3740
3755
  {
3741
3756
  type: "button",
3742
- onClick: () => setViewMonth(addMonths(viewMonth, 1)),
3743
- "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",
3744
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",
3745
3764
  children: /* @__PURE__ */ jsx(ChevronRight3, {})
3746
3765
  }
3747
3766
  )
3748
3767
  ] }),
3749
- /* @__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(
3750
3805
  "table",
3751
3806
  {
3752
3807
  ref: gridRef,
@@ -3828,6 +3883,6 @@ function ChevronRight3() {
3828
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" }) });
3829
3884
  }
3830
3885
 
3831
- 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 };
3832
3887
  //# sourceMappingURL=index.js.map
3833
3888
  //# sourceMappingURL=index.js.map