react-os-shell 4.63.0 → 4.67.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 (50) hide show
  1. package/README.md +1 -0
  2. package/dist/{Browser-SPTNLEMR.js → Browser-NBYPPENQ.js} +6 -6
  3. package/dist/{Browser-SPTNLEMR.js.map → Browser-NBYPPENQ.js.map} +1 -1
  4. package/dist/{Calculator-XMOPPCK3.js → Calculator-UNAVT7IE.js} +5 -5
  5. package/dist/{Calculator-XMOPPCK3.js.map → Calculator-UNAVT7IE.js.map} +1 -1
  6. package/dist/ConfirmDialog-SREDNTIB.js +3 -0
  7. package/dist/{ConfirmDialog-EEH4CWSW.js.map → ConfirmDialog-SREDNTIB.js.map} +1 -1
  8. package/dist/{CurrencyConverter-24HNX6C6.js → CurrencyConverter-KURXJHLK.js} +5 -5
  9. package/dist/{CurrencyConverter-24HNX6C6.js.map → CurrencyConverter-KURXJHLK.js.map} +1 -1
  10. package/dist/{Documents-GMRVVCH7.js → Documents-7CUBCETC.js} +6 -6
  11. package/dist/{Documents-GMRVVCH7.js.map → Documents-7CUBCETC.js.map} +1 -1
  12. package/dist/{Files-ITH4SVML.js → Files-UXEDRQF4.js} +6 -6
  13. package/dist/{Files-ITH4SVML.js.map → Files-UXEDRQF4.js.map} +1 -1
  14. package/dist/{Notepad-GE5P55TL.js → Notepad-ON4T45UP.js} +6 -6
  15. package/dist/{Notepad-GE5P55TL.js.map → Notepad-ON4T45UP.js.map} +1 -1
  16. package/dist/{PomodoroTimer-ER5STVKM.js → PomodoroTimer-J7V5VXR4.js} +5 -5
  17. package/dist/{PomodoroTimer-ER5STVKM.js.map → PomodoroTimer-J7V5VXR4.js.map} +1 -1
  18. package/dist/{Preview-UXVDXCDJ.js → Preview-QINOPDT2.js} +6 -6
  19. package/dist/{Preview-UXVDXCDJ.js.map → Preview-QINOPDT2.js.map} +1 -1
  20. package/dist/{Spreadsheet-B32JQVX4.js → Spreadsheet-OFF3J4HN.js} +6 -6
  21. package/dist/{Spreadsheet-B32JQVX4.js.map → Spreadsheet-OFF3J4HN.js.map} +1 -1
  22. package/dist/{Stock-FSFPFDQT.js → Stock-BSU7S7MW.js} +5 -5
  23. package/dist/{Stock-FSFPFDQT.js.map → Stock-BSU7S7MW.js.map} +1 -1
  24. package/dist/{Weather-HXOERJJV.js → Weather-BKNRVMG6.js} +5 -5
  25. package/dist/{Weather-HXOERJJV.js.map → Weather-BKNRVMG6.js.map} +1 -1
  26. package/dist/{WorldClock-2FUGLIUZ.js → WorldClock-VMTD2QWD.js} +5 -5
  27. package/dist/{WorldClock-2FUGLIUZ.js.map → WorldClock-VMTD2QWD.js.map} +1 -1
  28. package/dist/apps/index.js +16 -16
  29. package/dist/{chunk-CWA27IGB.js → chunk-B3IYV7JN.js} +588 -274
  30. package/dist/chunk-B3IYV7JN.js.map +1 -0
  31. package/dist/{chunk-OXLLRKBC.js → chunk-CDR2Q64Q.js} +38 -22
  32. package/dist/chunk-CDR2Q64Q.js.map +1 -0
  33. package/dist/{chunk-LYQVLRWQ.js → chunk-E2N5WEHI.js} +3 -3
  34. package/dist/{chunk-LYQVLRWQ.js.map → chunk-E2N5WEHI.js.map} +1 -1
  35. package/dist/{chunk-JMESET3G.js → chunk-GRSVL6WX.js} +4 -4
  36. package/dist/{chunk-JMESET3G.js.map → chunk-GRSVL6WX.js.map} +1 -1
  37. package/dist/{chunk-TO6ZZHJK.js → chunk-HAFM6ZQD.js} +4 -4
  38. package/dist/{chunk-TO6ZZHJK.js.map → chunk-HAFM6ZQD.js.map} +1 -1
  39. package/dist/chunk-K35QFOJF.js +7 -0
  40. package/dist/{chunk-WCGHIJZR.js.map → chunk-K35QFOJF.js.map} +1 -1
  41. package/dist/{index-Ka32s3Ij.d.ts → index-DM-d12A7.d.ts} +88 -22
  42. package/dist/index.d.ts +2 -2
  43. package/dist/index.js +8 -8
  44. package/dist/ui/index.d.ts +1 -1
  45. package/dist/ui/index.js +3 -3
  46. package/package.json +1 -1
  47. package/dist/ConfirmDialog-EEH4CWSW.js +0 -3
  48. package/dist/chunk-CWA27IGB.js.map +0 -1
  49. package/dist/chunk-OXLLRKBC.js.map +0 -1
  50. package/dist/chunk-WCGHIJZR.js +0 -7
@@ -1,7 +1,7 @@
1
1
  import { Breadcrumbs } from './chunk-HNH7FOYB.js';
2
2
  import { SidebarLayout } from './chunk-YG45DAUT.js';
3
3
  import { useIsMobile, PopupMenu, PopupMenuItem, useShellPrefs } from './chunk-ZCDFNZQS.js';
4
- import { registerModalEscapeInterceptor, useFocusTrap, useScrollLock } from './chunk-OXLLRKBC.js';
4
+ import { registerModalEscapeInterceptor, useFocusTrap, useScrollLock } from './chunk-CDR2Q64Q.js';
5
5
  import { glassStyle } from './chunk-3CUSBZWG.js';
6
6
  import { forwardRef, useId, useCallback, useState, useRef, useEffect, createContext, useLayoutEffect, useMemo, Children, isValidElement, cloneElement, useContext, Fragment as Fragment$1, Component, useSyncExternalStore } from 'react';
7
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -164,7 +164,7 @@ function inputClasses(opts) {
164
164
  return [shape, opts?.invalid ? INPUT_INVALID : "", INPUT_DISABLED, opts?.className ?? ""].filter(Boolean).join(" ");
165
165
  }
