react-os-shell 4.55.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 (52) hide show
  1. package/README.md +7 -3
  2. package/dist/{Browser-WHBSYGLC.js → Browser-NBYPPENQ.js} +6 -6
  3. package/dist/{Browser-WHBSYGLC.js.map → Browser-NBYPPENQ.js.map} +1 -1
  4. package/dist/{Calculator-PHKNMRLX.js → Calculator-UNAVT7IE.js} +5 -5
  5. package/dist/{Calculator-PHKNMRLX.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-MEG7RHEK.js → CurrencyConverter-KURXJHLK.js} +5 -5
  9. package/dist/{CurrencyConverter-MEG7RHEK.js.map → CurrencyConverter-KURXJHLK.js.map} +1 -1
  10. package/dist/{Documents-Y32RNCTU.js → Documents-7CUBCETC.js} +6 -6
  11. package/dist/{Documents-Y32RNCTU.js.map → Documents-7CUBCETC.js.map} +1 -1
  12. package/dist/{Files-LTG2S2KU.js → Files-UXEDRQF4.js} +6 -6
  13. package/dist/{Files-LTG2S2KU.js.map → Files-UXEDRQF4.js.map} +1 -1
  14. package/dist/{Notepad-P7J2OAXD.js → Notepad-ON4T45UP.js} +6 -6
  15. package/dist/{Notepad-P7J2OAXD.js.map → Notepad-ON4T45UP.js.map} +1 -1
  16. package/dist/{PomodoroTimer-5B5AFXQH.js → PomodoroTimer-J7V5VXR4.js} +5 -5
  17. package/dist/{PomodoroTimer-5B5AFXQH.js.map → PomodoroTimer-J7V5VXR4.js.map} +1 -1
  18. package/dist/{Preview-XZQ55YGY.js → Preview-QINOPDT2.js} +6 -6
  19. package/dist/{Preview-XZQ55YGY.js.map → Preview-QINOPDT2.js.map} +1 -1
  20. package/dist/{Spreadsheet-KKSLCSIK.js → Spreadsheet-OFF3J4HN.js} +6 -6
  21. package/dist/{Spreadsheet-KKSLCSIK.js.map → Spreadsheet-OFF3J4HN.js.map} +1 -1
  22. package/dist/{Stock-KTWRNBI6.js → Stock-BSU7S7MW.js} +5 -5
  23. package/dist/{Stock-KTWRNBI6.js.map → Stock-BSU7S7MW.js.map} +1 -1
  24. package/dist/{Weather-B7MO7N4N.js → Weather-BKNRVMG6.js} +5 -5
  25. package/dist/{Weather-B7MO7N4N.js.map → Weather-BKNRVMG6.js.map} +1 -1
  26. package/dist/{WorldClock-M5MEV74F.js → WorldClock-VMTD2QWD.js} +5 -5
  27. package/dist/{WorldClock-M5MEV74F.js.map → WorldClock-VMTD2QWD.js.map} +1 -1
  28. package/dist/apps/index.js +16 -16
  29. package/dist/{chunk-CTTHBTOX.js → chunk-B3IYV7JN.js} +1052 -334
  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-FHPPPDHL.js → chunk-E2N5WEHI.js} +3 -3
  34. package/dist/{chunk-FHPPPDHL.js.map → chunk-E2N5WEHI.js.map} +1 -1
  35. package/dist/{chunk-5S65ZC5W.js → chunk-GRSVL6WX.js} +83 -9
  36. package/dist/chunk-GRSVL6WX.js.map +1 -0
  37. package/dist/{chunk-S3ZIBWRY.js → chunk-HAFM6ZQD.js} +4 -4
  38. package/dist/{chunk-S3ZIBWRY.js.map → chunk-HAFM6ZQD.js.map} +1 -1
  39. package/dist/chunk-K35QFOJF.js +7 -0
  40. package/dist/{chunk-LCLB7YBY.js.map → chunk-K35QFOJF.js.map} +1 -1
  41. package/dist/{index-BE0z2D6y.d.ts → index-DM-d12A7.d.ts} +206 -7
  42. package/dist/index.d.ts +29 -8
  43. package/dist/index.js +58 -20
  44. package/dist/index.js.map +1 -1
  45. package/dist/ui/index.d.ts +1 -1
  46. package/dist/ui/index.js +3 -3
  47. package/package.json +1 -1
  48. package/dist/ConfirmDialog-EEH4CWSW.js +0 -3
  49. package/dist/chunk-5S65ZC5W.js.map +0 -1
  50. package/dist/chunk-CTTHBTOX.js.map +0 -1
  51. package/dist/chunk-LCLB7YBY.js +0 -7
  52. package/dist/chunk-OXLLRKBC.js.map +0 -1