166
166
  var Input = forwardRef(function Input2({ invalid, leftIcon, rightAdornment, size, className = "", ...rest }, ref) {
167
- const pad4 = `${leftIcon ? "pl-9" : ""} ${rightAdornment ? "pr-9" : ""}`.trim();
167
+ const pad5 = `${leftIcon ? "pl-9" : ""} ${rightAdornment ? "pr-9" : ""}`.trim();
168
168
  const field = (
169
169
  // `inputMode` needs no plumbing — it rides the native spread onto the
170
170
  // <input>, which is how a till sets inputMode="none" to keep the OS
@@ -174,7 +174,7 @@ var Input = forwardRef(function Input2({ invalid, leftIcon, rightAdornment, size
174
174
  {
175
175
  ref,
176
176
  "aria-invalid": invalid || void 0,
177
- className: inputClasses({ invalid, size, className: [pad4, className].filter(Boolean).join(" ") }),
177
+ className: inputClasses({ invalid, size, className: [pad5, className].filter(Boolean).join(" ") }),
178
178
  ...rest
179
179
  }
180
180
  )
@@ -282,7 +282,8 @@ function useAnchoredPosition(triggerRef, open) {
282
282
  const placeAbove = spaceBelow < Math.min(MENU_MAX_HEIGHT, 160) && spaceAbove > spaceBelow;
283
283
  const maxHeight = Math.max(96, Math.min(MENU_MAX_HEIGHT, placeAbove ? spaceAbove : spaceBelow));
284
284
  const left = Math.max(VIEWPORT_MARGIN, Math.min(rect.left, window.innerWidth - rect.width - VIEWPORT_MARGIN));
285
- const next = { left, minWidth: rect.width, maxHeight };
285
+ const width = Math.min(rect.width, window.innerWidth - 2 * VIEWPORT_MARGIN);
286
+ const next = { left, width, maxHeight };
286
287
  if (placeAbove) next.bottom = window.innerHeight - rect.top + MENU_GAP;
287
288
  else next.top = rect.bottom + MENU_GAP;
288
289
  setPos(next);
@@ -497,12 +498,12 @@ var ListboxSelect = forwardRef(function ListboxSelect2({
497
498
  ref: listRef,
498
499
  id: listboxId,
499
500
  role: "listbox",
500
- className: "fixed z-[400] overflow-y-auto rounded-2xl py-1",
501
+ className: "fixed z-[10000] overflow-y-auto rounded-2xl py-1",
501
502
  style: {
502
503
  left: menuPos?.left,
503
504
  top: menuPos?.top,
504
505
  bottom: menuPos?.bottom,
505
- minWidth: menuPos?.minWidth,
506
+ width: menuPos?.width,
506
507
  maxHeight: menuPos?.maxHeight ?? MENU_MAX_HEIGHT,
507
508
  visibility: menuPos ? void 0 : "hidden",
508
509
  ...glassStyle()
@@ -1098,6 +1099,273 @@ function MediaUploadGrid({
1098
1099
  }
1099
1100
  ) });
1100
1101
  }
1102
+ var MONTH_NAMES = [
1103
+ "January",
1104
+ "February",
1105
+ "March",
1106
+ "April",
1107
+ "May",
1108
+ "June",
1109
+ "July",
1110
+ "August",
1111
+ "September",
1112
+ "October",
1113
+ "November",
1114
+ "December"
1115
+ ];
1116
+ var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
1117
+ var DAY_SHORT = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
1118
+ var pad = (n) => String(n).padStart(2, "0");
1119
+ function toKey(d) {
1120
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
1121
+ }
1122
+ function fromKey(key) {
1123
+ if (!key) return null;
1124
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(key);
1125
+ if (!m) return null;
1126
+ const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
1127
+ return Number.isNaN(d.getTime()) ? null : d;
1128
+ }
1129
+ var monthKey = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}`;
1130
+ var YEARS_PER_PAGE = 12;
1131
+ var yearPageStart = (y) => Math.floor(y / YEARS_PER_PAGE) * YEARS_PER_PAGE;
1132
+ function monthStart(key) {
1133
+ const m = /^(\d{4})-(\d{2})$/.exec(key);
1134
+ return m ? new Date(Number(m[1]), Number(m[2]) - 1, 1) : /* @__PURE__ */ new Date();
1135
+ }
1136
+ function buildCells(view, weekStartsOn) {
1137
+ const first = new Date(view.getFullYear(), view.getMonth(), 1);
1138
+ const lead = (first.getDay() - weekStartsOn + 7) % 7;
1139
+ const start = new Date(first.getFullYear(), first.getMonth(), 1 - lead);
1140
+ return Array.from({ length: 42 }, (_, i) => new Date(start.getFullYear(), start.getMonth(), start.getDate() + i));
1141
+ }
1142
+ var addDays = (d, n) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
1143
+ var addMonths = (d, n) => {
1144
+ const target = new Date(d.getFullYear(), d.getMonth() + n, 1);
1145
+ const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
1146
+ return new Date(target.getFullYear(), target.getMonth(), Math.min(d.getDate(), lastDay));
1147
+ };
1148
+ function Calendar({
1149
+ value,
1150
+ endValue,
1151
+ mode = "single",
1152
+ onSelect,
1153
+ min,
1154
+ max,
1155
+ month,
1156
+ defaultMonth,
1157
+ onMonthChange,
1158
+ weekStartsOn = 0,
1159
+ quickJump = true,
1160
+ "aria-label": ariaLabel,
1161
+ className = ""
1162
+ }) {
1163
+ const selected = fromKey(value);
1164
+ const today = /* @__PURE__ */ new Date();
1165
+ const todayKey = toKey(today);
1166
+ const [innerMonth, setInnerMonth] = useState(() => {
1167
+ if (defaultMonth) return defaultMonth;
1168
+ if (selected) return monthKey(selected);
1169
+ const here = monthKey(today);
1170
+ if (min && here < min.slice(0, 7)) return min.slice(0, 7);
1171
+ if (max && here > max.slice(0, 7)) return max.slice(0, 7);
1172
+ return here;
1173
+ });
1174
+ const viewMonth = month ?? innerMonth;
1175
+ const view = monthStart(viewMonth);
1176
+ const [active, setActive] = useState(() => value ?? todayKey);
1177
+ const gridRef = useRef(null);
1178
+ const shouldFocus = useRef(false);
1179
+ const [panel, setPanel] = useState("days");
1180
+ const cells = useMemo(() => buildCells(view, weekStartsOn), [viewMonth, weekStartsOn]);
1181
+ const outOfRange = (key) => !!min && key < min || !!max && key > max;
1182
+ const activeInView = cells.some((c) => toKey(c) === active);
1183
+ const fallbackActive = toKey(cells.find((c) => c.getMonth() === view.getMonth()) ?? cells[0]);
1184
+ const activeKey = activeInView ? active : fallbackActive;
1185
+ useEffect(() => {
1186
+ if (!shouldFocus.current) return;
1187
+ shouldFocus.current = false;
1188
+ gridRef.current?.querySelector('[data-active="true"]')?.focus();
1189
+ }, [activeKey, viewMonth]);
1190
+ const pageBy = (n) => {
1191
+ if (panel === "years") return new Date(view.getFullYear() + n * YEARS_PER_PAGE, view.getMonth(), 1);
1192
+ if (panel === "months") return new Date(view.getFullYear() + n, view.getMonth(), 1);
1193
+ return addMonths(view, n);
1194
+ };
1195
+ const pageBack = () => pageBy(-1);
1196
+ const pageForward = () => pageBy(1);
1197
+ const goToMonth = (next) => {
1198
+ const key = monthKey(next);
1199
+ if (month === void 0) setInnerMonth(key);
1200
+ onMonthChange?.(key);
1201
+ };
1202
+ const moveTo = (next) => {
1203
+ shouldFocus.current = true;
1204
+ setActive(toKey(next));
1205
+ if (monthKey(next) !== viewMonth) goToMonth(next);
1206
+ };
1207
+ const onKeyDown = (e) => {
1208
+ const current = fromKey(activeKey) ?? today;
1209
+ const step = {
1210
+ ArrowLeft: () => addDays(current, -1),
1211
+ ArrowRight: () => addDays(current, 1),
1212
+ ArrowUp: () => addDays(current, -7),
1213
+ ArrowDown: () => addDays(current, 7),
1214
+ // Home and End are the week, not the month: the grid's rows are weeks, and
1215
+ // this is what the ARIA date-grid pattern asks for.
1216
+ Home: () => addDays(current, -((current.getDay() - weekStartsOn + 7) % 7)),
1217
+ End: () => addDays(current, 6 - (current.getDay() - weekStartsOn + 7) % 7),
1218
+ PageUp: () => addMonths(current, e.shiftKey ? -12 : -1),
1219
+ PageDown: () => addMonths(current, e.shiftKey ? 12 : 1)
1220
+ };
1221
+ if (step[e.key]) {
1222
+ e.preventDefault();
1223
+ moveTo(step[e.key]());
1224
+ return;
1225
+ }
1226
+ if (e.key === "Enter" || e.key === " ") {
1227
+ e.preventDefault();
1228
+ if (!outOfRange(activeKey)) onSelect(activeKey);
1229
+ }
1230
+ };
1231
+ const inRange = (key) => {
1232
+ if (mode !== "range" || !value || !endValue) return false;
1233
+ return key > value && key < endValue;
1234
+ };
1235
+ const label = ariaLabel ?? `${MONTH_NAMES[view.getMonth()]} ${view.getFullYear()}`;
1236
+ return /* @__PURE__ */ jsxs("div", { className, children: [
1237
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between px-1", children: [
1238
+ /* @__PURE__ */ jsx(
1239
+ "button",
1240
+ {
1241
+ type: "button",
1242
+ "aria-label": panel === "years" ? "Previous years" : panel === "months" ? "Previous year" : "Previous month",
1243
+ onClick: () => goToMonth(pageBack()),
1244
+ className: "rounded-full p-1 text-gray-600 hover:bg-gray-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400",
1245
+ children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 19l-7-7 7-7" }) })
1246
+ }
1247
+ ),
1248
+ /* @__PURE__ */ jsx("div", { "aria-live": "polite", className: "flex items-center gap-1 text-sm font-semibold text-gray-800", children: quickJump ? /* @__PURE__ */ jsxs(Fragment, { children: [
1249
+ panel !== "years" && /* @__PURE__ */ jsx(
1250
+ "button",
1251
+ {
1252
+ type: "button",
1253
+ "aria-label": `Choose a month, currently ${MONTH_NAMES[view.getMonth()]}`,
1254
+ onClick: () => setPanel((p) => p === "months" ? "days" : "months"),
1255
+ className: "rounded-md px-1.5 py-0.5 transition-colors hover:bg-gray-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400",
1256
+ children: MONTH_NAMES[view.getMonth()]
1257
+ }
1258
+ ),
1259
+ /* @__PURE__ */ jsx(
1260
+ "button",
1261
+ {
1262
+ type: "button",
1263
+ "aria-label": `Choose a year, currently ${view.getFullYear()}`,
1264
+ onClick: () => setPanel((p) => p === "years" ? "days" : "years"),
1265
+ className: "rounded-md px-1.5 py-0.5 transition-colors hover:bg-gray-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400",
1266
+ children: view.getFullYear()
1267
+ }
1268
+ )
1269
+ ] }) : /* @__PURE__ */ jsxs("span", { children: [
1270
+ MONTH_NAMES[view.getMonth()],
1271
+ " ",
1272
+ view.getFullYear()
1273
+ ] }) }),
1274
+ /* @__PURE__ */ jsx(
1275
+ "button",
1276
+ {
1277
+ type: "button",
1278
+ "aria-label": panel === "years" ? "Next years" : panel === "months" ? "Next year" : "Next month",
1279
+ onClick: () => goToMonth(pageForward()),
1280
+ className: "rounded-full p-1 text-gray-600 hover:bg-gray-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400",
1281
+ children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) })
1282
+ }
1283
+ )
1284
+ ] }),
1285
+ panel === "months" && /* @__PURE__ */ jsx("div", { role: "group", "aria-label": "Choose a month", className: "grid grid-cols-3 gap-1 text-center text-sm", children: MONTH_NAMES.map((name, m) => /* @__PURE__ */ jsx(
1286
+ "button",
1287
+ {
1288
+ type: "button",
1289
+ "aria-pressed": m === view.getMonth(),
1290
+ onClick: () => {
1291
+ goToMonth(new Date(view.getFullYear(), m, 1));
1292
+ setPanel("days");
1293
+ },
1294
+ className: [
1295
+ "rounded-md py-4 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400",
1296
+ m === view.getMonth() ? "bg-blue-600 font-semibold text-white" : m === today.getMonth() && view.getFullYear() === today.getFullYear() ? "font-medium text-blue-600 hover:bg-gray-100" : "text-gray-700 hover:bg-gray-100"
1297
+ ].join(" "),
1298
+ children: name.slice(0, 3)
1299
+ },
1300
+ name
1301
+ )) }),
1302
+ panel === "years" && /* @__PURE__ */ jsx("div", { role: "group", "aria-label": "Choose a year", className: "grid grid-cols-3 gap-1 text-center text-sm", children: Array.from({ length: YEARS_PER_PAGE }, (_, i) => yearPageStart(view.getFullYear()) + i).map((y) => /* @__PURE__ */ jsx(
1303
+ "button",
1304
+ {
1305
+ type: "button",
1306
+ "aria-pressed": y === view.getFullYear(),
1307
+ onClick: () => {
1308
+ goToMonth(new Date(y, view.getMonth(), 1));
1309
+ setPanel("months");
1310
+ },
1311
+ className: [
1312
+ "rounded-md py-4 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400",
1313
+ y === view.getFullYear() ? "bg-blue-600 font-semibold text-white" : y === today.getFullYear() ? "font-medium text-blue-600 hover:bg-gray-100" : "text-gray-700 hover:bg-gray-100"
1314
+ ].join(" "),
1315
+ children: y
1316
+ },
1317
+ y
1318
+ )) }),
1319
+ /* @__PURE__ */ jsxs(
1320
+ "div",
1321
+ {
1322
+ ref: gridRef,
1323
+ role: "grid",
1324
+ "aria-label": label,
1325
+ onKeyDown,
1326
+ hidden: panel !== "days",
1327
+ children: [
1328
+ /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 text-center text-xs font-medium text-gray-500", children: Array.from({ length: 7 }, (_, i) => (i + weekStartsOn) % 7).map((d) => (
1329
+ // abbr carries the full name: "Su" is read as a word otherwise.
1330
+ /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": DAY_NAMES[d], className: "py-1", children: /* @__PURE__ */ jsx("abbr", { title: DAY_NAMES[d], className: "no-underline", children: DAY_SHORT[d] }) }, d)
1331
+ )) }),
1332
+ Array.from({ length: 6 }, (_, row) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 text-center text-sm", children: cells.slice(row * 7, row * 7 + 7).map((cell) => {
1333
+ const key = toKey(cell);
1334
+ const outside = cell.getMonth() !== view.getMonth();
1335
+ const isSelected = key === value || mode === "range" && key === endValue;
1336
+ const between = inRange(key);
1337
+ const disabled = outOfRange(key);
1338
+ return /* @__PURE__ */ jsx("div", { role: "gridcell", "aria-selected": isSelected, children: /* @__PURE__ */ jsx(
1339
+ "button",
1340
+ {
1341
+ type: "button",
1342
+ "data-active": key === activeKey,
1343
+ tabIndex: key === activeKey ? 0 : -1,
1344
+ disabled,
1345
+ "aria-label": `${cell.getDate()} ${MONTH_NAMES[cell.getMonth()]} ${cell.getFullYear()}`,
1346
+ "aria-current": key === todayKey ? "date" : void 0,
1347
+ onClick: () => {
1348
+ setActive(key);
1349
+ onSelect(key);
1350
+ },
1351
+ className: [
1352
+ // 44px below `sm`, compact above it. A 32px cell is under
1353
+ // every touch-target guideline there is, and a date grid
1354
+ // is seven of them side by side — the case where missing
1355
+ // by four pixels picks the wrong day rather than nothing.
1356
+ "w-full rounded-md py-1.5 min-h-11 sm:min-h-0 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400",
1357
+ disabled ? "cursor-not-allowed text-gray-300" : isSelected ? "bg-blue-600 font-semibold text-white" : between ? "bg-blue-100 text-blue-800" : outside ? "text-gray-300 hover:bg-gray-100" : "text-gray-700 hover:bg-gray-100",
1358
+ key === todayKey && !isSelected ? "font-semibold text-blue-600" : ""
1359
+ ].filter(Boolean).join(" "),
1360
+ children: cell.getDate()
1361
+ }
1362
+ ) }, key);
1363
+ }) }, row))
1364
+ ]
1365
+ }
1366
+ )
1367
+ ] });
1368
+ }
1101
1369
  function useClickOutside(ref, onClose) {
1102
1370
  useEffect(() => {
1103
1371
  const handler = (e) => {
@@ -1108,10 +1376,8 @@ function useClickOutside(ref, onClose) {
1108
1376
  }, [ref, onClose]);
1109
1377
  }
1110
1378
  var PRESET_LABELS = ["Last 2 Weeks", "Last Month", "Last 3 Months", "Last 6 Months", "Last 12 Months"];
1111
- var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1112
- var YEARS_PER_PAGE = 12;
1113
- var yearPageStart = (y) => Math.floor(y / YEARS_PER_PAGE) * YEARS_PER_PAGE;
1114
- var pad = (n) => String(n).padStart(2, "0");
1379
+ var monthKeyOf = (d) => `${d.getFullYear()}-${pad2(d.getMonth() + 1)}`;
1380
+ var pad2 = (n) => String(n).padStart(2, "0");
1115
1381
  var EDGE_MARGIN = 8;
1116
1382
  function clipBounds(el) {
1117
1383
  for (let node = el.parentElement; node; node = node.parentElement) {
@@ -1125,7 +1391,7 @@ function clipBounds(el) {
1125
1391
  return { left: 0, right: window.innerWidth };
1126
1392
  }
1127
1393
  function toISODate(d) {
1128
- return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
1394
+ return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
1129
1395
  }
1130
1396
  function parseDate(s) {
1131
1397
  const [y, m, d] = s.split("-").map(Number);
@@ -1151,10 +1417,7 @@ function DateRangePicker({
1151
1417
  const panelRef = useRef(null);
1152
1418
  const [align, setAlign] = useState("left");
1153
1419
  const now = /* @__PURE__ */ new Date();
1154
- const [month, setMonth] = useState(from ? parseDate(from).getMonth() : now.getMonth());
1155
- const [year, setYear] = useState(from ? parseDate(from).getFullYear() : now.getFullYear());
1156
- const [view, setView] = useState("days");
1157
- const [yearPage, setYearPage] = useState(() => yearPageStart(year));
1420
+ const [viewMonth, setViewMonth] = useState(() => monthKeyOf(from ? parseDate(from) : now));
1158
1421
  const displayDate = (s) => s ? formatDisplay(s) : "";
1159
1422
  useClickOutside(ref, useCallback(() => {
1160
1423
  if (open) setOpen(false);
@@ -1179,11 +1442,7 @@ function DateRangePicker({
1179
1442
  setTempFrom(from);
1180
1443
  setTempTo(to);
1181
1444
  setActivePreset(null);
1182
- const anchor = from ? parseDate(from) : now;
1183
- setMonth(anchor.getMonth());
1184
- setYear(anchor.getFullYear());
1185
- setYearPage(yearPageStart(anchor.getFullYear()));
1186
- setView("days");
1445
+ setViewMonth(monthKeyOf(from ? parseDate(from) : now));
1187
1446
  setOpen(true);
1188
1447
  };
1189
1448
  const handlePreset = (label) => {
@@ -1216,10 +1475,7 @@ function DateRangePicker({
1216
1475
  setTempFrom(toISODate(start));
1217
1476
  setTempTo(toISODate(end));
1218
1477
  setActivePreset(label);
1219
- setMonth(start.getMonth());
1220
- setYear(start.getFullYear());
1221
- setYearPage(yearPageStart(start.getFullYear()));
1222
- setView("days");
1478
+ setViewMonth(monthKeyOf(start));
1223
1479
  };
1224
1480
  const handleCalendarSelect = (date) => {
1225
1481
  setActivePreset("Custom");
@@ -1243,57 +1499,6 @@ function DateRangePicker({
1243
1499
  onChange("", "");
1244
1500
  setOpen(false);
1245
1501
  };
1246
- const changeMonth = (delta) => {
1247
- let m = month + delta, y = year;
1248
- if (m < 0) {
1249
- m = 11;
1250
- y--;
1251
- } else if (m > 11) {
1252
- m = 0;
1253
- y++;
1254
- }
1255
- setMonth(m);
1256
- setYear(y);
1257
- };
1258
- const handleStep = (delta) => {
1259
- if (view === "days") changeMonth(delta);
1260
- else if (view === "months") setYear(year + delta);
1261
- else setYearPage(yearPage + delta * YEARS_PER_PAGE);
1262
- };
1263
- const selectMonth = (m) => {
1264
- setMonth(m);
1265
- setView("days");
1266
- };
1267
- const selectYear = (y) => {
1268
- setYear(y);
1269
- setView("months");
1270
- };
1271
- const openYears = () => {
1272
- setYearPage(yearPageStart(year));
1273
- setView("years");
1274
- };
1275
- const firstDay = new Date(year, month, 1).getDay();
1276
- const daysInMonth = new Date(year, month + 1, 0).getDate();
1277
- const prevDays = new Date(year, month, 0).getDate();
1278
- const monthName = new Date(year, month).toLocaleString("default", { month: "long" });
1279
- const cells = [];
1280
- for (let i = firstDay - 1; i >= 0; i--) {
1281
- const d = new Date(year, month - 1, prevDays - i);
1282
- cells.push({ day: prevDays - i, current: false, date: toISODate(d) });
1283
- }
1284
- for (let d = 1; d <= daysInMonth; d++) {
1285
- cells.push({ day: d, current: true, date: toISODate(new Date(year, month, d)) });
1286
- }
1287
- const remaining = 42 - cells.length;
1288
- for (let d = 1; d <= remaining; d++) {
1289
- cells.push({ day: d, current: false, date: toISODate(new Date(year, month + 1, d)) });
1290
- }
1291
- const isInRange = (date) => {
1292
- if (!tempFrom || !tempTo) return false;
1293
- const s = tempFrom < tempTo ? tempFrom : tempTo;
1294
- const e = tempFrom < tempTo ? tempTo : tempFrom;
1295
- return date > s && date < e;
1296
- };
1297
1502
  const displayText = from && to ? `${displayDate(from)} \u2014 ${displayDate(to)}` : from ? `From ${displayDate(from)}` : to ? `To ${displayDate(to)}` : "";
1298
1503
  return /* @__PURE__ */ jsxs("div", { className: "relative", ref, children: [
1299
1504
  /* @__PURE__ */ jsxs(
@@ -1349,85 +1554,18 @@ function DateRangePicker({
1349
1554
  ] })
1350
1555
  ] }),
1351
1556
  /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
1352
- /* @__PURE__ */ jsxs("div", { className: "w-64", children: [
1353
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2 px-1", children: [
1354
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => handleStep(-1), "aria-label": "Previous", className: "p-1 rounded-full hover:bg-gray-100 text-gray-600", children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", strokeWidth: "2", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 19l-7-7 7-7" }) }) }),
1355
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-sm font-semibold text-gray-800", children: [
1356
- view === "days" && /* @__PURE__ */ jsx(
1357
- "button",
1358
- {
1359
- type: "button",
1360
- onClick: () => setView("months"),
1361
- className: "px-1.5 py-0.5 rounded-md hover:bg-gray-100 transition-colors",
1362
- children: monthName
1363
- }
1364
- ),
1365
- view === "years" ? /* @__PURE__ */ jsxs("span", { className: "px-1.5 py-0.5", children: [
1366
- yearPage,
1367
- " \u2013 ",
1368
- yearPage + YEARS_PER_PAGE - 1
1369
- ] }) : /* @__PURE__ */ jsx(
1370
- "button",
1371
- {
1372
- type: "button",
1373
- onClick: openYears,
1374
- className: "px-1.5 py-0.5 rounded-md hover:bg-gray-100 transition-colors",
1375
- children: year
1376
- }
1377
- )
1378
- ] }),
1379
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => handleStep(1), "aria-label": "Next", className: "p-1 rounded-full hover:bg-gray-100 text-gray-600", children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", strokeWidth: "2", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) }) })
1380
- ] }),
1381
- /* @__PURE__ */ jsxs("div", { className: "min-h-[13.75rem]", children: [
1382
- view === "days" && /* @__PURE__ */ jsxs(Fragment, { children: [
1383
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 text-center text-xs font-medium text-gray-500 mb-1", children: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((d) => /* @__PURE__ */ jsx("div", { className: "py-1", children: d }, d)) }),
1384
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 text-center text-sm", children: cells.map((c, i) => {
1385
- const isStart = c.date === tempFrom;
1386
- const isEnd = c.date === tempTo;
1387
- const isSelected = isStart || isEnd;
1388
- const inRange = isInRange(c.date);
1389
- return /* @__PURE__ */ jsx(
1390
- "button",
1391
- {
1392
- type: "button",
1393
- onClick: () => handleCalendarSelect(c.date),
1394
- className: `py-1.5 rounded-md transition-colors ${!c.current ? "text-gray-300" : isSelected ? "bg-blue-600 text-white font-semibold" : inRange ? "bg-blue-100 text-blue-800" : "text-gray-700 hover:bg-gray-100"}`,
1395
- children: c.day
1396
- },
1397
- i
1398
- );
1399
- }) })
1400
- ] }),
1401
- view === "months" && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-1 text-center text-sm", children: MONTH_NAMES.map((name, m) => {
1402
- const isCurrent = m === month;
1403
- const isThisMonth = m === now.getMonth() && year === now.getFullYear();
1404
- return /* @__PURE__ */ jsx(
1405
- "button",
1406
- {
1407
- type: "button",
1408
- onClick: () => selectMonth(m),
1409
- className: `py-4 rounded-md transition-colors ${isCurrent ? "bg-blue-600 text-white font-semibold" : isThisMonth ? "text-blue-600 font-medium hover:bg-gray-100" : "text-gray-700 hover:bg-gray-100"}`,
1410
- children: name
1411
- },
1412
- name
1413
- );
1414
- }) }),
1415
- view === "years" && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-1 text-center text-sm", children: Array.from({ length: YEARS_PER_PAGE }, (_, i) => yearPage + i).map((y) => {
1416
- const isCurrent = y === year;
1417
- const isThisYear = y === now.getFullYear();
1418
- return /* @__PURE__ */ jsx(
1419
- "button",
1420
- {
1421
- type: "button",
1422
- onClick: () => selectYear(y),
1423
- className: `py-4 rounded-md transition-colors ${isCurrent ? "bg-blue-600 text-white font-semibold" : isThisYear ? "text-blue-600 font-medium hover:bg-gray-100" : "text-gray-700 hover:bg-gray-100"}`,
1424
- children: y
1425
- },
1426
- y
1427
- );
1428
- }) })
1429
- ] })
1430
- ] }),
1557
+ /* @__PURE__ */ jsx("div", { className: "w-64", children: /* @__PURE__ */ jsx(
1558
+ Calendar,
1559
+ {
1560
+ mode: "range",
1561
+ value: tempFrom || null,
1562
+ endValue: tempTo || null,
1563
+ month: viewMonth,
1564
+ onMonthChange: setViewMonth,
1565
+ onSelect: handleCalendarSelect,
1566
+ "aria-label": "Date range"
1567
+ }
1568
+ ) }),
1431
1569
  /* @__PURE__ */ jsxs("div", { className: "border-l border-gray-200 pl-4 flex flex-col gap-1 min-w-[130px]", children: [
1432
1570
  PRESET_LABELS.map((label) => /* @__PURE__ */ jsx(
1433
1571
  "button",
@@ -1469,45 +1607,14 @@ function DateRangePicker({
1469
1607
  )
1470
1608
  ] });
1471
1609
  }
1472
- var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
1473
- function toInputValue(value) {
1474
- if (value === null || value === void 0 || value === "") return "";
1475
- if (value instanceof Date) return Number.isNaN(value.getTime()) ? "" : toISODate(value);
1476
- if (ISO_DATE.test(value)) return value;
1477
- const parsed = new Date(value);
1478
- return Number.isNaN(parsed.getTime()) ? "" : toISODate(parsed);
1479
- }
1480
- function fromInputValue(raw) {
1481
- if (!ISO_DATE.test(raw)) return null;
1482
- const [year, month, day] = raw.split("-").map(Number);
1483
- const date = new Date(year, month - 1, day);
1484
- return date.getMonth() === month - 1 && date.getDate() === day ? date : null;
1485
- }
1486
- var DatePicker = forwardRef(function DatePicker2({ value, onChange, min, max, invalid, size, className = "", ...rest }, ref) {
1487
- return /* @__PURE__ */ jsx(
1488
- "input",
1489
- {
1490
- ref,
1491
- type: "date",
1492
- "aria-invalid": invalid || void 0,
1493
- value: toInputValue(value),
1494
- min: toInputValue(min) || void 0,
1495
- max: toInputValue(max) || void 0,
1496
- onChange: (event) => onChange?.(fromInputValue(event.target.value)),
1497
- className: inputClasses({ invalid, size, className }),
1498
- ...rest
1499
- }
1500
- );
1501
- });
1502
- var DatePicker_default = DatePicker;
1503
1610
  var TIME = /^\d{2}:\d{2}(:\d{2})?$/;
1504
- var pad2 = (n) => String(n).padStart(2, "0");
1505
- function toInputValue2(value, withSeconds) {
1611
+ var pad3 = (n) => String(n).padStart(2, "0");
1612
+ function toInputValue(value, withSeconds) {
1506
1613
  if (value === null || value === void 0 || value === "") return "";
1507
1614
  if (value instanceof Date) {
1508
1615
  if (Number.isNaN(value.getTime())) return "";
1509
- const base = `${pad2(value.getHours())}:${pad2(value.getMinutes())}`;
1510
- return withSeconds ? `${base}:${pad2(value.getSeconds())}` : base;
1616
+ const base = `${pad3(value.getHours())}:${pad3(value.getMinutes())}`;
1617
+ return withSeconds ? `${base}:${pad3(value.getSeconds())}` : base;
1511
1618
  }
1512
1619
  return TIME.test(value) ? value : "";
1513
1620
  }
@@ -1520,9 +1627,9 @@ var TimePicker = forwardRef(function TimePicker2({ value, onChange, min, max, in
1520
1627
  type: "time",
1521
1628
  step,
1522
1629
  "aria-invalid": invalid || void 0,
1523
- value: toInputValue2(value, withSeconds),
1524
- min: toInputValue2(min, withSeconds) || void 0,
1525
- max: toInputValue2(max, withSeconds) || void 0,
1630
+ value: toInputValue(value, withSeconds),
1631
+ min: toInputValue(min, withSeconds) || void 0,
1632
+ max: toInputValue(max, withSeconds) || void 0,
1526
1633
  onChange: (event) => onChange?.(TIME.test(event.target.value) ? event.target.value : null),
1527
1634
  className: inputClasses({ invalid, size, className }),
1528
1635
  ...rest
@@ -1530,10 +1637,252 @@ var TimePicker = forwardRef(function TimePicker2({ value, onChange, min, max, in
1530
1637
  );
1531
1638
  });
1532
1639
  var TimePicker_default = TimePicker;
1640
+ var POPUP_MAX_WIDTH = 448;
1641
+ var MENU_MAX_HEIGHT2 = 240;
1642
+ var MENU_GAP2 = 4;
1643
+ var VIEWPORT_MARGIN2 = 8;
1644
+ function useDropdownPosition(triggerRef, open) {
1645
+ const [pos, setPos] = useState(null);
1646
+ useLayoutEffect(() => {
1647
+ if (!open) {
1648
+ setPos(null);
1649
+ return;
1650
+ }
1651
+ let lastLeft = NaN, lastTop = NaN, lastRight = NaN, lastBottom = NaN;
1652
+ const compute = () => {
1653
+ const rect = triggerRef.current?.getBoundingClientRect();
1654
+ if (!rect) return;
1655
+ lastLeft = rect.left;
1656
+ lastTop = rect.top;
1657
+ lastRight = rect.right;
1658
+ lastBottom = rect.bottom;
1659
+ const spaceBelow = window.innerHeight - rect.bottom - MENU_GAP2 - VIEWPORT_MARGIN2;
1660
+ const spaceAbove = rect.top - MENU_GAP2 - VIEWPORT_MARGIN2;
1661
+ const placeAbove = spaceBelow < Math.min(MENU_MAX_HEIGHT2, 160) && spaceAbove > spaceBelow;
1662
+ const maxHeight = Math.max(96, Math.min(MENU_MAX_HEIGHT2, placeAbove ? spaceAbove : spaceBelow));
1663
+ const next = { minWidth: rect.width, maxHeight };
1664
+ if (window.innerWidth - rect.left < POPUP_MAX_WIDTH) {
1665
+ next.right = Math.max(VIEWPORT_MARGIN2, window.innerWidth - rect.right);
1666
+ } else {
1667
+ next.left = Math.max(VIEWPORT_MARGIN2, rect.left);
1668
+ }
1669
+ if (placeAbove) next.bottom = window.innerHeight - rect.top + MENU_GAP2;
1670
+ else next.top = rect.bottom + MENU_GAP2;
1671
+ setPos(next);
1672
+ };
1673
+ compute();
1674
+ let raf = requestAnimationFrame(function tick() {
1675
+ const rect = triggerRef.current?.getBoundingClientRect();
1676
+ if (rect && (rect.left !== lastLeft || rect.top !== lastTop || rect.right !== lastRight || rect.bottom !== lastBottom)) {
1677
+ compute();
1678
+ }
1679
+ raf = requestAnimationFrame(tick);
1680
+ });
1681
+ window.addEventListener("scroll", compute, true);
1682
+ window.addEventListener("resize", compute);
1683
+ return () => {
1684
+ cancelAnimationFrame(raf);
1685
+ window.removeEventListener("scroll", compute, true);
1686
+ window.removeEventListener("resize", compute);
1687
+ };
1688
+ }, [open, triggerRef]);
1689
+ return pos;
1690
+ }
1691
+ var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
1692
+ var MONTH_NAMES2 = [
1693
+ "January",
1694
+ "February",
1695
+ "March",
1696
+ "April",
1697
+ "May",
1698
+ "June",
1699
+ "July",
1700
+ "August",
1701
+ "September",
1702
+ "October",
1703
+ "November",
1704
+ "December"
1705
+ ];
1706
+ function toInputValue2(value) {
1707
+ if (value === null || value === void 0 || value === "") return "";
1708
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? "" : toISODate(value);
1709
+ if (ISO_DATE.test(value)) return value;
1710
+ const parsed = new Date(value);
1711
+ return Number.isNaN(parsed.getTime()) ? "" : toISODate(parsed);
1712
+ }
1713
+ function fromInputValue(raw) {
1714
+ if (!ISO_DATE.test(raw)) return null;
1715
+ const [year, month, day] = raw.split("-").map(Number);
1716
+ const date = new Date(year, month - 1, day);
1717
+ return date.getMonth() === month - 1 && date.getDate() === day ? date : null;
1718
+ }
1719
+ function display(key) {
1720
+ const d = fromInputValue(key);
1721
+ return d ? `${d.getDate()} ${MONTH_NAMES2[d.getMonth()]} ${d.getFullYear()}` : "";
1722
+ }
1723
+ var DatePicker = forwardRef(function DatePicker2({
1724
+ value,
1725
+ onChange,
1726
+ min,
1727
+ max,
1728
+ invalid,
1729
+ size,
1730
+ native,
1731
+ weekStartsOn,
1732
+ className = "",
1733
+ "aria-label": ariaLabel,
1734
+ "aria-describedby": describedBy,
1735
+ id,
1736
+ name,
1737
+ disabled,
1738
+ placeholder,
1739
+ ...rest
1740
+ }, ref) {
1741
+ const current = toInputValue2(value);
1742
+ const lo = toInputValue2(min);
1743
+ const hi = toInputValue2(max);
1744
+ const [open, setOpen] = useState(false);
1745
+ const wrapRef = useRef(null);
1746
+ const triggerRef = useRef(null);
1747
+ const panelRef = useRef(null);
1748
+ const panelId = useId();
1749
+ const pos = useDropdownPosition(triggerRef, open);
1750
+ useEffect(() => {
1751
+ if (!open) return;
1752
+ const onPointerDown = (e) => {
1753
+ const target = e.target;
1754
+ if (wrapRef.current?.contains(target) || panelRef.current?.contains(target)) return;
1755
+ setOpen(false);
1756
+ };
1757
+ document.addEventListener("pointerdown", onPointerDown, true);
1758
+ return () => document.removeEventListener("pointerdown", onPointerDown, true);
1759
+ }, [open]);
1760
+ useEffect(() => {
1761
+ if (!open) return;
1762
+ return registerModalEscapeInterceptor((event) => {
1763
+ if (event.key !== "Escape") return false;
1764
+ setOpen(false);
1765
+ triggerRef.current?.focus();
1766
+ return true;
1767
+ });
1768
+ }, [open]);
1769
+ if (native) {
1770
+ return /* @__PURE__ */ jsx(
1771
+ "input",
1772
+ {
1773
+ ref,
1774
+ type: "date",
1775
+ id,
1776
+ name,
1777
+ disabled,
1778
+ "aria-label": ariaLabel,
1779
+ "aria-describedby": describedBy,
1780
+ "aria-invalid": invalid || void 0,
1781
+ value: current,
1782
+ min: lo || void 0,
1783
+ max: hi || void 0,
1784
+ onChange: (event) => onChange?.(fromInputValue(event.target.value)),
1785
+ className: inputClasses({ invalid, size, className }),
1786
+ ...rest
1787
+ }
1788
+ );
1789
+ }
1790
+ return /* @__PURE__ */ jsxs("div", { ref: wrapRef, className: "relative", children: [
1791
+ /* @__PURE__ */ jsx("input", { ref, type: "hidden", name, value: current }),
1792
+ /* @__PURE__ */ jsxs(
1793
+ "button",
1794
+ {
1795
+ ref: triggerRef,
1796
+ type: "button",
1797
+ id,
1798
+ disabled,
1799
+ "aria-haspopup": "dialog",
1800
+ "aria-expanded": open,
1801
+ "aria-controls": open ? panelId : void 0,
1802
+ "aria-describedby": describedBy,
1803
+ "aria-invalid": invalid || void 0,
1804
+ "aria-label": ariaLabel ? `${ariaLabel}${current ? `, ${display(current)}` : ""}` : void 0,
1805
+ onClick: () => setOpen((o) => !o),
1806
+ className: `${inputClasses({ invalid, size, className })} flex items-center justify-between text-left`,
1807
+ children: [
1808
+ /* @__PURE__ */ jsx("span", { className: current ? "text-gray-900" : "text-gray-400", children: current ? display(current) : placeholder ?? "Select a date" }),
1809
+ /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 shrink-0 text-gray-400", fill: "none", stroke: "currentColor", strokeWidth: "1.75", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8 7V3m8 4V3M4 11h16M5 7h14a1 1 0 011 1v11a1 1 0 01-1 1H5a1 1 0 01-1-1V8a1 1 0 011-1z" }) })
1810
+ ]
1811
+ }
1812
+ ),
1813
+ open && createPortal(
1814
+ /* @__PURE__ */ jsxs(
1815
+ "div",
1816
+ {
1817
+ ref: panelRef,
1818
+ id: panelId,
1819
+ role: "dialog",
1820
+ "aria-label": ariaLabel ?? "Choose a date",
1821
+ className: "fixed z-[400] w-72 rounded-xl border border-gray-200 bg-white p-3 shadow-lg",
1822
+ style: {
1823
+ left: pos?.left,
1824
+ right: pos?.right,
1825
+ top: pos?.top,
1826
+ bottom: pos?.bottom,
1827
+ // Hidden for the first paint until the layout effect measures the
1828
+ // trigger, so the panel never flashes at (0,0).
1829
+ visibility: pos ? void 0 : "hidden"
1830
+ },
1831
+ children: [
1832
+ /* @__PURE__ */ jsx(
1833
+ Calendar,
1834
+ {
1835
+ value: current || null,
1836
+ min: lo || null,
1837
+ max: hi || null,
1838
+ weekStartsOn,
1839
+ onSelect: (key) => {
1840
+ setOpen(false);
1841
+ triggerRef.current?.focus();
1842
+ onChange?.(fromInputValue(key));
1843
+ }
1844
+ }
1845
+ ),
1846
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex justify-between border-t border-gray-100 pt-2", children: [
1847
+ /* @__PURE__ */ jsx(
1848
+ "button",
1849
+ {
1850
+ type: "button",
1851
+ onClick: () => {
1852
+ setOpen(false);
1853
+ triggerRef.current?.focus();
1854
+ onChange?.(null);
1855
+ },
1856
+ className: "rounded-md px-2 py-1 text-sm text-gray-500 hover:bg-gray-100 hover:text-gray-700",
1857
+ children: "Clear"
1858
+ }
1859
+ ),
1860
+ /* @__PURE__ */ jsx(
1861
+ "button",
1862
+ {
1863
+ type: "button",
1864
+ onClick: () => {
1865
+ setOpen(false);
1866
+ triggerRef.current?.focus();
1867
+ onChange?.(/* @__PURE__ */ new Date());
1868
+ },
1869
+ className: "rounded-md px-2 py-1 text-sm font-medium text-blue-600 hover:bg-blue-50",
1870
+ children: "Today"
1871
+ }
1872
+ )
1873
+ ] })
1874
+ ]
1875
+ }
1876
+ ),
1877
+ document.body
1878
+ )
1879
+ ] });
1880
+ });
1881
+ var DatePicker_default = DatePicker;
1533
1882
  var ISO_LOCAL = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?$/;
1534
- var pad3 = (n) => String(n).padStart(2, "0");
1883
+ var pad4 = (n) => String(n).padStart(2, "0");
1535
1884
  function serialise(date) {
1536
- return `${date.getFullYear()}-${pad3(date.getMonth() + 1)}-${pad3(date.getDate())}T${pad3(date.getHours())}:${pad3(date.getMinutes())}`;
1885
+ return `${date.getFullYear()}-${pad4(date.getMonth() + 1)}-${pad4(date.getDate())}T${pad4(date.getHours())}:${pad4(date.getMinutes())}`;
1537
1886
  }
1538
1887
  function toInputValue3(value) {
1539
1888
  if (value === null || value === void 0 || value === "") return "";
@@ -1677,8 +2026,8 @@ function Segmented({
1677
2026
  className = ""
1678
2027
  }) {
1679
2028
  const groupId = useId();
1680
- const track = `inline-flex items-center gap-1 rounded-lg bg-gray-100 p-1 ${block ? "w-full" : ""} ${className}`.trim();
1681
- const itemBase = `inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors ${block ? "flex-1" : ""}`;
2029
+ const track = `inline-flex max-w-full items-center gap-1 overflow-x-auto rounded-lg bg-gray-100 p-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden ${block ? "w-full" : ""} ${className}`.trim();
2030
+ const itemBase = `inline-flex shrink-0 items-center justify-center gap-1.5 whitespace-nowrap rounded-md font-medium transition-colors ${block ? "flex-1" : ""}`;
1682
2031
  if (name) {
1683
2032
  return /* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-label": label, className: track, children: options.map((opt) => {
1684
2033
  const id = `${groupId}-${opt.value}`;
@@ -2029,57 +2378,6 @@ function FilePicker({
2029
2378
  error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-red-600", children: error })
2030
2379
  ] });
2031
2380
  }
2032
- var POPUP_MAX_WIDTH = 448;
2033
- var MENU_MAX_HEIGHT2 = 240;
2034
- var MENU_GAP2 = 4;
2035
- var VIEWPORT_MARGIN2 = 8;
2036
- function useDropdownPosition(triggerRef, open) {
2037
- const [pos, setPos] = useState(null);
2038
- useLayoutEffect(() => {
2039
- if (!open) {
2040
- setPos(null);
2041
- return;
2042
- }
2043
- let lastLeft = NaN, lastTop = NaN, lastRight = NaN, lastBottom = NaN;
2044
- const compute = () => {
2045
- const rect = triggerRef.current?.getBoundingClientRect();
2046
- if (!rect) return;
2047
- lastLeft = rect.left;
2048
- lastTop = rect.top;
2049
- lastRight = rect.right;
2050
- lastBottom = rect.bottom;
2051
- const spaceBelow = window.innerHeight - rect.bottom - MENU_GAP2 - VIEWPORT_MARGIN2;
2052
- const spaceAbove = rect.top - MENU_GAP2 - VIEWPORT_MARGIN2;
2053
- const placeAbove = spaceBelow < Math.min(MENU_MAX_HEIGHT2, 160) && spaceAbove > spaceBelow;
2054
- const maxHeight = Math.max(96, Math.min(MENU_MAX_HEIGHT2, placeAbove ? spaceAbove : spaceBelow));
2055
- const next = { minWidth: rect.width, maxHeight };
2056
- if (window.innerWidth - rect.left < POPUP_MAX_WIDTH) {
2057
- next.right = Math.max(VIEWPORT_MARGIN2, window.innerWidth - rect.right);
2058
- } else {
2059
- next.left = Math.max(VIEWPORT_MARGIN2, rect.left);
2060
- }
2061
- if (placeAbove) next.bottom = window.innerHeight - rect.top + MENU_GAP2;
2062
- else next.top = rect.bottom + MENU_GAP2;
2063
- setPos(next);
2064
- };
2065
- compute();
2066
- let raf = requestAnimationFrame(function tick() {
2067
- const rect = triggerRef.current?.getBoundingClientRect();
2068
- if (rect && (rect.left !== lastLeft || rect.top !== lastTop || rect.right !== lastRight || rect.bottom !== lastBottom)) {
2069
- compute();
2070
- }
2071
- raf = requestAnimationFrame(tick);
2072
- });
2073
- window.addEventListener("scroll", compute, true);
2074
- window.addEventListener("resize", compute);
2075
- return () => {
2076
- cancelAnimationFrame(raf);
2077
- window.removeEventListener("scroll", compute, true);
2078
- window.removeEventListener("resize", compute);
2079
- };
2080
- }, [open, triggerRef]);
2081
- return pos;
2082
- }
2083
2381
  function ClearButton({ onClear, ariaLabel = "Clear selection" }) {
2084
2382
  return /* @__PURE__ */ jsx(
2085
2383
  "button",
@@ -2229,7 +2527,7 @@ function SearchableSelect({
2229
2527
  "div",
2230
2528
  {
2231
2529
  ref: menuRef,
2232
- className: "fixed z-[400] rounded-2xl overflow-hidden",
2530
+ className: "fixed z-[10000] rounded-2xl overflow-hidden",
2233
2531
  style: {
2234
2532
  left: menuPos?.left,
2235
2533
  right: menuPos?.right,
@@ -2398,7 +2696,7 @@ function TagInput({
2398
2696
  "div",
2399
2697
  {
2400
2698
  ref: menuRef,
2401
- className: "fixed z-[400] rounded-2xl overflow-hidden",
2699
+ className: "fixed z-[10000] rounded-2xl overflow-hidden",
2402
2700
  style: {
2403
2701
  left: menuPos?.left,
2404
2702
  right: menuPos?.right,
@@ -2714,23 +3012,23 @@ function DescriptionList({
2714
3012
  const resolved = typeof columns === "number" ? { base: columns } : columns;
2715
3013
  const base = resolved.base ?? 1;
2716
3014
  const widest = Math.max(base, resolved.sm ?? 0, resolved.lg ?? 0);
2717
- const pad4 = size === "sm" ? "px-3 py-2" : "px-4 py-3";
3015
+ const pad5 = size === "sm" ? "px-3 py-2" : "px-4 py-3";
2718
3016
  const text = size === "sm" ? "text-xs" : "text-sm";
2719
3017
  return /* @__PURE__ */ jsxs("div", { className: [bordered ? "rounded-lg border border-gray-200 bg-white" : "", className].filter(Boolean).join(" "), children: [
2720
- title && /* @__PURE__ */ jsx("div", { className: `${pad4} ${bordered ? "border-b border-gray-100" : ""} text-sm font-semibold text-gray-900`.trim(), children: title }),
3018
+ title && /* @__PURE__ */ jsx("div", { className: `${pad5} ${bordered ? "border-b border-gray-100" : ""} text-sm font-semibold text-gray-900`.trim(), children: title }),
2721
3019
  /* @__PURE__ */ jsx("dl", { className: [
2722
3020
  "grid",
2723
3021
  COLS2[base],
2724
3022
  resolved.sm ? SM[resolved.sm] : "",
2725
3023
  resolved.lg ? LG[resolved.lg] : "",
2726
3024
  bordered ? "" : "gap-x-6 gap-y-3",
2727
- bordered ? "" : pad4 === "px-4 py-3" ? "py-1" : ""
3025
+ bordered ? "" : pad5 === "px-4 py-3" ? "py-1" : ""
2728
3026
  ].filter(Boolean).join(" "), children: items.map((item, i) => /* @__PURE__ */ jsxs(
2729
3027
  "div",
2730
3028
  {
2731
3029
  className: [
2732
3030
  item.span ? SPAN[widest] : "",
2733
- bordered ? `${pad4} border-b border-gray-100 last:border-b-0` : ""
3031
+ bordered ? `${pad5} border-b border-gray-100 last:border-b-0` : ""
2734
3032
  ].filter(Boolean).join(" "),
2735
3033
  children: [
2736
3034
  /* @__PURE__ */ jsx("dt", { className: `${text} text-gray-500`, children: item.label }),
@@ -4484,12 +4782,28 @@ function DropdownMenu({
4484
4782
  )
4485
4783
  ] });
4486
4784
  }
4487
- var SIZE_PX2 = { sm: 320, md: 448, lg: 640 };
4785
+ var SIZE_CLASS = {
4786
+ sm: "sm:w-[320px]",
4787
+ md: "sm:w-[448px]",
4788
+ lg: "sm:w-[640px]"
4789
+ };
4488
4790
  var SIDE_POSITION = {
4489
4791
  right: "inset-y-0 right-0",
4490
4792
  left: "inset-y-0 left-0",
4491
4793
  bottom: "inset-x-0 bottom-0"
4492
4794
  };
4795
+ function CloseButton({ onClose }) {
4796
+ return /* @__PURE__ */ jsx(
4797
+ "button",
4798
+ {
4799
+ type: "button",
4800
+ onClick: onClose,
4801
+ "aria-label": "Close",
4802
+ className: "rounded-md p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400",
4803
+ children: /* @__PURE__ */ jsx("svg", { className: "h-5 w-5", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M6 6l8 8M14 6l-8 8" }) })
4804
+ }
4805
+ );
4806
+ }
4493
4807
  function Drawer({
4494
4808
  open,
4495
4809
  onClose,
@@ -4500,10 +4814,12 @@ function Drawer({
4500
4814
  children,
4501
4815
  blocking = false,
4502
4816
  initialFocus,
4817
+ "aria-label": ariaLabel,
4503
4818
  className = ""
4504
4819
  }) {
4505
4820
  const panelRef = useRef(null);
4506
4821
  const bodyId = useId();
4822
+ const titleId = useId();
4507
4823
  useFocusTrap(panelRef, open, initialFocus);
4508
4824
  useScrollLock(open);
4509
4825
  useEffect(() => {
@@ -4516,7 +4832,7 @@ function Drawer({
4516
4832
  }, [open, blocking, onClose]);
4517
4833
  if (!open) return null;
4518
4834
  const isBottom = side === "bottom";
4519
- const panelStyle = isBottom ? { maxHeight: "85vh" } : { width: SIZE_PX2[size], maxWidth: "100vw" };
4835
+ const panelStyle = isBottom ? { maxHeight: "85vh" } : void 0;
4520
4836
  return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[9999]", role: "presentation", children: [
4521
4837
  /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black/30", onClick: blocking ? void 0 : onClose, "aria-hidden": "true" }),
4522
4838
  /* @__PURE__ */ jsxs(
@@ -4525,29 +4841,27 @@ function Drawer({
4525
4841
  ref: panelRef,
4526
4842
  role: "dialog",
4527
4843
  "aria-modal": "true",
4528
- "aria-label": typeof title === "string" ? title : void 0,
4844
+ "aria-labelledby": title ? titleId : void 0,
4845
+ "aria-label": title ? void 0 : ariaLabel,
4529
4846
  "aria-describedby": children ? bodyId : void 0,
4530
4847
  style: panelStyle,
4531
4848
  className: [
4532
4849
  "fixed flex flex-col bg-white shadow-xl",
4850
+ // Full width on a phone, the asked-for width from `sm` up. A 320px
4851
+ // drawer on a 375px screen leaves a 55px strip of scrim: too narrow
4852
+ // to aim at, too wide to read as an edge, and it makes the panel look
4853
+ // like a desktop rail that was squeezed rather than a sheet built for
4854
+ // the screen it is on.
4855
+ isBottom ? "" : `w-full max-w-full ${SIZE_CLASS[size]}`,
4533
4856
  SIDE_POSITION[side],
4534
4857
  isBottom ? "rounded-t-xl" : "",
4535
4858
  className
4536
4859
  ].filter(Boolean).join(" "),
4537
4860
  children: [
4538
- (title || !blocking) && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-between gap-4 border-b border-gray-100 px-4 py-3", children: [
4539
- title ? /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-gray-900", children: title }) : /* @__PURE__ */ jsx("span", {}),
4540
- !blocking && /* @__PURE__ */ jsx(
4541
- "button",
4542
- {
4543
- type: "button",
4544
- onClick: onClose,
4545
- "aria-label": "Close",
4546
- className: "rounded-md p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600",
4547
- children: /* @__PURE__ */ jsx("svg", { className: "h-5 w-5", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M6 6l8 8M14 6l-8 8" }) })
4548
- }
4549
- )
4550
- ] }),
4861
+ title ? /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-between gap-4 border-b border-gray-100 px-4 py-3", children: [
4862
+ /* @__PURE__ */ jsx("h2", { id: titleId, className: "text-base font-semibold text-gray-900", children: title }),
4863
+ !blocking && /* @__PURE__ */ jsx(CloseButton, { onClose })
4864
+ ] }) : !blocking && /* @__PURE__ */ jsx("div", { className: "absolute right-2 top-2 z-10", children: /* @__PURE__ */ jsx(CloseButton, { onClose }) }),
4551
4865
  /* @__PURE__ */ jsx("div", { id: bodyId, className: "min-h-0 flex-1 overflow-y-auto px-4 py-4", children }),
4552
4866
  footer && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 justify-end gap-3 border-t border-gray-100 px-4 py-3", children: footer })
4553
4867
  ]
@@ -5204,8 +5518,8 @@ function Sparkline({
5204
5518
  const max = Math.max(...data);
5205
5519
  const min = Math.min(...data);
5206
5520
  const span = max - min || 1;
5207
- const pad4 = strokeWidth + (showDots ? 2 : 0);
5208
- const y = (v) => height - pad4 - (v - min) / span * (height - pad4 * 2);
5521
+ const pad5 = strokeWidth + (showDots ? 2 : 0);
5522
+ const y = (v) => height - pad5 - (v - min) / span * (height - pad5 * 2);
5209
5523
  const pts = data.length === 1 ? [[0, height / 2], [width, height / 2]] : data.map((v, i) => [i * width / (data.length - 1), y(v)]);
5210
5524
  const line = pts.map(([x, yy], i) => `${i === 0 ? "M" : "L"}${x.toFixed(2)},${yy.toFixed(2)}`).join(" ");
5211
5525
  const area = `${line} L${width.toFixed(2)},${height} L0,${height} Z`;
@@ -6107,6 +6421,6 @@ var ALT_SHIFT_D = isMac ? "\u2325\u21E7D" : "Alt+Shift+D";
6107
6421
  var ALT_SHIFT_E = isMac ? "\u2325\u21E7E" : "Alt+Shift+E";
6108
6422
  var ALT_SHIFT_N = isMac ? "\u2325\u21E7N" : "Alt+Shift+N";
6109
6423
 
6110
- export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, Button_default, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, CMD_SHIFT_Z, CMD_Z, Card, ChatTemplate, Checkbox_default, CheckoutTemplate, ColoredBadge, ContainerFillChart, CountBadge, DashboardTemplate, DataTable, DataTablePage, DatePicker_default, DateRangePicker, DateTimePicker_default, DescriptionList, Divider, DonutChart, Drawer, DropdownMenu, ENTER, EmailTemplate, EmptyState, ErrorBoundary, ErrorPage, FilePicker, FormField, FormLayoutPage, GROUP_COLORS, GalleryTemplate, GlobalSearch, Grid, HelpCenter, INPUT_BASE, INPUT_SIZES, IconButton_default, Inline, InputNumber_default, Input_default, Kanban, Label, LineChart, ListFooter, ListLoadError, LoadingSpinner, MAX_FRACTION_DIGITS, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NativeSelect, NumericKeypad, PageHeader, Pagination, Paragraph, Radio_default, Result, SHIFT, SearchableSelect, Segmented, Select_default, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, Skeleton, Sparkline, Stack, StatCard, Statistic, StatusBadge, StatusBadgeProvider, Stepper, Switch_default, Tabs, TagInput, Text, Textarea_default, TileButton_default, TimePicker_default, Title, Tooltip, TopNav, appendKey, applyThemePrefs, backspace, firstEnabledIndex, formatDate, inputClasses, isMac, isSeverityTone, lastEnabledIndex, matchTypeahead, mediaFileName, nextEnabledIndex, resolveTheme, severityOf, tabButtonId, tabPanelId, toISODate, useClickOutside, useTheme };
6111
- //# sourceMappingURL=chunk-CWA27IGB.js.map
6112
- //# sourceMappingURL=chunk-CWA27IGB.js.map
6424
+ export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, Button_default, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, CMD_SHIFT_Z, CMD_Z, Calendar, Card, ChatTemplate, Checkbox_default, CheckoutTemplate, ColoredBadge, ContainerFillChart, CountBadge, DashboardTemplate, DataTable, DataTablePage, DatePicker_default, DateRangePicker, DateTimePicker_default, DescriptionList, Divider, DonutChart, Drawer, DropdownMenu, ENTER, EmailTemplate, EmptyState, ErrorBoundary, ErrorPage, FilePicker, FormField, FormLayoutPage, GROUP_COLORS, GalleryTemplate, GlobalSearch, Grid, HelpCenter, INPUT_BASE, INPUT_SIZES, IconButton_default, Inline, InputNumber_default, Input_default, Kanban, Label, LineChart, ListFooter, ListLoadError, LoadingSpinner, MAX_FRACTION_DIGITS, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NativeSelect, NumericKeypad, PageHeader, Pagination, Paragraph, Radio_default, Result, SHIFT, SearchableSelect, Segmented, Select_default, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, Skeleton, Sparkline, Stack, StatCard, Statistic, StatusBadge, StatusBadgeProvider, Stepper, Switch_default, Tabs, TagInput, Text, Textarea_default, TileButton_default, TimePicker_default, Title, Tooltip, TopNav, appendKey, applyThemePrefs, backspace, firstEnabledIndex, formatDate, fromKey, inputClasses, isMac, isSeverityTone, lastEnabledIndex, matchTypeahead, mediaFileName, nextEnabledIndex, resolveTheme, severityOf, tabButtonId, tabPanelId, toISODate, toKey, useClickOutside, useTheme };
6425
+ //# sourceMappingURL=chunk-B3IYV7JN.js.map
6426
+ //# sourceMappingURL=chunk-B3IYV7JN.js.map