@@ -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 pad2 = `${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: [pad2, 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,37 +1607,315 @@ function DateRangePicker({
1469
1607
  )
1470
1608
  ] });
1471
1609
  }
1472
- var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
1473
- function toInputValue(value) {
1610
+ var TIME = /^\d{2}:\d{2}(:\d{2})?$/;
1611
+ var pad3 = (n) => String(n).padStart(2, "0");
1612
+ function toInputValue(value, withSeconds) {
1613
+ if (value === null || value === void 0 || value === "") return "";
1614
+ if (value instanceof Date) {
1615
+ if (Number.isNaN(value.getTime())) return "";
1616
+ const base = `${pad3(value.getHours())}:${pad3(value.getMinutes())}`;
1617
+ return withSeconds ? `${base}:${pad3(value.getSeconds())}` : base;
1618
+ }
1619
+ return TIME.test(value) ? value : "";
1620
+ }
1621
+ var TimePicker = forwardRef(function TimePicker2({ value, onChange, min, max, invalid, size, className = "", step, ...rest }, ref) {
1622
+ const withSeconds = step != null && Number(step) < 60;
1623
+ return /* @__PURE__ */ jsx(
1624
+ "input",
1625
+ {
1626
+ ref,
1627
+ type: "time",
1628
+ step,
1629
+ "aria-invalid": invalid || void 0,
1630
+ value: toInputValue(value, withSeconds),
1631
+ min: toInputValue(min, withSeconds) || void 0,
1632
+ max: toInputValue(max, withSeconds) || void 0,
1633
+ onChange: (event) => onChange?.(TIME.test(event.target.value) ? event.target.value : null),
1634
+ className: inputClasses({ invalid, size, className }),
1635
+ ...rest
1636
+ }
1637
+ );
1638
+ });
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;
1882
+ var ISO_LOCAL = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?$/;
1883
+ var pad4 = (n) => String(n).padStart(2, "0");
1884
+ function serialise(date) {
1885
+ return `${date.getFullYear()}-${pad4(date.getMonth() + 1)}-${pad4(date.getDate())}T${pad4(date.getHours())}:${pad4(date.getMinutes())}`;
1886
+ }
1887
+ function toInputValue3(value) {
1474
1888
  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);
1889
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? "" : serialise(value);
1890
+ if (ISO_LOCAL.test(value)) return value;
1891
+ return "";
1479
1892
  }
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);
1893
+ function fromInputValue2(raw) {
1894
+ if (!ISO_LOCAL.test(raw)) return null;
1895
+ const [datePart, timePart] = raw.split("T");
1896
+ const [year, month, day] = datePart.split("-").map(Number);
1897
+ const [hours, minutes, seconds = 0] = timePart.split(":").map(Number);
1898
+ if (hours > 23 || minutes > 59 || seconds > 59) return null;
1899
+ const date = new Date(year, month - 1, day, hours, minutes, seconds);
1484
1900
  return date.getMonth() === month - 1 && date.getDate() === day ? date : null;
1485
1901
  }
1486
- var DatePicker = forwardRef(function DatePicker2({ value, onChange, min, max, invalid, size, className = "", ...rest }, ref) {
1902
+ var DateTimePicker = forwardRef(function DateTimePicker2({ value, onChange, min, max, invalid, size, className = "", ...rest }, ref) {
1487
1903
  return /* @__PURE__ */ jsx(
1488
1904
  "input",
1489
1905
  {
1490
1906
  ref,
1491
- type: "date",
1907
+ type: "datetime-local",
1492
1908
  "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)),
1909
+ value: toInputValue3(value),
1910
+ min: toInputValue3(min) || void 0,
1911
+ max: toInputValue3(max) || void 0,
1912
+ onChange: (event) => onChange?.(fromInputValue2(event.target.value)),
1497
1913
  className: inputClasses({ invalid, size, className }),
1498
1914
  ...rest
1499
1915
  }
1500
1916
  );
1501
1917
  });
1502
- var DatePicker_default = DatePicker;
1918
+ var DateTimePicker_default = DateTimePicker;
1503
1919
 
1504
1920
  // src/forms/keypadInput.ts
1505
1921
  var MAX_FRACTION_DIGITS = 2;
@@ -1610,8 +2026,8 @@ function Segmented({
1610
2026
  className = ""
1611
2027
  }) {
1612
2028
  const groupId = useId();
1613
- const track = `inline-flex items-center gap-1 rounded-lg bg-gray-100 p-1 ${block ? "w-full" : ""} ${className}`.trim();
1614
- 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" : ""}`;
1615
2031
  if (name) {
1616
2032
  return /* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-label": label, className: track, children: options.map((opt) => {
1617
2033
  const id = `${groupId}-${opt.value}`;
@@ -1962,57 +2378,6 @@ function FilePicker({
1962
2378
  error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-red-600", children: error })
1963
2379
  ] });
1964
2380
  }
1965
- var POPUP_MAX_WIDTH = 448;
1966
- var MENU_MAX_HEIGHT2 = 240;
1967
- var MENU_GAP2 = 4;
1968
- var VIEWPORT_MARGIN2 = 8;
1969
- function useDropdownPosition(triggerRef, open) {
1970
- const [pos, setPos] = useState(null);
1971
- useLayoutEffect(() => {
1972
- if (!open) {
1973
- setPos(null);
1974
- return;
1975
- }
1976
- let lastLeft = NaN, lastTop = NaN, lastRight = NaN, lastBottom = NaN;
1977
- const compute = () => {
1978
- const rect = triggerRef.current?.getBoundingClientRect();
1979
- if (!rect) return;
1980
- lastLeft = rect.left;
1981
- lastTop = rect.top;
1982
- lastRight = rect.right;
1983
- lastBottom = rect.bottom;
1984
- const spaceBelow = window.innerHeight - rect.bottom - MENU_GAP2 - VIEWPORT_MARGIN2;
1985
- const spaceAbove = rect.top - MENU_GAP2 - VIEWPORT_MARGIN2;
1986
- const placeAbove = spaceBelow < Math.min(MENU_MAX_HEIGHT2, 160) && spaceAbove > spaceBelow;
1987
- const maxHeight = Math.max(96, Math.min(MENU_MAX_HEIGHT2, placeAbove ? spaceAbove : spaceBelow));
1988
- const next = { minWidth: rect.width, maxHeight };
1989
- if (window.innerWidth - rect.left < POPUP_MAX_WIDTH) {
1990
- next.right = Math.max(VIEWPORT_MARGIN2, window.innerWidth - rect.right);
1991
- } else {
1992
- next.left = Math.max(VIEWPORT_MARGIN2, rect.left);
1993
- }
1994
- if (placeAbove) next.bottom = window.innerHeight - rect.top + MENU_GAP2;
1995
- else next.top = rect.bottom + MENU_GAP2;
1996
- setPos(next);
1997
- };
1998
- compute();
1999
- let raf = requestAnimationFrame(function tick() {
2000
- const rect = triggerRef.current?.getBoundingClientRect();
2001
- if (rect && (rect.left !== lastLeft || rect.top !== lastTop || rect.right !== lastRight || rect.bottom !== lastBottom)) {
2002
- compute();
2003
- }
2004
- raf = requestAnimationFrame(tick);
2005
- });
2006
- window.addEventListener("scroll", compute, true);
2007
- window.addEventListener("resize", compute);
2008
- return () => {
2009
- cancelAnimationFrame(raf);
2010
- window.removeEventListener("scroll", compute, true);
2011
- window.removeEventListener("resize", compute);
2012
- };
2013
- }, [open, triggerRef]);
2014
- return pos;
2015
- }
2016
2381
  function ClearButton({ onClear, ariaLabel = "Clear selection" }) {
2017
2382
  return /* @__PURE__ */ jsx(
2018
2383
  "button",
@@ -2162,7 +2527,7 @@ function SearchableSelect({
2162
2527
  "div",
2163
2528
  {
2164
2529
  ref: menuRef,
2165
- className: "fixed z-[400] rounded-2xl overflow-hidden",
2530
+ className: "fixed z-[10000] rounded-2xl overflow-hidden",
2166
2531
  style: {
2167
2532
  left: menuPos?.left,
2168
2533
  right: menuPos?.right,
@@ -2199,6 +2564,171 @@ function SearchableSelect({
2199
2564
  )
2200
2565
  ] });
2201
2566
  }
2567
+ function TagInput({
2568
+ value,
2569
+ onChange,
2570
+ options = [],
2571
+ placeholder,
2572
+ allowFreeText = false,
2573
+ invalid,
2574
+ disabled,
2575
+ id,
2576
+ className = ""
2577
+ }) {
2578
+ const [open, setOpen] = useState(false);
2579
+ const [search, setSearch] = useState("");
2580
+ const wrapRef = useRef(null);
2581
+ const inputRef = useRef(null);
2582
+ const menuRef = useRef(null);
2583
+ const menuPos = useDropdownPosition(wrapRef, open);
2584
+ const labelFor = (v) => options.find((o) => o.value === v)?.label ?? v;
2585
+ const available = useMemo(() => {
2586
+ const seen = new Set(value);
2587
+ const out = [];
2588
+ const q = search.trim().toLowerCase();
2589
+ for (const o of options) {
2590
+ if (seen.has(o.value)) continue;
2591
+ seen.add(o.value);
2592
+ if (!q || o.label.toLowerCase().includes(q)) out.push(o);
2593
+ }
2594
+ return out;
2595
+ }, [options, value, search]);
2596
+ const add = (v) => {
2597
+ if (!v || value.includes(v)) {
2598
+ setSearch("");
2599
+ return;
2600
+ }
2601
+ onChange([...value, v]);
2602
+ setSearch("");
2603
+ };
2604
+ const remove = (v) => onChange(value.filter((x) => x !== v));
2605
+ const commitFreeText = () => {
2606
+ const t = search.trim();
2607
+ if (allowFreeText && t) add(t);
2608
+ else setSearch("");
2609
+ };
2610
+ useEffect(() => {
2611
+ if (!open) return;
2612
+ const handler = (e) => {
2613
+ const t = e.target;
2614
+ if (wrapRef.current?.contains(t) || menuRef.current?.contains(t)) return;
2615
+ commitFreeText();
2616
+ setOpen(false);
2617
+ };
2618
+ document.addEventListener("mousedown", handler);
2619
+ return () => document.removeEventListener("mousedown", handler);
2620
+ });
2621
+ const hasMenu = options.length > 0;
2622
+ return /* @__PURE__ */ jsxs("div", { ref: wrapRef, className: "relative", children: [
2623
+ /* @__PURE__ */ jsxs(
2624
+ "div",
2625
+ {
2626
+ onMouseDown: (e) => {
2627
+ if (disabled) return;
2628
+ if (e.target.closest("button")) return;
2629
+ e.preventDefault();
2630
+ inputRef.current?.focus();
2631
+ },
2632
+ className: inputClasses({
2633
+ invalid,
2634
+ className: `flex flex-wrap items-center gap-1 cursor-text focus-within:border-blue-400 focus-within:ring-2 focus-within:ring-blue-400/30 ${disabled ? "bg-gray-50 cursor-not-allowed" : ""} ${className}`
2635
+ }),
2636
+ children: [
2637
+ value.map((v) => /* @__PURE__ */ jsxs("span", { className: "inline-flex max-w-full items-center gap-1 rounded bg-blue-50 px-1.5 py-0.5 text-xs font-medium text-blue-700", children: [
2638
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: labelFor(v) }),
2639
+ !disabled && /* @__PURE__ */ jsx(
2640
+ "button",
2641
+ {
2642
+ type: "button",
2643
+ "aria-label": `Remove ${labelFor(v)}`,
2644
+ onClick: () => remove(v),
2645
+ className: "text-blue-400 hover:text-red-500 focus:outline-none focus-visible:ring-1 focus-visible:ring-blue-500 rounded leading-none",
2646
+ children: "\xD7"
2647
+ }
2648
+ )
2649
+ ] }, v)),
2650
+ /* @__PURE__ */ jsx(
2651
+ "input",
2652
+ {
2653
+ id,
2654
+ ref: inputRef,
2655
+ type: "text",
2656
+ autoComplete: "off",
2657
+ role: "combobox",
2658
+ "aria-expanded": open && hasMenu,
2659
+ "aria-invalid": invalid || void 0,
2660
+ disabled,
2661
+ value: search,
2662
+ placeholder: value.length === 0 ? placeholder : void 0,
2663
+ onChange: (e) => {
2664
+ setSearch(e.target.value);
2665
+ setOpen(true);
2666
+ },
2667
+ onFocus: () => {
2668
+ if (!disabled && hasMenu) setOpen(true);
2669
+ },
2670
+ onKeyDown: (e) => {
2671
+ if (e.key === "Enter" || e.key === "," && allowFreeText) {
2672
+ e.preventDefault();
2673
+ const q = search.trim();
2674
+ const matches = q ? available.filter((o) => o.label.toLowerCase().includes(q.toLowerCase())) : [];
2675
+ if (matches.length === 1) add(matches[0].value);
2676
+ else commitFreeText();
2677
+ } else if (e.key === "Backspace" && search === "" && value.length > 0) {
2678
+ remove(value[value.length - 1]);
2679
+ } else if (e.key === "Escape") {
2680
+ setOpen(false);
2681
+ setSearch("");
2682
+ inputRef.current?.blur();
2683
+ } else if (e.key === "Tab") {
2684
+ commitFreeText();
2685
+ setOpen(false);
2686
+ }
2687
+ },
2688
+ className: "min-w-[8ch] flex-1 border-none bg-transparent p-0 text-sm text-gray-800 placeholder:text-gray-400 focus:outline-none focus:ring-0 disabled:cursor-not-allowed"
2689
+ }
2690
+ )
2691
+ ]
2692
+ }
2693
+ ),
2694
+ open && hasMenu && createPortal(
2695
+ /* @__PURE__ */ jsx(
2696
+ "div",
2697
+ {
2698
+ ref: menuRef,
2699
+ className: "fixed z-[10000] rounded-2xl overflow-hidden",
2700
+ style: {
2701
+ left: menuPos?.left,
2702
+ right: menuPos?.right,
2703
+ top: menuPos?.top,
2704
+ bottom: menuPos?.bottom,
2705
+ minWidth: menuPos?.minWidth,
2706
+ maxWidth: POPUP_MAX_WIDTH,
2707
+ width: "max-content",
2708
+ // Hidden for the first paint until the layout effect measures the
2709
+ // trigger, so the menu never flashes at (0,0).
2710
+ visibility: menuPos ? void 0 : "hidden",
2711
+ ...glassStyle()
2712
+ },
2713
+ children: /* @__PURE__ */ jsx("div", { className: "overflow-y-auto", style: { maxHeight: menuPos?.maxHeight ?? MENU_MAX_HEIGHT2 }, children: available.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-3 text-sm text-gray-400 text-center", children: options.length > 0 && value.length >= options.length ? "All options selected" : "No matches" }) : available.map((o) => /* @__PURE__ */ jsx(
2714
+ "button",
2715
+ {
2716
+ type: "button",
2717
+ onMouseDown: (e) => {
2718
+ e.preventDefault();
2719
+ add(o.value);
2720
+ },
2721
+ className: "w-full text-left px-3 py-1.5 text-sm hover:bg-gray-50 text-gray-700 whitespace-nowrap",
2722
+ children: o.label
2723
+ },
2724
+ o.value
2725
+ )) })
2726
+ }
2727
+ ),
2728
+ document.body
2729
+ )
2730
+ ] });
2731
+ }
2202
2732
  var PADDING = {
2203
2733
  none: { body: "", edge: "px-4 py-3" },
2204
2734
  sm: { body: "p-3", edge: "px-3 py-2" },
@@ -2482,23 +3012,23 @@ function DescriptionList({
2482
3012
  const resolved = typeof columns === "number" ? { base: columns } : columns;
2483
3013
  const base = resolved.base ?? 1;
2484
3014
  const widest = Math.max(base, resolved.sm ?? 0, resolved.lg ?? 0);
2485
- const pad2 = size === "sm" ? "px-3 py-2" : "px-4 py-3";
3015
+ const pad5 = size === "sm" ? "px-3 py-2" : "px-4 py-3";
2486
3016
  const text = size === "sm" ? "text-xs" : "text-sm";
2487
3017
  return /* @__PURE__ */ jsxs("div", { className: [bordered ? "rounded-lg border border-gray-200 bg-white" : "", className].filter(Boolean).join(" "), children: [
2488
- title && /* @__PURE__ */ jsx("div", { className: `${pad2} ${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 }),
2489
3019
  /* @__PURE__ */ jsx("dl", { className: [
2490
3020
  "grid",
2491
3021
  COLS2[base],
2492
3022
  resolved.sm ? SM[resolved.sm] : "",
2493
3023
  resolved.lg ? LG[resolved.lg] : "",
2494
3024
  bordered ? "" : "gap-x-6 gap-y-3",
2495
- bordered ? "" : pad2 === "px-4 py-3" ? "py-1" : ""
3025
+ bordered ? "" : pad5 === "px-4 py-3" ? "py-1" : ""
2496
3026
  ].filter(Boolean).join(" "), children: items.map((item, i) => /* @__PURE__ */ jsxs(
2497
3027
  "div",
2498
3028
  {
2499
3029
  className: [
2500
3030
  item.span ? SPAN[widest] : "",
2501
- bordered ? `${pad2} border-b border-gray-100 last:border-b-0` : ""
3031
+ bordered ? `${pad5} border-b border-gray-100 last:border-b-0` : ""
2502
3032
  ].filter(Boolean).join(" "),
2503
3033
  children: [
2504
3034
  /* @__PURE__ */ jsx("dt", { className: `${text} text-gray-500`, children: item.label }),
@@ -2867,6 +3397,69 @@ function Tabs({
2867
3397
  }
2868
3398
  );
2869
3399
  }
3400
+ var Check = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", fill: "none", className: "h-3.5 w-3.5", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M2.5 6.5L5 9l4.5-5", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) });
3401
+ function Stepper({
3402
+ items,
3403
+ value,
3404
+ onChange,
3405
+ "aria-label": ariaLabel,
3406
+ className = ""
3407
+ }) {
3408
+ const currentIndex = Math.max(0, items.findIndex((s) => s.id === value));
3409
+ return /* @__PURE__ */ jsx("ol", { "aria-label": ariaLabel, className: `flex items-start ${className}`.trim(), children: items.map((step, i) => {
3410
+ const state = i < currentIndex ? "complete" : i === currentIndex ? "current" : "upcoming";
3411
+ const clickable = state === "complete" && !!onChange;
3412
+ const circle = /* @__PURE__ */ jsx(
3413
+ "span",
3414
+ {
3415
+ "aria-hidden": "true",
3416
+ className: [
3417
+ "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-semibold transition-colors",
3418
+ state === "complete" ? "bg-blue-600 text-white" : "",
3419
+ state === "current" ? "border-2 border-blue-600 bg-white text-blue-600" : "",
3420
+ state === "upcoming" ? "border-2 border-gray-300 bg-white text-gray-400" : ""
3421
+ ].filter(Boolean).join(" "),
3422
+ children: state === "complete" ? /* @__PURE__ */ jsx(Check, {}) : i + 1
3423
+ }
3424
+ );
3425
+ const text = /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
3426
+ /* @__PURE__ */ jsx("span", { className: `block text-sm font-medium ${state === "upcoming" ? "text-gray-400" : "text-gray-900"}`, children: step.label }),
3427
+ step.description && /* @__PURE__ */ jsx("span", { className: "block text-xs text-gray-500", children: step.description })
3428
+ ] });
3429
+ return /* @__PURE__ */ jsxs(
3430
+ "li",
3431
+ {
3432
+ "aria-current": state === "current" ? "step" : void 0,
3433
+ className: `flex items-start gap-2 ${i > 0 ? "flex-1" : ""}`,
3434
+ children: [
3435
+ i > 0 && /* @__PURE__ */ jsx(
3436
+ "span",
3437
+ {
3438
+ "aria-hidden": "true",
3439
+ className: `mt-3.5 h-0.5 flex-1 rounded ${i <= currentIndex ? "bg-blue-600" : "bg-gray-200"}`
3440
+ }
3441
+ ),
3442
+ clickable ? /* @__PURE__ */ jsxs(
3443
+ "button",
3444
+ {
3445
+ type: "button",
3446
+ onClick: () => onChange?.(step.id),
3447
+ className: "flex items-start gap-2 text-left rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400",
3448
+ children: [
3449
+ circle,
3450
+ text
3451
+ ]
3452
+ }
3453
+ ) : /* @__PURE__ */ jsxs("span", { className: "flex items-start gap-2", children: [
3454
+ circle,
3455
+ text
3456
+ ] })
3457
+ ]
3458
+ },
3459
+ step.id
3460
+ );
3461
+ }) });
3462
+ }
2870
3463
  function Accordion({
2871
3464
  items,
2872
3465
  defaultOpenIds = [],
@@ -3287,8 +3880,8 @@ function formatDate(value) {
3287
3880
  const dd = String(d).padStart(2, "0");
3288
3881
  const mm = String(m).padStart(2, "0");
3289
3882
  const yyyy = String(y);
3290
- const fmt = getUserDateFormat();
3291
- switch (fmt) {
3883
+ const fmt2 = getUserDateFormat();
3884
+ switch (fmt2) {
3292
3885
  case "MM/DD/YYYY":
3293
3886
  return `${mm}/${dd}/${yyyy}`;
3294
3887
  case "YYYY-MM-DD":
@@ -4189,12 +4782,28 @@ function DropdownMenu({
4189
4782
  )
4190
4783
  ] });
4191
4784
  }
4192
- 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
+ };
4193
4790
  var SIDE_POSITION = {
4194
4791
  right: "inset-y-0 right-0",
4195
4792
  left: "inset-y-0 left-0",
4196
4793
  bottom: "inset-x-0 bottom-0"
4197
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
+ }
4198
4807
  function Drawer({
4199
4808
  open,
4200
4809
  onClose,
@@ -4205,10 +4814,12 @@ function Drawer({
4205
4814
  children,
4206
4815
  blocking = false,
4207
4816
  initialFocus,
4817
+ "aria-label": ariaLabel,
4208
4818
  className = ""
4209
4819
  }) {
4210
4820
  const panelRef = useRef(null);
4211
4821
  const bodyId = useId();
4822
+ const titleId = useId();
4212
4823
  useFocusTrap(panelRef, open, initialFocus);
4213
4824
  useScrollLock(open);
4214
4825
  useEffect(() => {
@@ -4221,7 +4832,7 @@ function Drawer({
4221
4832
  }, [open, blocking, onClose]);
4222
4833
  if (!open) return null;
4223
4834
  const isBottom = side === "bottom";
4224
- const panelStyle = isBottom ? { maxHeight: "85vh" } : { width: SIZE_PX2[size], maxWidth: "100vw" };
4835
+ const panelStyle = isBottom ? { maxHeight: "85vh" } : void 0;
4225
4836
  return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[9999]", role: "presentation", children: [
4226
4837
  /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black/30", onClick: blocking ? void 0 : onClose, "aria-hidden": "true" }),
4227
4838
  /* @__PURE__ */ jsxs(
@@ -4230,29 +4841,27 @@ function Drawer({
4230
4841
  ref: panelRef,
4231
4842
  role: "dialog",
4232
4843
  "aria-modal": "true",
4233
- "aria-label": typeof title === "string" ? title : void 0,
4844
+ "aria-labelledby": title ? titleId : void 0,
4845
+ "aria-label": title ? void 0 : ariaLabel,
4234
4846
  "aria-describedby": children ? bodyId : void 0,
4235
4847
  style: panelStyle,
4236
4848
  className: [
4237
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]}`,
4238
4856
  SIDE_POSITION[side],
4239
4857
  isBottom ? "rounded-t-xl" : "",
4240
4858
  className
4241
4859
  ].filter(Boolean).join(" "),
4242
4860
  children: [
4243
- (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: [
4244
- title ? /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-gray-900", children: title }) : /* @__PURE__ */ jsx("span", {}),
4245
- !blocking && /* @__PURE__ */ jsx(
4246
- "button",
4247
- {
4248
- type: "button",
4249
- onClick: onClose,
4250
- "aria-label": "Close",
4251
- className: "rounded-md p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600",
4252
- 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" }) })
4253
- }
4254
- )
4255
- ] }),
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 }) }),
4256
4865
  /* @__PURE__ */ jsx("div", { id: bodyId, className: "min-h-0 flex-1 overflow-y-auto px-4 py-4", children }),
4257
4866
  footer && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 justify-end gap-3 border-t border-gray-100 px-4 py-3", children: footer })
4258
4867
  ]
@@ -4664,9 +5273,28 @@ function DataTable({
4664
5273
  onRow,
4665
5274
  emptyText = "Nothing to show",
4666
5275
  footer,
5276
+ virtualized,
4667
5277
  className = ""
4668
5278
  }) {
4669
5279
  const keyOf = (row, i) => typeof rowKey === "function" ? rowKey(row, i) : String(row[rowKey]);
5280
+ const [startRow, setStartRow] = useState(0);
5281
+ const overscan = virtualized?.overscan ?? 6;
5282
+ const onBodyScroll = virtualized ? (e) => {
5283
+ const next = Math.max(0, Math.floor(e.currentTarget.scrollTop / virtualized.rowHeight) - overscan);
5284
+ setStartRow((prev) => prev === next ? prev : next);
5285
+ } : void 0;
5286
+ const windowSize = virtualized ? Math.ceil(virtualized.height / virtualized.rowHeight) + overscan * 2 : data.length;
5287
+ const start = virtualized ? Math.min(startRow, Math.max(0, data.length - windowSize)) : 0;
5288
+ const end = Math.min(data.length, start + windowSize);
5289
+ const topPad = virtualized ? start * virtualized.rowHeight : 0;
5290
+ const bottomPad = virtualized ? (data.length - end) * virtualized.rowHeight : 0;
5291
+ const headRowRef = useRef(null);
5292
+ const [headRowH, setHeadRowH] = useState(0);
5293
+ useLayoutEffect(() => {
5294
+ if (!virtualized) return;
5295
+ const h = headRowRef.current?.offsetHeight ?? 0;
5296
+ setHeadRowH((prev) => prev === h ? prev : h);
5297
+ });
4670
5298
  const fieldOf = (col) => col.sortField ?? col.key;
4671
5299
  const directionFor = (col) => sort && sort.field === fieldOf(col) ? sort.direction : null;
4672
5300
  const toggleSort = (col) => {
@@ -4682,7 +5310,7 @@ function DataTable({
4682
5310
  const leaves = columns.flatMap((h) => isGroup(h) ? h.columns : [h]);
4683
5311
  const grouped = columns.some(isGroup);
4684
5312
  const cellBase = `${PAD[size]} ${size === "sm" ? "text-xs" : "text-sm"}`;
4685
- const headerCell = (col, ci, rowSpan) => {
5313
+ const headerCell = (col, ci, rowSpan, topOffset = 0) => {
4686
5314
  const dir = directionFor(col);
4687
5315
  const align = ALIGN2[col.align ?? (col.numeric ? "right" : "left")];
4688
5316
  const pinned = col.fixed === "left";
@@ -4692,13 +5320,20 @@ function DataTable({
4692
5320
  scope: "col",
4693
5321
  rowSpan: rowSpan > 1 ? rowSpan : void 0,
4694
5322
  "aria-sort": dir === "asc" ? "ascending" : dir === "desc" ? "descending" : col.sortable && onSortChange ? "none" : void 0,
4695
- style: { width: col.width, ...pinned ? { left: offsets[ci] ?? 0 } : {} },
5323
+ style: {
5324
+ width: col.width,
5325
+ ...pinned ? { left: offsets[ci] ?? 0 } : {},
5326
+ // A virtualized header pins to the top of the vertical scroll; one
5327
+ // sticky box can hold both axes, so a pinned column's header corner
5328
+ // stays put in both directions.
5329
+ ...virtualized ? { top: topOffset } : {}
5330
+ },
4696
5331
  className: [
4697
5332
  cellBase,
4698
5333
  align,
4699
5334
  "font-semibold uppercase tracking-wide text-gray-500",
4700
5335
  size === "sm" ? "text-[10px]" : "text-xs",
4701
- pinned ? "sticky z-10 bg-gray-50" : "",
5336
+ pinned ? virtualized ? "sticky z-20 bg-gray-50" : "sticky z-10 bg-gray-50" : virtualized ? "sticky z-10 bg-gray-50" : "",
4702
5337
  col.headerClassName ?? ""
4703
5338
  ].filter(Boolean).join(" "),
4704
5339
  children: col.sortable && onSortChange ? /* @__PURE__ */ jsxs(
@@ -4725,92 +5360,108 @@ function DataTable({
4725
5360
  return at;
4726
5361
  });
4727
5362
  return /* @__PURE__ */ jsxs("div", { className, children: [
4728
- /* @__PURE__ */ jsxs("div", { className: `relative overflow-x-auto ${bordered ? "rounded-lg border border-gray-200" : ""}`.trim(), children: [
4729
- loading && // An overlay rather than replacing the rows: the table keeps its
4730
- // height and its scroll position, so re-sorting does not throw the
4731
- // page around under the user.
4732
- /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 z-20 flex items-start justify-center bg-white/60 pt-8", role: "status", "aria-live": "polite", children: [
4733
- /* @__PURE__ */ jsx("span", { className: "h-6 w-6 animate-spin rounded-full border-2 border-gray-200 border-t-blue-600" }),
4734
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading" })
4735
- ] }),
4736
- /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse tabular-nums", style: minWidth ? { minWidth } : void 0, children: [
4737
- caption && /* @__PURE__ */ jsx("caption", { className: "sr-only", children: caption }),
4738
- /* @__PURE__ */ jsxs("thead", { children: [
4739
- /* @__PURE__ */ jsx("tr", { className: "border-b border-gray-200 bg-gray-50", children: columns.map((h, hi) => {
4740
- if (isGroup(h)) {
4741
- return /* @__PURE__ */ jsx(
4742
- "th",
4743
- {
4744
- scope: "colgroup",
4745
- colSpan: h.columns.length,
4746
- className: [
4747
- cellBase,
4748
- "text-center",
4749
- "font-semibold uppercase tracking-wide text-gray-500",
4750
- size === "sm" ? "text-[10px]" : "text-xs",
4751
- "border-b border-gray-200",
4752
- h.headerClassName ?? ""
4753
- ].filter(Boolean).join(" "),
4754
- children: h.title
4755
- },
4756
- h.key ?? `group-${hi}`
4757
- );
4758
- }
4759
- return headerCell(h, leaves.indexOf(h), grouped ? 2 : 1);
4760
- }) }),
4761
- grouped && /* @__PURE__ */ jsx("tr", { className: "border-b border-gray-200 bg-gray-50", children: columns.flatMap((h) => isGroup(h) ? h.columns : []).map(
4762
- (col) => headerCell(col, leaves.indexOf(col), 1)
4763
- ) })
4764
- ] }),
4765
- /* @__PURE__ */ jsx("tbody", { children: data.length === 0 && !loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: leaves.length, className: `${cellBase} py-10 text-center text-gray-500`, children: emptyText }) }) : data.map((row, i) => {
4766
- const rowProps = onRow?.(row, i);
4767
- return /* @__PURE__ */ jsx(
4768
- "tr",
4769
- {
4770
- onClick: rowProps?.onClick,
4771
- ...rowProps?.onClick ? {
4772
- tabIndex: 0,
4773
- onKeyDown: (e) => {
4774
- if (e.target !== e.currentTarget) return;
4775
- if (e.key !== "Enter" && e.key !== " ") return;
4776
- e.preventDefault();
4777
- rowProps.onClick?.();
5363
+ /* @__PURE__ */ jsxs(
5364
+ "div",
5365
+ {
5366
+ onScroll: onBodyScroll,
5367
+ style: virtualized ? { maxHeight: virtualized.height } : void 0,
5368
+ className: `relative overflow-x-auto ${virtualized ? "overflow-y-auto" : ""} ${bordered ? "rounded-lg border border-gray-200" : ""}`.trim(),
5369
+ children: [
5370
+ loading && // An overlay rather than replacing the rows: the table keeps its
5371
+ // height and its scroll position, so re-sorting does not throw the
5372
+ // page around under the user.
5373
+ /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 z-20 flex items-start justify-center bg-white/60 pt-8", role: "status", "aria-live": "polite", children: [
5374
+ /* @__PURE__ */ jsx("span", { className: "h-6 w-6 animate-spin rounded-full border-2 border-gray-200 border-t-blue-600" }),
5375
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading" })
5376
+ ] }),
5377
+ /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse tabular-nums", style: minWidth ? { minWidth } : void 0, children: [
5378
+ caption && /* @__PURE__ */ jsx("caption", { className: "sr-only", children: caption }),
5379
+ /* @__PURE__ */ jsxs("thead", { children: [
5380
+ /* @__PURE__ */ jsx("tr", { ref: headRowRef, className: "border-b border-gray-200 bg-gray-50", children: columns.map((h, hi) => {
5381
+ if (isGroup(h)) {
5382
+ return /* @__PURE__ */ jsx(
5383
+ "th",
5384
+ {
5385
+ scope: "colgroup",
5386
+ colSpan: h.columns.length,
5387
+ style: virtualized ? { top: 0 } : void 0,
5388
+ className: [
5389
+ cellBase,
5390
+ "text-center",
5391
+ "font-semibold uppercase tracking-wide text-gray-500",
5392
+ size === "sm" ? "text-[10px]" : "text-xs",
5393
+ "border-b border-gray-200",
5394
+ virtualized ? "sticky z-10 bg-gray-50" : "",
5395
+ h.headerClassName ?? ""
5396
+ ].filter(Boolean).join(" "),
5397
+ children: h.title
5398
+ },
5399
+ h.key ?? `group-${hi}`
5400
+ );
4778
5401
  }
4779
- } : {},
4780
- className: [
4781
- "border-b border-gray-100 last:border-b-0",
4782
- rowProps?.onClick ? "cursor-pointer hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-400" : "",
4783
- rowClassName?.(row, i) ?? "",
4784
- rowProps?.className ?? ""
4785
- ].filter(Boolean).join(" "),
4786
- children: leaves.map((col, ci) => {
4787
- const pinned = col.fixed === "left";
5402
+ return headerCell(h, leaves.indexOf(h), grouped ? 2 : 1);
5403
+ }) }),
5404
+ grouped && /* @__PURE__ */ jsx("tr", { className: "border-b border-gray-200 bg-gray-50", children: columns.flatMap((h) => isGroup(h) ? h.columns : []).map(
5405
+ (col) => headerCell(col, leaves.indexOf(col), 1, headRowH)
5406
+ ) })
5407
+ ] }),
5408
+ /* @__PURE__ */ jsx("tbody", { children: data.length === 0 && !loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: leaves.length, className: `${cellBase} py-10 text-center text-gray-500`, children: emptyText }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5409
+ topPad > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx("td", { colSpan: leaves.length, style: { height: topPad, padding: 0, border: 0 } }) }),
5410
+ data.slice(start, end).map((row, si) => {
5411
+ const i = start + si;
5412
+ const rowProps = onRow?.(row, i);
4788
5413
  return /* @__PURE__ */ jsx(
4789
- "td",
5414
+ "tr",
4790
5415
  {
4791
- style: pinned ? { left: offsets[ci] ?? 0 } : void 0,
5416
+ style: virtualized ? { height: virtualized.rowHeight } : void 0,
5417
+ onClick: rowProps?.onClick,
5418
+ ...rowProps?.onClick ? {
5419
+ tabIndex: 0,
5420
+ onKeyDown: (e) => {
5421
+ if (e.target !== e.currentTarget) return;
5422
+ if (e.key !== "Enter" && e.key !== " ") return;
5423
+ e.preventDefault();
5424
+ rowProps.onClick?.();
5425
+ }
5426
+ } : {},
4792
5427
  className: [
4793
- cellBase,
4794
- ALIGN2[col.align ?? (col.numeric ? "right" : "left")],
4795
- col.numeric ? "font-mono" : "",
4796
- "text-gray-900",
4797
- col.ellipsis ? "max-w-0 truncate" : "",
4798
- // A pinned cell needs its own background or the
4799
- // scrolling columns show through it.
4800
- pinned ? "sticky z-10 bg-white" : ""
5428
+ "border-b border-gray-100 last:border-b-0",
5429
+ rowProps?.onClick ? "cursor-pointer hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-400" : "",
5430
+ rowClassName?.(row, i) ?? "",
5431
+ rowProps?.className ?? ""
4801
5432
  ].filter(Boolean).join(" "),
4802
- children: col.render ? col.render(row, i) : col.dataIndex ? row[col.dataIndex] : null
5433
+ children: leaves.map((col, ci) => {
5434
+ const pinned = col.fixed === "left";
5435
+ return /* @__PURE__ */ jsx(
5436
+ "td",
5437
+ {
5438
+ style: pinned ? { left: offsets[ci] ?? 0 } : void 0,
5439
+ className: [
5440
+ cellBase,
5441
+ ALIGN2[col.align ?? (col.numeric ? "right" : "left")],
5442
+ col.numeric ? "font-mono" : "",
5443
+ "text-gray-900",
5444
+ col.ellipsis ? "max-w-0 truncate" : "",
5445
+ // A pinned cell needs its own background or the
5446
+ // scrolling columns show through it.
5447
+ pinned ? "sticky z-10 bg-white" : ""
5448
+ ].filter(Boolean).join(" "),
5449
+ children: col.render ? col.render(row, i) : col.dataIndex ? row[col.dataIndex] : null
5450
+ },
5451
+ col.key
5452
+ );
5453
+ })
4803
5454
  },
4804
- col.key
5455
+ keyOf(row, i)
4805
5456
  );
4806
- })
4807
- },
4808
- keyOf(row, i)
4809
- );
4810
- }) })
4811
- ] }),
4812
- footer
4813
- ] }),
5457
+ }),
5458
+ bottomPad > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx("td", { colSpan: leaves.length, style: { height: bottomPad, padding: 0, border: 0 } }) })
5459
+ ] }) })
5460
+ ] }),
5461
+ footer
5462
+ ]
5463
+ }
5464
+ ),
4814
5465
  pagination && pagination.pageCount > 1 && /* @__PURE__ */ jsx("div", { className: "mt-3 flex justify-end", children: /* @__PURE__ */ jsx(
4815
5466
  Pagination,
4816
5467
  {
@@ -4867,8 +5518,8 @@ function Sparkline({
4867
5518
  const max = Math.max(...data);
4868
5519
  const min = Math.min(...data);
4869
5520
  const span = max - min || 1;
4870
- const pad2 = strokeWidth + (showDots ? 2 : 0);
4871
- const y = (v) => height - pad2 - (v - min) / span * (height - pad2 * 2);
5521
+ const pad5 = strokeWidth + (showDots ? 2 : 0);
5522
+ const y = (v) => height - pad5 - (v - min) / span * (height - pad5 * 2);
4872
5523
  const pts = data.length === 1 ? [[0, height / 2], [width, height / 2]] : data.map((v, i) => [i * width / (data.length - 1), y(v)]);
4873
5524
  const line = pts.map(([x, yy], i) => `${i === 0 ? "M" : "L"}${x.toFixed(2)},${yy.toFixed(2)}`).join(" ");
4874
5525
  const area = `${line} L${width.toFixed(2)},${height} L0,${height} Z`;
@@ -4960,6 +5611,73 @@ function DonutChart({ segments, size = 120, thickness = 16, centerLabel, classNa
4960
5611
  }
4961
5612
  );
4962
5613
  }
5614
+ function toPoints(data, min, span) {
5615
+ if (data.length === 1) return [[0, 100 - (data[0] - min) / span * 100], [100, 100 - (data[0] - min) / span * 100]];
5616
+ return data.map((v, i) => [i * 100 / (data.length - 1), 100 - (v - min) / span * 100]);
5617
+ }
5618
+ function pathFor(pts) {
5619
+ return pts.map(([x, y], i) => `${i === 0 ? "M" : "L"}${x.toFixed(2)},${y.toFixed(2)}`).join(" ");
5620
+ }
5621
+ var fmt = (v) => Number.isInteger(v) ? String(v) : v.toFixed(1);
5622
+ function LineChart({
5623
+ series,
5624
+ labels,
5625
+ height = 160,
5626
+ strokeWidth = 2,
5627
+ showDots = false,
5628
+ max,
5629
+ min,
5630
+ showScale = false,
5631
+ showLegend = false,
5632
+ className,
5633
+ style
5634
+ }) {
5635
+ const drawn = series.filter((s) => s.data.length > 0);
5636
+ if (drawn.length === 0) return null;
5637
+ const all = drawn.flatMap((s) => s.data);
5638
+ const top = max ?? Math.max(...all);
5639
+ const bottom = min ?? Math.min(...all);
5640
+ const span = top - bottom || 1;
5641
+ const color = (s) => s.color ?? "currentColor";
5642
+ return /* @__PURE__ */ jsxs("div", { className, style, children: [
5643
+ showLegend && drawn.some((s) => s.label) && /* @__PURE__ */ jsx("div", { className: "mb-1.5 flex flex-wrap items-center gap-x-3 gap-y-0.5", children: drawn.map((s, i) => s.label && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs text-gray-500", children: [
5644
+ /* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full", style: { backgroundColor: color(s) } }),
5645
+ s.label
5646
+ ] }, i)) }),
5647
+ /* @__PURE__ */ jsxs("div", { className: "flex", children: [
5648
+ showScale && /* @__PURE__ */ jsxs("div", { className: "flex flex-col justify-between pr-1.5 text-right text-[10px] leading-none text-gray-400", style: { height }, children: [
5649
+ /* @__PURE__ */ jsx("span", { children: fmt(top) }),
5650
+ /* @__PURE__ */ jsx("span", { children: fmt(bottom + span / 2) }),
5651
+ /* @__PURE__ */ jsx("span", { children: fmt(bottom) })
5652
+ ] }),
5653
+ /* @__PURE__ */ jsxs(
5654
+ "svg",
5655
+ {
5656
+ className: "min-w-0 flex-1",
5657
+ width: "100%",
5658
+ height,
5659
+ viewBox: "0 0 100 100",
5660
+ preserveAspectRatio: "none",
5661
+ role: "img",
5662
+ "aria-hidden": "true",
5663
+ children: [
5664
+ showScale && [0, 50, 100].map((y) => /* @__PURE__ */ jsx("line", { x1: 0, y1: y, x2: 100, y2: y, stroke: "currentColor", strokeOpacity: 0.12, strokeWidth: 1, vectorEffect: "non-scaling-stroke" }, y)),
5665
+ drawn.map((s, si) => {
5666
+ const pts = toPoints(s.data, bottom, span);
5667
+ const line = pathFor(pts);
5668
+ return /* @__PURE__ */ jsxs("g", { children: [
5669
+ s.fill && /* @__PURE__ */ jsx("path", { d: `${line} L100,100 L0,100 Z`, fill: s.fill, stroke: "none" }),
5670
+ /* @__PURE__ */ jsx("path", { d: line, fill: "none", stroke: color(s), strokeWidth, strokeLinejoin: "round", strokeLinecap: "round", vectorEffect: "non-scaling-stroke" }),
5671
+ showDots && s.data.length > 1 && pts.map(([x, y], i) => /* @__PURE__ */ jsx("path", { d: `M${x.toFixed(2)},${y.toFixed(2)} h0.01`, stroke: color(s), strokeWidth: strokeWidth * 2.5, strokeLinecap: "round", vectorEffect: "non-scaling-stroke", children: /* @__PURE__ */ jsx("title", { children: `${labels?.[i] != null ? `${labels[i]}: ` : ""}${s.label ? `${s.label} ` : ""}${s.data[i]}` }) }, i))
5672
+ ] }, si);
5673
+ })
5674
+ ]
5675
+ }
5676
+ )
5677
+ ] }),
5678
+ labels && /* @__PURE__ */ jsx("div", { className: `mt-1 flex justify-between ${showScale ? "pl-6" : ""}`, children: labels.map((l, i) => /* @__PURE__ */ jsx("div", { className: "truncate text-[10px] text-gray-400", children: l }, i)) })
5679
+ ] });
5680
+ }
4963
5681
  var STATUS_GROUPS = {
4964
5682
  paid: "success",
4965
5683
  shipped: "active",
@@ -5703,6 +6421,6 @@ var ALT_SHIFT_D = isMac ? "\u2325\u21E7D" : "Alt+Shift+D";
5703
6421
  var ALT_SHIFT_E = isMac ? "\u2325\u21E7E" : "Alt+Shift+E";
5704
6422
  var ALT_SHIFT_N = isMac ? "\u2325\u21E7N" : "Alt+Shift+N";
5705
6423
 
5706
- 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, 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, 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, Switch_default, Tabs, Text, Textarea_default, TileButton_default, Title, Tooltip, TopNav, appendKey, applyThemePrefs, backspace, firstEnabledIndex, formatDate, inputClasses, isMac, isSeverityTone, lastEnabledIndex, matchTypeahead, mediaFileName, nextEnabledIndex, resolveTheme, severityOf, tabButtonId, tabPanelId, toISODate, useClickOutside, useTheme };
5707
- //# sourceMappingURL=chunk-CTTHBTOX.js.map
5708
- //# sourceMappingURL=chunk-CTTHBTOX.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