gridsmith-ui 0.9.2 → 0.11.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.
package/dist/index.js CHANGED
@@ -1,18 +1,19 @@
1
- import { useAppShell, ControlSizeContext, Button, Spinner, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, useDirtyGuard, DiscardConfirm, defaultMaxWidth } from './chunk-Z37HO5NU.js';
2
- export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell } from './chunk-Z37HO5NU.js';
1
+ import { useAppShell, ControlSizeContext, Button, Spinner, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, useDirtyGuard, DiscardConfirm, defaultMaxWidth } from './chunk-YMUCTZXS.js';
2
+ export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell } from './chunk-YMUCTZXS.js';
3
3
  import { Skeleton } from './chunk-M3AARGLI.js';
4
4
  export { Skeleton } from './chunk-M3AARGLI.js';
5
- import { useOverlayLayer, useCloseOnFocusOut, focusInitial, rovingTabStop, OVERLAY_CLOSE_ATTR, useNonModalDialog, useFocusTrap } from './chunk-RLUK575N.js';
6
- import { Link } from './chunk-ENBESZS2.js';
7
- export { CodeBlock, Divider, Link } from './chunk-ENBESZS2.js';
8
- import { Badge } from './chunk-OVVRMMCO.js';
9
- export { Badge } from './chunk-OVVRMMCO.js';
10
- import { inferColumnKinds, CardValue } from './chunk-FDIFQ5UF.js';
11
- import { SplitPaneCtx, TableCardCtx } from './chunk-RG4KUCRS.js';
5
+ import { useOverlayLayer, useCloseOnFocusOut, focusInitial, rovingTabStop, OVERLAY_CLOSE_ATTR, useNonModalDialog, useFocusTrap } from './chunk-NHQCMOAN.js';
6
+ import { Link } from './chunk-QJBCZ7BR.js';
7
+ export { CodeBlock, Divider, Link } from './chunk-QJBCZ7BR.js';
8
+ import { Badge } from './chunk-U2JT37GJ.js';
9
+ export { Badge } from './chunk-U2JT37GJ.js';
10
+ import { inferColumnKinds, CardValue } from './chunk-CR2KF73R.js';
11
+ import { SplitPaneCtx, usePreferences, TableCardCtx, preferenceActionKey } from './chunk-QNVSMY4R.js';
12
+ export { PreferencesContext, preferenceActionKey, usePreferences } from './chunk-QNVSMY4R.js';
12
13
  import { useBreakpoint } from './chunk-CRQ6KCRD.js';
13
14
  export { BreakpointWidthProvider, useBreakpoint } from './chunk-CRQ6KCRD.js';
14
- import { useTextOverflow, Tooltip, usePosition } from './chunk-CQADS7JZ.js';
15
- export { Tooltip, useTextOverflow } from './chunk-CQADS7JZ.js';
15
+ import { useTextOverflow, Tooltip, usePosition } from './chunk-IYD6ZDM2.js';
16
+ export { Tooltip, useTextOverflow } from './chunk-IYD6ZDM2.js';
16
17
  import { mergeRefs, Slot } from './chunk-PKLVP5ZB.js';
17
18
  import { useLatest } from './chunk-WH2FUFAC.js';
18
19
  import { Icon } from './chunk-X4CNZFWC.js';
@@ -1123,6 +1124,36 @@ function RadioGroup({ value, onChange, options, orientation = "vertical", classN
1123
1124
  )
1124
1125
  ] });
1125
1126
  }
1127
+
1128
+ // ../../showcase/src/lib/segments.ts
1129
+ function stepValue(spec, current, delta) {
1130
+ if (current === null) return delta > 0 ? spec.min : spec.max;
1131
+ const span = spec.max - spec.min + 1;
1132
+ return spec.min + ((current - spec.min + delta) % span + span) % span;
1133
+ }
1134
+ function typeDigit(spec, buffer, digit) {
1135
+ if (!/^\d$/.test(digit)) return { buffer, value: buffer ? Number(buffer) : null, advance: false };
1136
+ let next = buffer + digit;
1137
+ if (next.length > spec.maxDigits || Number(next) > spec.max) next = digit;
1138
+ const n = Number(next);
1139
+ const value = n >= spec.min && n <= spec.max ? n : null;
1140
+ const full = next.length >= spec.maxDigits;
1141
+ const noRoom = n > 0 && n * 10 > spec.max;
1142
+ return { buffer: next, value, advance: full || noRoom };
1143
+ }
1144
+ var pad2 = (n) => String(n).padStart(2, "0");
1145
+ function timeSegments(use24Hour) {
1146
+ return [
1147
+ { key: "hour", label: "Hours", min: use24Hour ? 0 : 1, max: use24Hour ? 23 : 12, maxDigits: 2, placeholder: "--" },
1148
+ { key: "minute", label: "Minutes", min: 0, max: 59, maxDigits: 2, placeholder: "--" }
1149
+ ];
1150
+ }
1151
+ function dateSegments(dayFirst, daysInMonth = 31) {
1152
+ const day = { key: "day", label: "Day", min: 1, max: daysInMonth, maxDigits: 2, placeholder: "dd" };
1153
+ const month = { key: "month", label: "Month", min: 1, max: 12, maxDigits: 2, placeholder: "mm" };
1154
+ const year = { key: "year", label: "Year", min: 1, max: 9999, maxDigits: 4, placeholder: "yyyy" };
1155
+ return dayFirst ? [day, month, year] : [month, day, year];
1156
+ }
1126
1157
  var CONTROL_HEIGHT2 = {
1127
1158
  sm: "h-[var(--ds-control-height-sm)]",
1128
1159
  md: "h-[var(--ds-control-height-md)]",
@@ -1160,6 +1191,44 @@ function isSameDay(a, b) {
1160
1191
  function formatDate(d) {
1161
1192
  return `${MONTHS[d.getMonth()].slice(0, 3)} ${d.getDate()}, ${d.getFullYear()}`;
1162
1193
  }
1194
+ function localeDayFirst() {
1195
+ try {
1196
+ const parts = new Intl.DateTimeFormat(void 0).formatToParts(new Date(2e3, 11, 25));
1197
+ return parts.findIndex((p) => p.type === "day") < parts.findIndex((p) => p.type === "month");
1198
+ } catch {
1199
+ return true;
1200
+ }
1201
+ }
1202
+ function validDate(year, monthIndex, day) {
1203
+ if (monthIndex < 0 || monthIndex > 11 || day < 1) return null;
1204
+ const d = new Date(year, monthIndex, day);
1205
+ return d.getFullYear() === year && d.getMonth() === monthIndex && d.getDate() === day ? d : null;
1206
+ }
1207
+ function parseTypedDate(text, dayFirst = localeDayFirst()) {
1208
+ const t = text.trim().replace(/\s+/g, " ");
1209
+ if (!t) return null;
1210
+ let m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(t);
1211
+ if (m) return validDate(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
1212
+ const monthIndex = (name) => MONTHS.findIndex((mm) => mm.toLowerCase().startsWith(name.toLowerCase().replace(/\.$/, "")));
1213
+ m = /^([A-Za-z]{3,})\.?\s+(\d{1,2}),?\s+(\d{4})$/.exec(t);
1214
+ if (m) {
1215
+ const mi = monthIndex(m[1]);
1216
+ return mi < 0 ? null : validDate(Number(m[3]), mi, Number(m[2]));
1217
+ }
1218
+ m = /^(\d{1,2})\s+([A-Za-z]{3,})\.?,?\s+(\d{4})$/.exec(t);
1219
+ if (m) {
1220
+ const mi = monthIndex(m[2]);
1221
+ return mi < 0 ? null : validDate(Number(m[3]), mi, Number(m[1]));
1222
+ }
1223
+ m = /^(\d{1,2})[/.-](\d{1,2})[/.-](\d{2,4})$/.exec(t);
1224
+ if (m) {
1225
+ const a = Number(m[1]), b = Number(m[2]);
1226
+ const year = m[3].length === 2 ? 2e3 + Number(m[3]) : Number(m[3]);
1227
+ const [day, month] = a > 12 ? [a, b] : b > 12 ? [b, a] : dayFirst ? [a, b] : [b, a];
1228
+ return validDate(year, month - 1, day);
1229
+ }
1230
+ return null;
1231
+ }
1163
1232
  function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, className, ref, container, error = false, label, id, size: sizeProp, min, max, range }) {
1164
1233
  const contextSize = useContext(ControlSizeContext);
1165
1234
  const size = sizeProp ?? contextSize ?? "md";
@@ -1177,6 +1246,33 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1177
1246
  const firstDay = getFirstDayOfWeek(year, month);
1178
1247
  const today = useMemo(() => /* @__PURE__ */ new Date(), []);
1179
1248
  const isTopLayer = useOverlayLayer(open);
1249
+ const prevMonth = () => setViewDate(new Date(year, month - 1, 1));
1250
+ const nextMonth = () => setViewDate(new Date(year, month + 1, 1));
1251
+ const startOf = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
1252
+ const isDisabledDay = (d) => !!min && d < startOf(min) || !!max && d > startOf(max);
1253
+ useCloseOnFocusOut(open, triggerRef, panelRef, () => setOpen(false), isTopLayer);
1254
+ useEffect(() => {
1255
+ if (open) requestAnimationFrame(() => focusInitial(panelRef.current));
1256
+ }, [open]);
1257
+ const closeAndRefocus = () => {
1258
+ setOpen(false);
1259
+ focusSegment(0);
1260
+ };
1261
+ const dayFirst = useMemo(() => localeDayFirst(), []);
1262
+ const [segs, setSegs] = useState(() => value ? { day: value.getDate(), month: value.getMonth() + 1, year: value.getFullYear() } : { day: null, month: null, year: null });
1263
+ const [buffers, setBuffers] = useState({});
1264
+ const lastCommitted = useRef(null);
1265
+ useEffect(() => {
1266
+ if (value && value.getTime() === lastCommitted.current) return;
1267
+ setSegs(value ? { day: value.getDate(), month: value.getMonth() + 1, year: value.getFullYear() } : { day: null, month: null, year: null });
1268
+ setBuffers({});
1269
+ }, [value]);
1270
+ const specs = dateSegments(dayFirst, segs.month && segs.year ? getDaysInMonth(segs.year, segs.month - 1) : 31);
1271
+ const segmentRefs = useRef([]);
1272
+ const focusSegment = (i) => {
1273
+ segmentRefs.current[Math.max(0, Math.min(specs.length - 1, i))]?.focus();
1274
+ };
1275
+ const focusSegmentRef = useLatest(focusSegment);
1180
1276
  useEffect(() => {
1181
1277
  if (!open) return;
1182
1278
  const handler = (e) => {
@@ -1187,7 +1283,7 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1187
1283
  const keyHandler = (e) => {
1188
1284
  if (e.key === "Escape" && isTopLayer()) {
1189
1285
  setOpen(false);
1190
- triggerRef.current?.focus();
1286
+ focusSegmentRef.current(0);
1191
1287
  }
1192
1288
  };
1193
1289
  document.addEventListener("mousedown", handler);
@@ -1196,18 +1292,88 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1196
1292
  document.removeEventListener("mousedown", handler);
1197
1293
  document.removeEventListener("keydown", keyHandler);
1198
1294
  };
1199
- }, [open, isTopLayer]);
1200
- const prevMonth = () => setViewDate(new Date(year, month - 1, 1));
1201
- const nextMonth = () => setViewDate(new Date(year, month + 1, 1));
1202
- const startOf = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
1203
- const isDisabledDay = (d) => !!min && d < startOf(min) || !!max && d > startOf(max);
1204
- useCloseOnFocusOut(open, triggerRef, panelRef, () => setOpen(false), isTopLayer);
1205
- useEffect(() => {
1206
- if (open) requestAnimationFrame(() => focusInitial(panelRef.current));
1207
- }, [open]);
1208
- const closeAndRefocus = () => {
1209
- setOpen(false);
1210
- triggerRef.current?.focus();
1295
+ }, [open, isTopLayer, focusSegmentRef]);
1296
+ const [typedInvalid, setTypedInvalid] = useState(false);
1297
+ const commitIfComplete = (next) => {
1298
+ if (next.day === null || next.month === null || next.year === null) {
1299
+ setTypedInvalid(false);
1300
+ return;
1301
+ }
1302
+ const d = validDate(next.year, next.month - 1, next.day);
1303
+ const ok = !!d && (!min || d >= min) && (!max || d <= max);
1304
+ setTypedInvalid(!ok);
1305
+ if (d && ok && !(value && isSameDay(value, d))) {
1306
+ lastCommitted.current = d.getTime();
1307
+ onChange(d);
1308
+ setViewDate(d);
1309
+ }
1310
+ };
1311
+ const setSegment = (key, v, commit = true) => {
1312
+ setSegs((cur) => {
1313
+ const next = { ...cur, [key]: v };
1314
+ if (commit) commitIfComplete(next);
1315
+ return next;
1316
+ });
1317
+ };
1318
+ const applyTyped = (text) => {
1319
+ const d = parseTypedDate(text, dayFirst);
1320
+ if (!d) return;
1321
+ const next = { day: d.getDate(), month: d.getMonth() + 1, year: d.getFullYear() };
1322
+ setSegs(next);
1323
+ setBuffers({});
1324
+ commitIfComplete(next);
1325
+ };
1326
+ const onSegmentKeyDown = (e, index, spec) => {
1327
+ const key = e.key;
1328
+ if (key === "Tab" || key === "Shift" || key === "Escape" || key.startsWith("F")) return;
1329
+ e.preventDefault();
1330
+ const k = spec.key;
1331
+ const clearBuffer = () => setBuffers((b) => ({ ...b, [k]: "" }));
1332
+ if (key === "ArrowLeft") {
1333
+ focusSegment(index - 1);
1334
+ return;
1335
+ }
1336
+ if (key === "ArrowRight") {
1337
+ focusSegment(index + 1);
1338
+ return;
1339
+ }
1340
+ if (key === "ArrowUp" || key === "ArrowDown") {
1341
+ clearBuffer();
1342
+ setSegment(k, stepValue(spec, segs[k], key === "ArrowUp" ? 1 : -1));
1343
+ return;
1344
+ }
1345
+ if (key === "Home") {
1346
+ clearBuffer();
1347
+ setSegment(k, spec.min);
1348
+ return;
1349
+ }
1350
+ if (key === "End") {
1351
+ clearBuffer();
1352
+ setSegment(k, spec.max);
1353
+ return;
1354
+ }
1355
+ if (key === "Backspace" || key === "Delete") {
1356
+ if (segs[k] === null && !buffers[k]) {
1357
+ focusSegment(index - 1);
1358
+ return;
1359
+ }
1360
+ clearBuffer();
1361
+ setSegment(k, null, false);
1362
+ return;
1363
+ }
1364
+ if (/^\d$/.test(key)) {
1365
+ const r = typeDigit(spec, buffers[k] ?? "", key);
1366
+ setBuffers((b) => ({ ...b, [k]: r.advance ? "" : r.buffer }));
1367
+ setSegment(k, r.value);
1368
+ if (r.advance) focusSegment(index + 1);
1369
+ }
1370
+ };
1371
+ const shown = (spec) => {
1372
+ const k = spec.key;
1373
+ const b = buffers[k];
1374
+ if (b && segs[k] === null) return b;
1375
+ if (segs[k] === null) return spec.placeholder;
1376
+ return k === "year" ? String(segs[k]) : pad2(segs[k]);
1211
1377
  };
1212
1378
  const selectDay = (day) => {
1213
1379
  const d = new Date(year, month, day);
@@ -1227,34 +1393,78 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1227
1393
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
1228
1394
  label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "block text-sm font-medium text-[var(--ds-text-primary)] mb-ds-1", children: label }),
1229
1395
  /* @__PURE__ */ jsxs(
1230
- "button",
1396
+ "div",
1231
1397
  {
1232
- id: inputId,
1233
1398
  ref: triggerRef,
1234
- type: "button",
1235
- onClick: () => !disabled && setOpen(!open),
1236
- disabled,
1237
- "aria-invalid": error || void 0,
1238
- "aria-haspopup": "dialog",
1239
- "aria-expanded": open,
1399
+ "data-date-field": "",
1400
+ role: "group",
1401
+ id: inputId,
1402
+ "aria-label": label ?? "Date",
1403
+ "aria-description": !value ? placeholder : void 0,
1404
+ "aria-invalid": error || typedInvalid || void 0,
1405
+ onPaste: (e) => {
1406
+ const text = e.clipboardData.getData("text");
1407
+ if (text) {
1408
+ e.preventDefault();
1409
+ applyTyped(text);
1410
+ }
1411
+ },
1240
1412
  className: cn(
1241
1413
  "flex items-center gap-ds-2 w-full",
1242
1414
  CONTROL_HEIGHT2[size],
1243
1415
  TEXT_SIZE2[size],
1244
1416
  "border-[length:var(--ds-border-width)]",
1245
- error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1417
+ error || typedInvalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1246
1418
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
1247
1419
  "shadow-[var(--ds-input-shadow)]",
1248
- "transition-colors cursor-pointer",
1420
+ "transition-colors",
1249
1421
  "px-ds-3 py-ds-2",
1250
- !error && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
1422
+ !error && !disabled && !open && "hover:border-[var(--ds-input-border-hover)] focus-within:border-[var(--ds-input-border-hover)]",
1251
1423
  open && ACTIVE_RING,
1252
1424
  disabled && "opacity-disabled cursor-not-allowed",
1253
- value ? "text-[var(--ds-text-primary)]" : "text-[var(--ds-text-muted)]"
1425
+ "text-[var(--ds-text-primary)]"
1254
1426
  ),
1255
1427
  children: [
1256
- /* @__PURE__ */ jsx(Icon, { name: "calendar", size: 14, className: "text-[var(--ds-text-muted)]" }),
1257
- value ? formatDate(value) : placeholder
1428
+ /* @__PURE__ */ jsx(Icon, { name: "calendar", size: 14, className: "text-[var(--ds-text-muted)] shrink-0" }),
1429
+ /* @__PURE__ */ jsx("div", { className: "flex items-center min-w-0 flex-1 tabular-nums", onClick: () => focusSegment(0), children: specs.map((spec, i) => {
1430
+ const k = spec.key;
1431
+ return /* @__PURE__ */ jsxs("span", { className: "contents", children: [
1432
+ i > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "px-[1px] text-[var(--ds-text-muted)]", children: "/" }),
1433
+ /* @__PURE__ */ jsx(
1434
+ "span",
1435
+ {
1436
+ ref: (el) => {
1437
+ segmentRefs.current[i] = el;
1438
+ },
1439
+ role: "spinbutton",
1440
+ tabIndex: disabled ? -1 : 0,
1441
+ "data-segment": spec.key,
1442
+ "aria-label": spec.label,
1443
+ "aria-valuemin": spec.min,
1444
+ "aria-valuemax": spec.max,
1445
+ "aria-valuenow": segs[k] ?? void 0,
1446
+ "aria-valuetext": segs[k] === null ? "empty" : String(segs[k]),
1447
+ onKeyDown: (e) => onSegmentKeyDown(e, i, spec),
1448
+ onClick: (e) => e.stopPropagation(),
1449
+ className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", segs[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
1450
+ children: shown(spec)
1451
+ }
1452
+ )
1453
+ ] }, spec.key);
1454
+ }) }),
1455
+ /* @__PURE__ */ jsx(
1456
+ "button",
1457
+ {
1458
+ type: "button",
1459
+ "aria-label": "Choose a date",
1460
+ "aria-haspopup": "dialog",
1461
+ "aria-expanded": open,
1462
+ onClick: () => !disabled && setOpen(!open),
1463
+ disabled,
1464
+ className: "shrink-0 -mr-ds-1 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] focus-visible:outline-2 focus-visible:outline-[var(--ds-accent-primary)] disabled:cursor-not-allowed",
1465
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: 14 })
1466
+ }
1467
+ )
1258
1468
  ]
1259
1469
  }
1260
1470
  ),
@@ -1292,8 +1502,8 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1292
1502
  }
1293
1503
  ),
1294
1504
  /* @__PURE__ */ jsxs("div", { style: { padding: "var(--ds-space-2) var(--ds-space-3)" }, children: [
1295
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-ds-0.5 mb-ds-1", children: DAYS.map((d) => /* @__PURE__ */ jsx("div", { className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, d)) }),
1296
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-ds-0.5", onMouseLeave: () => setHover(null), children: cells.map((day, i) => {
1505
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-ds-0.5 mb-ds-1 tabular-nums", children: DAYS.map((d) => /* @__PURE__ */ jsx("div", { className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, d)) }),
1506
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-ds-0.5 tabular-nums", onMouseLeave: () => setHover(null), children: cells.map((day, i) => {
1297
1507
  if (day === null) return /* @__PURE__ */ jsx("div", {}, `empty-${i}`);
1298
1508
  const dateObj = new Date(year, month, day);
1299
1509
  const isSelected = !!value && isSameDay(dateObj, value);
@@ -2080,6 +2290,12 @@ function SliderRange({
2080
2290
  )
2081
2291
  ] });
2082
2292
  }
2293
+ function fromValue(v, use24Hour) {
2294
+ const m = /^(\d{1,2}):(\d{2})$/.exec(v);
2295
+ if (!m) return { hour: null, minute: null, period: null };
2296
+ const h24 = Number(m[1]);
2297
+ return use24Hour ? { hour: h24, minute: Number(m[2]), period: null } : { hour: h24 % 12 === 0 ? 12 : h24 % 12, minute: Number(m[2]), period: h24 >= 12 ? "PM" : "AM" };
2298
+ }
2083
2299
  var CONTROL_HEIGHT4 = {
2084
2300
  sm: "h-[var(--ds-control-height-sm)]",
2085
2301
  md: "h-[var(--ds-control-height-md)]",
@@ -2105,6 +2321,23 @@ function generateMinutes(step) {
2105
2321
  for (let m = 0; m < 60; m += step) mins.push(m);
2106
2322
  return mins;
2107
2323
  }
2324
+ function parseTypedTime(text, use24Hour) {
2325
+ const t = text.trim().toLowerCase().replace(/\s+/g, " ");
2326
+ if (!t) return null;
2327
+ const m = /^(\d{1,2})(?::?(\d{2}))?\s*(a\.?m\.?|p\.?m\.?|a|p)?$/.exec(t);
2328
+ if (!m) return null;
2329
+ let hour = Number(m[1]);
2330
+ const minute = m[2] === void 0 ? 0 : Number(m[2]);
2331
+ const suffix = m[3]?.replace(/\./g, "");
2332
+ if (Number.isNaN(hour) || minute < 0 || minute > 59) return null;
2333
+ if (suffix) {
2334
+ if (hour < 1 || hour > 12) return null;
2335
+ const pm = suffix.startsWith("p");
2336
+ hour = pm ? hour === 12 ? 12 : hour + 12 : hour === 12 ? 0 : hour;
2337
+ } else if (hour > 23) return null;
2338
+ else if (!use24Hour && hour >= 1 && hour <= 12 && m[2] === void 0) ;
2339
+ return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
2340
+ }
2108
2341
  function parseTime(value) {
2109
2342
  const [h, m] = value.split(":").map(Number);
2110
2343
  const hour = isNaN(h) ? 12 : h;
@@ -2140,25 +2373,6 @@ function TimePicker({
2140
2373
  const minutes = generateMinutes(minuteStep);
2141
2374
  const isTopLayer = useOverlayLayer(open);
2142
2375
  useCloseOnFocusOut(open, triggerRef, panelRef, () => setOpen(false), isTopLayer);
2143
- useEffect(() => {
2144
- if (!open) return;
2145
- const handler = (e) => {
2146
- if (triggerRef.current?.contains(e.target) || panelRef.current?.contains(e.target)) return;
2147
- setOpen(false);
2148
- };
2149
- const keyHandler = (e) => {
2150
- if (e.key === "Escape" && isTopLayer()) {
2151
- setOpen(false);
2152
- triggerRef.current?.focus();
2153
- }
2154
- };
2155
- document.addEventListener("mousedown", handler);
2156
- document.addEventListener("keydown", keyHandler);
2157
- return () => {
2158
- document.removeEventListener("mousedown", handler);
2159
- document.removeEventListener("keydown", keyHandler);
2160
- };
2161
- }, [open, isTopLayer]);
2162
2376
  useEffect(() => {
2163
2377
  if (!open) return;
2164
2378
  const id2 = requestAnimationFrame(() => {
@@ -2195,20 +2409,148 @@ function TimePicker({
2195
2409
  }
2196
2410
  onChange(`${pad(h24)}:${pad(minute)}`);
2197
2411
  }
2198
- const display = use24Hour ? value || "00:00" : `${pad(parsed.hour)}:${pad(parsed.minute)} ${parsed.period}`;
2412
+ const specs = timeSegments(use24Hour);
2413
+ const [segs, setSegs] = useState(() => fromValue(value, use24Hour));
2414
+ const [buffers, setBuffers] = useState({});
2415
+ const lastCommitted = useRef(null);
2416
+ useEffect(() => {
2417
+ if (value === lastCommitted.current) return;
2418
+ setSegs(fromValue(value, use24Hour));
2419
+ setBuffers({});
2420
+ }, [value, use24Hour]);
2421
+ const segmentRefs = useRef([]);
2422
+ const segmentCount = specs.length + (use24Hour ? 0 : 1);
2423
+ const focusSegment = (i) => {
2424
+ const el = segmentRefs.current[Math.max(0, Math.min(segmentCount - 1, i))];
2425
+ el?.focus();
2426
+ };
2427
+ const focusSegmentRef = useLatest(focusSegment);
2428
+ useEffect(() => {
2429
+ if (!open) return;
2430
+ const handler = (e) => {
2431
+ if (triggerRef.current?.contains(e.target) || panelRef.current?.contains(e.target)) return;
2432
+ setOpen(false);
2433
+ };
2434
+ const keyHandler = (e) => {
2435
+ if (e.key === "Escape" && isTopLayer()) {
2436
+ setOpen(false);
2437
+ focusSegmentRef.current(0);
2438
+ }
2439
+ };
2440
+ document.addEventListener("mousedown", handler);
2441
+ document.addEventListener("keydown", keyHandler);
2442
+ return () => {
2443
+ document.removeEventListener("mousedown", handler);
2444
+ document.removeEventListener("keydown", keyHandler);
2445
+ };
2446
+ }, [open, isTopLayer, focusSegmentRef]);
2447
+ const commitIfComplete = (next) => {
2448
+ if (next.hour === null || next.minute === null) return;
2449
+ let h24 = next.hour;
2450
+ if (!use24Hour) {
2451
+ const period = next.period ?? "AM";
2452
+ h24 = period === "AM" ? next.hour === 12 ? 0 : next.hour : next.hour === 12 ? 12 : next.hour + 12;
2453
+ }
2454
+ const out = `${pad2(h24)}:${pad2(next.minute)}`;
2455
+ lastCommitted.current = out;
2456
+ if (out !== value) onChange(out);
2457
+ };
2458
+ const setSegment = (key, v, commit = true) => {
2459
+ setSegs((s) => {
2460
+ const next = { ...s, [key]: v };
2461
+ if (commit) commitIfComplete(next);
2462
+ return next;
2463
+ });
2464
+ };
2465
+ const applyTyped = (text) => {
2466
+ const parsed2 = parseTypedTime(text, use24Hour);
2467
+ if (!parsed2) return;
2468
+ const next = fromValue(parsed2, use24Hour);
2469
+ setSegs(next);
2470
+ setBuffers({});
2471
+ commitIfComplete(next);
2472
+ };
2473
+ const onSegmentKeyDown = (e, index, spec) => {
2474
+ const key = e.key;
2475
+ if (key === "Tab" || key === "Shift" || key === "Escape" || key.startsWith("F")) return;
2476
+ e.preventDefault();
2477
+ if (key === "ArrowLeft") {
2478
+ focusSegment(index - 1);
2479
+ return;
2480
+ }
2481
+ if (key === "ArrowRight") {
2482
+ focusSegment(index + 1);
2483
+ return;
2484
+ }
2485
+ if (!spec) {
2486
+ if (key === "ArrowUp" || key === "ArrowDown" || key === " ") setSegment("period", segs.period === "PM" ? "AM" : "PM");
2487
+ else if (/^[aA]$/.test(key)) setSegment("period", "AM");
2488
+ else if (/^[pP]$/.test(key)) setSegment("period", "PM");
2489
+ else if (key === "Backspace" || key === "Delete") {
2490
+ setSegment("period", null, false);
2491
+ }
2492
+ return;
2493
+ }
2494
+ const k = spec.key;
2495
+ const clearBuffer = () => setBuffers((b) => ({ ...b, [k]: "" }));
2496
+ if (key === "ArrowUp" || key === "ArrowDown") {
2497
+ clearBuffer();
2498
+ setSegment(k, stepValue(spec, segs[k], key === "ArrowUp" ? 1 : -1));
2499
+ return;
2500
+ }
2501
+ if (key === "Home") {
2502
+ clearBuffer();
2503
+ setSegment(k, spec.min);
2504
+ return;
2505
+ }
2506
+ if (key === "End") {
2507
+ clearBuffer();
2508
+ setSegment(k, spec.max);
2509
+ return;
2510
+ }
2511
+ if (key === "Backspace" || key === "Delete") {
2512
+ if (segs[k] === null && !buffers[k]) {
2513
+ focusSegment(index - 1);
2514
+ return;
2515
+ }
2516
+ clearBuffer();
2517
+ setSegment(k, null, false);
2518
+ return;
2519
+ }
2520
+ if (/^\d$/.test(key)) {
2521
+ const r = typeDigit(spec, buffers[k] ?? "", key);
2522
+ setBuffers((b) => ({ ...b, [k]: r.advance ? "" : r.buffer }));
2523
+ setSegment(k, r.value);
2524
+ if (r.advance) focusSegment(index + 1);
2525
+ return;
2526
+ }
2527
+ if (!use24Hour && /^[aApP]$/.test(key)) {
2528
+ setSegment("period", /^[aA]$/.test(key) ? "AM" : "PM");
2529
+ return;
2530
+ }
2531
+ };
2532
+ const shown = (k, spec) => {
2533
+ const b = buffers[k];
2534
+ if (b && segs[k] === null) return b;
2535
+ return segs[k] === null ? spec.placeholder : pad2(segs[k]);
2536
+ };
2199
2537
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
2200
2538
  /* @__PURE__ */ jsxs(
2201
- "button",
2539
+ "div",
2202
2540
  {
2203
- id,
2204
2541
  ref: triggerRef,
2205
- type: "button",
2206
- onClick: () => !disabled && setOpen(!open),
2207
- disabled,
2542
+ "data-time-field": "",
2543
+ role: "group",
2544
+ id,
2545
+ "aria-label": "Time",
2208
2546
  "aria-invalid": error || void 0,
2209
- "aria-haspopup": "dialog",
2210
- "aria-expanded": open,
2211
- "aria-controls": open ? listboxId : void 0,
2547
+ onPaste: (e) => {
2548
+ const text = e.clipboardData.getData("text");
2549
+ if (text) {
2550
+ e.preventDefault();
2551
+ applyTyped(text);
2552
+ }
2553
+ },
2212
2554
  className: cn(
2213
2555
  "flex w-full items-center gap-ds-2",
2214
2556
  CONTROL_HEIGHT4[size],
@@ -2217,16 +2559,77 @@ function TimePicker({
2217
2559
  error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
2218
2560
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
2219
2561
  "shadow-[var(--ds-input-shadow)]",
2220
- "transition-colors cursor-pointer",
2562
+ "transition-colors",
2221
2563
  "px-ds-3 py-ds-2",
2222
- !error && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
2564
+ !error && !disabled && !open && "hover:border-[var(--ds-input-border-hover)] focus-within:border-[var(--ds-input-border-hover)]",
2223
2565
  open && ACTIVE_RING,
2224
2566
  disabled && "opacity-disabled cursor-not-allowed",
2225
- value ? "text-[var(--ds-text-primary)]" : "text-[var(--ds-text-muted)]"
2567
+ "text-[var(--ds-text-primary)]"
2226
2568
  ),
2227
2569
  children: [
2228
- /* @__PURE__ */ jsx(Icon, { name: "clock", size: 14, className: "text-[var(--ds-text-muted)]" }),
2229
- display
2570
+ /* @__PURE__ */ jsx(Icon, { name: "clock", size: 14, className: "text-[var(--ds-text-muted)] shrink-0" }),
2571
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center min-w-0 flex-1 tabular-nums", onClick: () => focusSegment(0), children: [
2572
+ specs.map((spec, i) => {
2573
+ const k = spec.key;
2574
+ return /* @__PURE__ */ jsxs("span", { className: "contents", children: [
2575
+ i > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "px-[1px]", children: ":" }),
2576
+ /* @__PURE__ */ jsx(
2577
+ "span",
2578
+ {
2579
+ ref: (el) => {
2580
+ segmentRefs.current[i] = el;
2581
+ },
2582
+ role: "spinbutton",
2583
+ tabIndex: disabled ? -1 : 0,
2584
+ "data-segment": spec.key,
2585
+ "aria-label": spec.label,
2586
+ "aria-valuemin": spec.min,
2587
+ "aria-valuemax": spec.max,
2588
+ "aria-valuenow": segs[k] ?? void 0,
2589
+ "aria-valuetext": segs[k] === null ? "empty" : pad2(segs[k]),
2590
+ onKeyDown: (e) => onSegmentKeyDown(e, i, spec),
2591
+ onClick: (e) => e.stopPropagation(),
2592
+ className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", segs[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
2593
+ children: shown(k, spec)
2594
+ }
2595
+ )
2596
+ ] }, spec.key);
2597
+ }),
2598
+ !use24Hour && /* @__PURE__ */ jsx(
2599
+ "span",
2600
+ {
2601
+ ref: (el) => {
2602
+ segmentRefs.current[specs.length] = el;
2603
+ },
2604
+ role: "spinbutton",
2605
+ tabIndex: disabled ? -1 : 0,
2606
+ "data-segment": "period",
2607
+ "aria-label": "AM or PM",
2608
+ "aria-valuemin": 0,
2609
+ "aria-valuemax": 1,
2610
+ "aria-valuenow": segs.period === null ? void 0 : segs.period === "AM" ? 0 : 1,
2611
+ "aria-valuetext": segs.period ?? "empty",
2612
+ onKeyDown: (e) => onSegmentKeyDown(e, specs.length, null),
2613
+ onClick: (e) => e.stopPropagation(),
2614
+ className: cn("ml-ds-1 rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", segs.period === null && "text-[var(--ds-text-muted)]"),
2615
+ children: segs.period ?? "--"
2616
+ }
2617
+ )
2618
+ ] }),
2619
+ /* @__PURE__ */ jsx(
2620
+ "button",
2621
+ {
2622
+ type: "button",
2623
+ "aria-label": "Choose a time",
2624
+ "aria-haspopup": "dialog",
2625
+ "aria-expanded": open,
2626
+ "aria-controls": open ? listboxId : void 0,
2627
+ onClick: () => !disabled && setOpen(!open),
2628
+ disabled,
2629
+ className: "shrink-0 -mr-ds-1 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] focus-visible:outline-2 focus-visible:outline-[var(--ds-accent-primary)] disabled:cursor-not-allowed",
2630
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: 14 })
2631
+ }
2632
+ )
2230
2633
  ]
2231
2634
  }
2232
2635
  ),
@@ -2247,7 +2650,7 @@ function TimePicker({
2247
2650
  "rounded-[var(--ds-radius-md)]"
2248
2651
  ),
2249
2652
  style: { top: pos.top, left: pos.left },
2250
- children: /* @__PURE__ */ jsxs("div", { className: "inline-flex p-ds-1 gap-ds-1", children: [
2653
+ children: /* @__PURE__ */ jsxs("div", { className: "inline-flex p-ds-1 gap-ds-1 tabular-nums", children: [
2251
2654
  /* @__PURE__ */ jsx("div", { "data-column": true, className: "max-h-48 overflow-y-auto w-ds-10", children: hours.map((h) => /* @__PURE__ */ jsx(
2252
2655
  "button",
2253
2656
  {
@@ -2575,7 +2978,7 @@ var Tag = forwardRef(function Tag2({
2575
2978
  className,
2576
2979
  children
2577
2980
  }, ref) {
2578
- const { ref: truncRef, isTruncated } = useTextOverflow({ lines: 1, disabled: !maxWidth });
2981
+ const { ref: truncRef, isTruncated } = useTextOverflow({ lines: 1 });
2579
2982
  const handleRemove = (e) => {
2580
2983
  e.stopPropagation();
2581
2984
  onRemove?.();
@@ -2596,7 +2999,7 @@ var Tag = forwardRef(function Tag2({
2596
2999
  }
2597
3000
  } : void 0,
2598
3001
  className: cn(
2599
- "inline-flex items-center font-medium max-w-full whitespace-nowrap",
3002
+ "inline-flex items-center font-medium max-w-full min-w-0 whitespace-nowrap",
2600
3003
  "rounded-[var(--ds-radius-sm)]",
2601
3004
  "transition-colors duration-[var(--ds-transition-speed)]",
2602
3005
  sizeStyles[size],
@@ -2607,7 +3010,7 @@ var Tag = forwardRef(function Tag2({
2607
3010
  ),
2608
3011
  style: maxWidth ? { maxWidth } : void 0,
2609
3012
  children: [
2610
- maxWidth ? /* @__PURE__ */ jsx("span", { ref: truncRef, className: "truncate", children }) : children,
3013
+ /* @__PURE__ */ jsx("span", { ref: truncRef, className: "truncate min-w-0", children }),
2611
3014
  onRemove && !disabled && /* @__PURE__ */ jsx(
2612
3015
  "button",
2613
3016
  {
@@ -3456,7 +3859,10 @@ function collectInvalidFields(form) {
3456
3859
  }
3457
3860
  return out;
3458
3861
  }
3459
- function FormWrapper({ onSubmit, validationMode = "onSubmit", children, actions, actionsAlign = "end", focusFirstError = true, errorSummary = true, className, ref }) {
3862
+ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, actions, actionsAlign: actionsAlignProp, focusFirstError = true, errorSummary = true, className, ref }) {
3863
+ const preferences = usePreferences();
3864
+ const validationMode = validationModeProp ?? preferences.validationTiming ?? "onSubmit";
3865
+ const actionsAlign = actionsAlignProp ?? preferences.formActions?.align ?? "end";
3460
3866
  const [errors, setErrors] = useState({});
3461
3867
  const [submitted, setSubmitted] = useState(false);
3462
3868
  const [submitCount, setSubmitCount] = useState(0);
@@ -4318,11 +4724,15 @@ var Card = forwardRef(function Card2({
4318
4724
  children: /* @__PURE__ */ jsx(CheckboxIndicator, { checked: !!selected })
4319
4725
  }
4320
4726
  ),
4321
- /* @__PURE__ */ jsx(CardSurfaceCtx, { value: true, children: /* @__PURE__ */ jsx(CardCtx, { value: checkable, children: loading ? /* @__PURE__ */ jsxs("div", { className: "space-y-ds-3", children: [
4322
- /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-4 w-1/3" }),
4323
- /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-3 w-full" }),
4324
- /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-3 w-2/3" })
4325
- ] }) : children }) })
4727
+ /* @__PURE__ */ jsx(CardSurfaceCtx, { value: true, children: /* @__PURE__ */ jsx(CardCtx, { value: checkable, children: loading ? (
4728
+ // Round 15 (the owner's 52.1): a placeholder stands where the content will stand — inside the card's
4729
+ // content inset (CardContent's padding), a title-height line then two body lines — never flush against the border.
4730
+ /* @__PURE__ */ jsx("div", { "data-card-loading": "", "aria-busy": "true", className: cn("pr-ds-5 py-ds-4", checkable ? "pl-ds-10" : "pl-ds-5"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-ds-3", children: [
4731
+ /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-5 w-1/3" }),
4732
+ /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-3 w-full" }),
4733
+ /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-3 w-2/3" })
4734
+ ] }) })
4735
+ ) : children }) })
4326
4736
  ]
4327
4737
  }
4328
4738
  );
@@ -4331,7 +4741,7 @@ var CardHeader = forwardRef(function CardHeader2({ className, children, actions,
4331
4741
  const isCheckable = useContext(CardCtx);
4332
4742
  const hasActions = actions || menuItems;
4333
4743
  const pl = isCheckable ? "pl-ds-10" : "pl-ds-5";
4334
- const actionsBlock = hasActions ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 shrink-0", children: [
4744
+ const actionsBlock = hasActions ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 shrink-0 ml-auto", children: [
4335
4745
  actions,
4336
4746
  menuItems && /* @__PURE__ */ jsx(
4337
4747
  DropdownMenu,
@@ -4355,8 +4765,8 @@ var CardHeader = forwardRef(function CardHeader2({ className, children, actions,
4355
4765
  ] }) : null;
4356
4766
  const iconBlock = icon ? /* @__PURE__ */ jsx("span", { className: "shrink-0 h-10 w-10 rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-muted)] flex items-center justify-center mb-ds-1", children: /* @__PURE__ */ jsx(Icon, { name: icon, size: 20, className: "text-[var(--ds-text-secondary)]" }) }) : null;
4357
4767
  if (hasActions) {
4358
- return /* @__PURE__ */ jsx("div", { ref, className: cn(pl, "pr-ds-5 pt-ds-4 last:pb-ds-4", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-ds-4", children: [
4359
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-ds-1.5 min-w-0", children: [
4768
+ return /* @__PURE__ */ jsx("div", { ref, className: cn(pl, "pr-ds-5 pt-ds-4 last:pb-ds-4", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-ds-4", children: [
4769
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-ds-1.5 grow basis-1/2 [min-width:min-content] break-words", children: [
4360
4770
  iconBlock,
4361
4771
  children
4362
4772
  ] }),
@@ -4767,7 +5177,7 @@ function Table({
4767
5177
  loading = false,
4768
5178
  emptyState,
4769
5179
  onRowClick,
4770
- mobileBreakpoint = "md",
5180
+ mobileBreakpoint: mobileBreakpointProp,
4771
5181
  ref
4772
5182
  }) {
4773
5183
  const [internalSortKey, setInternalSortKey] = useState(null);
@@ -4833,6 +5243,8 @@ function Table({
4833
5243
  (col) => col.kind ?? inferredKinds.get(col.key),
4834
5244
  [inferredKinds]
4835
5245
  );
5246
+ const preferences = usePreferences();
5247
+ const mobileBreakpoint = mobileBreakpointProp ?? preferences.tableToCardsBreakpoint ?? "md";
4836
5248
  const isMobile = useBreakpoint(mobileBreakpoint);
4837
5249
  if (isMobile) {
4838
5250
  if (loading) {
@@ -4915,7 +5327,7 @@ function Table({
4915
5327
  headerAlign === "right" ? "text-right" : headerAlign === "center" ? "text-center" : "text-left",
4916
5328
  isSortable && "cursor-pointer select-none hover:text-[var(--ds-text-primary)]"
4917
5329
  ),
4918
- style: col.maxWidth ? { maxWidth: col.maxWidth } : void 0,
5330
+ style: col.maxWidth || col.width || col.minWidth ? { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth } : void 0,
4919
5331
  onClick: isSortable ? () => handleSort(col.key) : void 0,
4920
5332
  children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-ds-1", children: [
4921
5333
  col.header,
@@ -5005,6 +5417,10 @@ function Table({
5005
5417
  } else {
5006
5418
  inner = cellContent;
5007
5419
  }
5420
+ const align = col.align ?? (kind === "number" ? "right" : "left");
5421
+ if (align !== "left" && cellContent !== null && cellContent !== void 0 && typeof cellContent !== "string" && typeof cellContent !== "number") {
5422
+ inner = /* @__PURE__ */ jsx("div", { className: cn("flex items-center", align === "center" ? "justify-center" : "justify-end"), children: inner });
5423
+ }
5008
5424
  return /* @__PURE__ */ jsx(
5009
5425
  "td",
5010
5426
  {
@@ -5014,7 +5430,7 @@ function Table({
5014
5430
  alignClass,
5015
5431
  shrink && "w-px whitespace-nowrap"
5016
5432
  ),
5017
- style: col.maxWidth ? { maxWidth: col.maxWidth } : void 0,
5433
+ style: col.maxWidth || col.width || col.minWidth ? { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth } : void 0,
5018
5434
  children: inner
5019
5435
  },
5020
5436
  col.key
@@ -5027,7 +5443,7 @@ function Table({
5027
5443
  "td",
5028
5444
  {
5029
5445
  colSpan: columns.length + (selectable ? 1 : 0) + 1,
5030
- className: "bg-[var(--ds-bg-muted)] px-ds-4 py-ds-3 text-sm text-[var(--ds-text-secondary)]",
5446
+ className: "bg-[var(--ds-bg-muted)] px-ds-6 py-ds-4 text-sm text-[var(--ds-text-primary)]",
5031
5447
  children: renderExpanded(row, i)
5032
5448
  }
5033
5449
  ) })
@@ -6791,8 +7207,10 @@ function renderIcon(icon) {
6791
7207
  if (isValidElement(icon) && icon.type === Icon) return cloneElement(icon, { size: FRAME_ICON_SIZE });
6792
7208
  return icon;
6793
7209
  }
6794
- function EmptyState({ icon, iconFrame = "circle", title, description, action, variant = "empty", bordered = false, className }) {
6795
- const iconNode = icon === void 0 || icon === null ? void 0 : renderIcon(icon);
7210
+ function EmptyState({ icon, iconFrame = "circle", title, description, action, variant = "empty", bordered = false, style: styleProp, className }) {
7211
+ const preferences = usePreferences();
7212
+ const style = styleProp ?? (preferences.emptyStyle === "text-cta" ? "text" : "illustration");
7213
+ const iconNode = style === "text" ? void 0 : icon === void 0 || icon === null ? void 0 : renderIcon(icon);
6796
7214
  return /* @__PURE__ */ jsxs(
6797
7215
  "div",
6798
7216
  {
@@ -6803,8 +7221,9 @@ function EmptyState({ icon, iconFrame = "circle", title, description, action, va
6803
7221
  bordered && "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
6804
7222
  className
6805
7223
  ),
7224
+ "data-style": style,
6806
7225
  children: [
6807
- iconFrame === "none" && iconNode ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center mb-ds-4", children: iconNode }) : /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-[var(--ds-radius-full)] bg-[var(--ds-bg-emphasis)] flex items-center justify-center mb-ds-4", children: iconNode ?? /* @__PURE__ */ jsx("span", { className: "text-2xl text-[var(--ds-text-muted)]", children: defaultIcons[variant] }) }),
7226
+ style === "text" ? null : iconFrame === "none" && iconNode ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center mb-ds-4", children: iconNode }) : /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-[var(--ds-radius-full)] bg-[var(--ds-bg-emphasis)] flex items-center justify-center mb-ds-4", children: iconNode ?? /* @__PURE__ */ jsx("span", { className: "text-2xl text-[var(--ds-text-muted)]", children: defaultIcons[variant] }) }),
6808
7227
  /* @__PURE__ */ jsx("h3", { className: "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] text-center", children: title }),
6809
7228
  description && /* @__PURE__ */ jsx("p", { className: "mt-ds-1 text-sm text-[var(--ds-text-muted)] max-w-sm text-center", children: description }),
6810
7229
  action && /* @__PURE__ */ jsx("div", { className: "mt-ds-4", children: action })
@@ -6818,8 +7237,9 @@ function initialsOf(name) {
6818
7237
  function looksLikeUrl(s) {
6819
7238
  return /^(?:https?:|data:|blob:|\/|\.{1,2}\/)/.test(s) || /\.[a-z]{2,5}(?:$|[?#])/i.test(s);
6820
7239
  }
6821
- function EntityCell({ avatar, src, initials, primary, secondary, size = "md", className }) {
7240
+ function EntityCell({ avatar, src, initials, primary, secondary, size: sizeProp, className }) {
6822
7241
  const inTableCard = useContext(TableCardCtx);
7242
+ const size = sizeProp ?? (inTableCard ? "sm" : "md");
6823
7243
  let avatarElement;
6824
7244
  if (typeof avatar === "string") {
6825
7245
  avatarElement = looksLikeUrl(avatar) ? /* @__PURE__ */ jsx(Avatar, { src: avatar, alt: primary, size }) : /* @__PURE__ */ jsx(Avatar, { initials: avatar, alt: primary, size });
@@ -8334,11 +8754,13 @@ var StackedList = forwardRef(function StackedList2({
8334
8754
  const interactive = !!item.onClick;
8335
8755
  const hasEmphasis = item.unread !== void 0;
8336
8756
  const multiline = !!item.description;
8757
+ const stackThis = stackMeta || isPhone && !!item.meta && !!item.actions;
8758
+ const tall = multiline || stackThis && !stackMeta && !!item.leading;
8337
8759
  const trailing = item.unread || item.meta ? /* @__PURE__ */ jsxs("div", { className: cn(
8338
8760
  "shrink-0 flex items-center gap-ds-2 text-sm",
8339
8761
  // Below sm an article's meta sits under the title, left-aligned, whatever alignment its own markup
8340
8762
  // carries (round 6: a builder's items-end column put its badge mid-row).
8341
- stackMeta && "self-start [&>*]:items-start! [&>*]:text-left!",
8763
+ stackThis && "self-start [&>*]:items-start! [&>*]:text-left!",
8342
8764
  isSelected ? "text-[var(--ds-nav-v-active-muted)]" : "text-[var(--ds-text-muted)]"
8343
8765
  ), children: [
8344
8766
  item.unread && /* @__PURE__ */ jsx(
@@ -8356,7 +8778,7 @@ var StackedList = forwardRef(function StackedList2({
8356
8778
  {
8357
8779
  className: cn(
8358
8780
  "flex gap-ds-3 py-ds-3",
8359
- multiline ? "items-start" : "items-center",
8781
+ tall ? "items-start" : "items-center",
8360
8782
  inset,
8361
8783
  interactive && [
8362
8784
  "cursor-pointer [transition:var(--ds-transition-interactive)]",
@@ -8383,7 +8805,7 @@ var StackedList = forwardRef(function StackedList2({
8383
8805
  }
8384
8806
  } : void 0,
8385
8807
  children: [
8386
- item.leading && /* @__PURE__ */ jsx("div", { className: cn("shrink-0", multiline && "mt-ds-0.5"), children: item.leading }),
8808
+ item.leading && /* @__PURE__ */ jsx("div", { className: cn("shrink-0", tall && "mt-ds-0.5"), children: item.leading }),
8387
8809
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
8388
8810
  /* @__PURE__ */ jsxs("div", { className: cn("flex gap-ds-2", stackMeta ? "flex-col" : "items-center justify-between"), children: [
8389
8811
  /* @__PURE__ */ jsx(
@@ -8401,9 +8823,10 @@ var StackedList = forwardRef(function StackedList2({
8401
8823
  (multiline || stackMeta) && trailing
8402
8824
  ] }),
8403
8825
  item.subtitle && /* @__PURE__ */ jsx(TruncText, { className: cn("text-sm", isSelected ? "text-[var(--ds-nav-v-active-muted)]" : "text-[var(--ds-text-secondary)]"), children: item.subtitle }),
8404
- item.description && /* @__PURE__ */ jsx(TruncText, { className: cn("text-xs", isSelected ? "text-[var(--ds-nav-v-active-muted)]" : "text-[var(--ds-text-muted)]"), children: item.description })
8826
+ item.description && /* @__PURE__ */ jsx(TruncText, { className: cn("text-xs", isSelected ? "text-[var(--ds-nav-v-active-muted)]" : "text-[var(--ds-text-muted)]"), children: item.description }),
8827
+ stackThis && !stackMeta && !multiline && trailing
8405
8828
  ] }),
8406
- !multiline && !stackMeta && trailing,
8829
+ !multiline && !stackThis && trailing,
8407
8830
  item.actions && /* @__PURE__ */ jsx(
8408
8831
  "div",
8409
8832
  {
@@ -8862,7 +9285,7 @@ function TabButton({
8862
9285
  className,
8863
9286
  hasPanel
8864
9287
  }) {
8865
- const { ref: truncRef, isTruncated } = useTextOverflow({ lines: 1, disabled: !truncate });
9288
+ const { ref: truncRef, isTruncated } = useTextOverflow({ lines: 1, disabled: !truncate && orientation !== "horizontal" });
8866
9289
  const isActive = activeTab === id;
8867
9290
  const tabId = `ds-tab-${id}`;
8868
9291
  const panelId = hasPanel === false ? void 0 : `ds-tabpanel-${id}`;
@@ -8921,6 +9344,8 @@ function TabButton({
8921
9344
  onKeyDown,
8922
9345
  className: cn(
8923
9346
  "px-ds-4 py-ds-2.5 text-sm font-medium",
9347
+ // never wrap: the strip scrolls when the tabs do not fit (round 17, 54.4)
9348
+ !fullWidth && !truncate && "shrink-0 whitespace-nowrap",
8924
9349
  disabled ? "cursor-not-allowed opacity-disabled" : "cursor-pointer",
8925
9350
  "transition-colors duration-[var(--ds-transition-speed)]",
8926
9351
  "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
@@ -8937,7 +9362,7 @@ function TabButton({
8937
9362
  countBadge && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: countBadge })
8938
9363
  ] }) : /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-ds-2 min-w-0", children: [
8939
9364
  icon && /* @__PURE__ */ jsx(Icon, { name: icon, size: 16, className: "shrink-0" }),
8940
- label,
9365
+ /* @__PURE__ */ jsx("span", { ref: truncRef, className: "truncate max-w-[24ch]", children: label }),
8941
9366
  countBadge
8942
9367
  ] })
8943
9368
  }
@@ -9400,8 +9825,10 @@ function Popover({ open, onOpenChange, trigger, children, side = "bottom", align
9400
9825
  )
9401
9826
  ] });
9402
9827
  }
9403
- function Pagination({ currentPage, totalPages, onPageChange, pageSize, pageSizeOptions, onPageSizeChange, className }) {
9828
+ function Pagination({ currentPage, totalPages, onPageChange, pageSize: pageSizeProp, pageSizeOptions, onPageSizeChange, className }) {
9404
9829
  const pages = buildPageRange(currentPage, totalPages);
9830
+ const preferences = usePreferences();
9831
+ const pageSize = pageSizeProp ?? preferences.itemsPerPage;
9405
9832
  return /* @__PURE__ */ jsxs("nav", { "aria-label": "Pagination", className: cn("flex flex-wrap items-center gap-ds-3", className), children: [
9406
9833
  pageSizeOptions && pageSize != null && onPageSizeChange && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
9407
9834
  /* @__PURE__ */ jsx("span", { className: "text-sm text-[var(--ds-text-muted)] whitespace-nowrap", children: "Rows per page" }),
@@ -10167,14 +10594,16 @@ function SidebarNav({
10167
10594
  item.active ? isRoot ? "bg-[var(--ds-sidebar-active-bg)] [background-image:var(--ds-sidebar-active-image)] text-[var(--ds-sidebar-active-text)] font-medium border-l-[length:var(--ds-sidebar-active-rail)] border-[var(--ds-sidebar-active-accent)]" : "bg-[var(--ds-sidebar-active-bg)] [background-image:var(--ds-sidebar-active-image)] text-[var(--ds-sidebar-active-accent)] font-medium" : isRoot ? "text-[var(--ds-sidebar-text-secondary)] hover:bg-[var(--ds-sidebar-item-hover)] hover:text-[var(--ds-sidebar-text)] border-l-[length:var(--ds-sidebar-active-rail)] border-transparent" : "text-[var(--ds-sidebar-text-muted)] hover:bg-[var(--ds-sidebar-item-hover)] hover:text-[var(--ds-sidebar-text)]"
10168
10595
  ),
10169
10596
  children: [
10170
- item.icon && /* @__PURE__ */ jsx(
10597
+ item.icon && // Round 15 (the owner's 52.8): the icon sits in a box one line tall, so a collapsed (icon-only) item is exactly as
10598
+ // tall as the expanded item whose label sets that line — the rail no longer jumps when the sidebar collapses.
10599
+ /* @__PURE__ */ jsx("span", { className: "inline-flex items-center shrink-0 min-h-[1lh]", "data-sidebar-icon": "", children: /* @__PURE__ */ jsx(
10171
10600
  Icon,
10172
10601
  {
10173
10602
  name: item.icon,
10174
10603
  size: isRoot ? rootIconSize : childIconSize,
10175
10604
  className: cn("shrink-0", item.active && "text-[var(--ds-sidebar-active-accent)]")
10176
10605
  }
10177
- ),
10606
+ ) }),
10178
10607
  !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
10179
10608
  /* @__PURE__ */ jsx(TruncLabel3, { className: isRoot ? "flex-1" : void 0, container: portalContainer ?? void 0, children: item.label }),
10180
10609
  item.badge && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: item.badge })
@@ -10669,6 +11098,15 @@ var FLOATING_BLOCK = cn(
10669
11098
  "overflow-hidden"
10670
11099
  );
10671
11100
  var PageContainerContext = createContext(null);
11101
+ function useContentWidth(width) {
11102
+ const ctx = useContext(PageContainerContext);
11103
+ const request = ctx?.requestContentWidth;
11104
+ useEffect(() => {
11105
+ if (!request) return;
11106
+ request(width);
11107
+ return () => request(null);
11108
+ }, [request, width]);
11109
+ }
10672
11110
  function usePageContainer() {
10673
11111
  return useContext(PageContainerContext);
10674
11112
  }
@@ -10709,6 +11147,7 @@ function floatingBlockPadding(navbarFloating, sidebarFloating, overlap) {
10709
11147
  var PageContainer = forwardRef(function PageContainer2({
10710
11148
  width,
10711
11149
  maxWidth = "xl",
11150
+ contentWidth,
10712
11151
  padding = "default",
10713
11152
  verticalPadding,
10714
11153
  centered = true,
@@ -10729,8 +11168,19 @@ var PageContainer = forwardRef(function PageContainer2({
10729
11168
  }, []);
10730
11169
  const resolvedMaxWidth = width ? widthToMaxWidth[width] : maxWidth;
10731
11170
  const maxWidthValue = maxWidthValues[resolvedMaxWidth];
11171
+ const [requestedWidth, setRequestedWidth] = useState(null);
11172
+ const requestContentWidth = useCallback((w) => setRequestedWidth(w), []);
11173
+ const effectiveContentWidth = contentWidth ?? requestedWidth ?? void 0;
11174
+ const contentCap = effectiveContentWidth ? maxWidthValues[widthToMaxWidth[effectiveContentWidth]] : void 0;
11175
+ const capped = (content) => contentCap ? /* @__PURE__ */ jsx("div", { "data-content-width": effectiveContentWidth, style: { maxWidth: contentCap }, children: content }) : content;
10732
11176
  const kids = Children.toArray(children);
10733
11177
  const headerIdx = kids.findIndex(isPageHeaderElement);
11178
+ const headerCount = useRef(0);
11179
+ const [wrappedHeader, setWrappedHeader] = useState(false);
11180
+ const reportHeader = useCallback((present) => {
11181
+ headerCount.current = Math.max(0, headerCount.current + (present ? 1 : -1));
11182
+ setWrappedHeader(headerCount.current > 0);
11183
+ }, []);
10734
11184
  const launcherCorner = shell?.launcherCorner ?? null;
10735
11185
  const vPad = verticalPadding ?? "default";
10736
11186
  const launcherPadding = launcherCorner ? { paddingBottom: `max(${bottomPaddingValues[vPad]}, var(--ds-toast-inset-${launcherCorner}, 0px))` } : {};
@@ -10776,21 +11226,22 @@ var PageContainer = forwardRef(function PageContainer2({
10776
11226
  observedRef.current = null;
10777
11227
  }, []);
10778
11228
  if (headerIdx === -1 || !headerEl || !r) {
10779
- return /* @__PURE__ */ jsx(
11229
+ return /* @__PURE__ */ jsx(PageContainerContext, { value: { contentMaxWidth: maxWidthValue, placement: "inside", containedBreadcrumbs: false, breadcrumbsInsideHeader: false, noBottomMargin: false, overlap: false, setPinnedHeight, reportHeader, requestContentWidth, insidePaddedColumn: true }, children: /* @__PURE__ */ jsx(
10780
11230
  "div",
10781
11231
  {
10782
- ref,
11232
+ ref: setRoot,
11233
+ "data-page-container": wrappedHeader ? "" : void 0,
10783
11234
  className: cn(
10784
11235
  "w-full",
10785
11236
  paddingClasses[padding],
10786
- verticalPaddingClasses[vPad],
11237
+ wrappedHeader ? bottomPaddingClasses[vPad] : verticalPaddingClasses[vPad],
10787
11238
  centered && "mx-auto",
10788
11239
  className
10789
11240
  ),
10790
11241
  style: { maxWidth: maxWidthValue, containerType: "inline-size", ...launcherPadding },
10791
- children
11242
+ children: capped(children)
10792
11243
  }
10793
- );
11244
+ ) });
10794
11245
  }
10795
11246
  const rest = kids.filter((_, i) => i !== headerIdx);
10796
11247
  const snapping = r.behavior === "snapping";
@@ -10802,15 +11253,15 @@ var PageContainer = forwardRef(function PageContainer2({
10802
11253
  const placement = block ? "block" : r.outside ? "outside" : "inside";
10803
11254
  const breadcrumbsInsideHeader = snapping && floatingHeader && contained;
10804
11255
  const strip = contained && !breadcrumbsInsideHeader && crumbs ? /* @__PURE__ */ jsx(Breadcrumbs, { variant: "contained", itemVariant: r.breadcrumbStyle, bleed: true, contentMaxWidth: maxWidthValue, separator: r.breadcrumbSeparator, items: headerEl.props.title ? [...crumbs, { label: headerEl.props.title }] : crumbs }) : null;
10805
- const columnClass = (pad2) => cn("w-full", paddingClasses[pad2], centered && "mx-auto");
11256
+ const columnClass = (pad3) => cn("w-full", paddingClasses[pad3], centered && "mx-auto");
10806
11257
  const columnStyle = { maxWidth: maxWidthValue, containerType: "inline-size" };
10807
- const header = /* @__PURE__ */ jsx(PageContainerContext, { value: { contentMaxWidth: maxWidthValue, placement, containedBreadcrumbs: contained, breadcrumbsInsideHeader, noBottomMargin: block, overlap: overlapActive, setPinnedHeight }, children: renderHeader ? renderHeader(headerEl) : headerEl });
11258
+ const header = /* @__PURE__ */ jsx(PageContainerContext, { value: { contentMaxWidth: maxWidthValue, placement, containedBreadcrumbs: contained, breadcrumbsInsideHeader, noBottomMargin: block, overlap: overlapActive, setPinnedHeight, reportHeader, requestContentWidth }, children: renderHeader ? renderHeader(headerEl) : headerEl });
10808
11259
  const contentPadding = floatingHeader && shell?.isMobile ? "none" : padding;
10809
11260
  const wrapperStyle = floatingHeader || overlapActive ? {
10810
11261
  ...floatingHeader ? { paddingRight: "var(--ds-space-3)", paddingLeft: r.sidebarFloating ? 0 : "var(--ds-space-3)" } : {},
10811
11262
  ...overlapActive ? { marginTop: "calc(-1 * var(--ds-overlap, 0px))", position: "relative", zIndex: 1 } : {}
10812
11263
  } : void 0;
10813
- const column = /* @__PURE__ */ jsx("div", { ref: columnRef, className: cn(columnClass(contentPadding), bottomPaddingClasses[vPad], className), style: { ...columnStyle, ...launcherPadding }, children: rest });
11264
+ const column = /* @__PURE__ */ jsx(PageContainerContext, { value: { contentMaxWidth: maxWidthValue, placement: "inside", containedBreadcrumbs: false, breadcrumbsInsideHeader: false, noBottomMargin: false, overlap: false, setPinnedHeight, reportHeader, requestContentWidth, insidePaddedColumn: true }, children: /* @__PURE__ */ jsx("div", { ref: columnRef, className: cn(columnClass(contentPadding), bottomPaddingClasses[vPad], className), style: { ...columnStyle, ...launcherPadding }, children: capped(rest) }) });
10814
11265
  return /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-page-container": "", className: "w-full", children: [
10815
11266
  placement === "block" ? /* @__PURE__ */ jsx("div", { style: floatingBlockPadding(r.navbarFloating, r.sidebarFloating, overlapActive), children: /* @__PURE__ */ jsxs("div", { className: FLOATING_BLOCK, children: [
10816
11267
  strip,
@@ -10969,6 +11420,12 @@ var PageHeader = forwardRef(function PageHeader2({
10969
11420
  }, ref) {
10970
11421
  const shell = useAppShell();
10971
11422
  const container = usePageContainer();
11423
+ const reportHeader = container?.reportHeader;
11424
+ useLayoutEffect(() => {
11425
+ if (!reportHeader) return;
11426
+ reportHeader(true);
11427
+ return () => reportHeader(false);
11428
+ }, [reportHeader]);
10972
11429
  const r = resolvePageHeader(shell?.theme, {
10973
11430
  variant: variantProp,
10974
11431
  behavior: behaviorProp,
@@ -10985,11 +11442,14 @@ var PageHeader = forwardRef(function PageHeader2({
10985
11442
  const bcPill = r.breadcrumbStyle === "pill";
10986
11443
  const bcSep = r.breadcrumbSeparator;
10987
11444
  const contentMaxWidth = contentMaxWidthProp ?? container?.contentMaxWidth;
11445
+ const insidePaddedColumn = !!container?.insidePaddedColumn;
11446
+ const gutterWidth = insidePaddedColumn ? void 0 : contentMaxWidth;
11447
+ const floating = isFloating && !insidePaddedColumn;
10988
11448
  const stickyTop = shell ? "top-0" : "top-[var(--ds-navbar-height)]";
10989
11449
  const snapping = behavior === "snapping";
10990
11450
  const inlineBreadcrumbs = container?.containedBreadcrumbs && !snapping ? void 0 : breadcrumbs;
10991
11451
  const hideExpandedBreadcrumbs = hideExpandedBreadcrumbsProp ?? (!!container?.containedBreadcrumbs && snapping);
10992
- const containedBreadcrumbs = containedBreadcrumbsProp ?? (container?.breadcrumbsInsideHeader && breadcrumbs?.length ? /* @__PURE__ */ jsx(Breadcrumbs, { variant: "contained", itemVariant: r.breadcrumbStyle, bleed: true, contentMaxWidth, separator: bcSep, items: [...breadcrumbs, { label: title }] }) : void 0);
11452
+ const containedBreadcrumbs = containedBreadcrumbsProp ?? (container?.breadcrumbsInsideHeader && breadcrumbs?.length ? /* @__PURE__ */ jsx(Breadcrumbs, { variant: "contained", itemVariant: r.breadcrumbStyle, bleed: true, contentMaxWidth: gutterWidth, separator: bcSep, items: [...breadcrumbs, { label: title }] }) : void 0);
10993
11453
  const noBottomMargin = noBottomMarginProp ?? container?.noBottomMargin;
10994
11454
  const overlapActive = container?.overlap ?? false;
10995
11455
  if (behavior === "snapping") {
@@ -11007,7 +11467,7 @@ var PageHeader = forwardRef(function PageHeader2({
11007
11467
  expandedContent,
11008
11468
  collapseOffset,
11009
11469
  pinned,
11010
- contentMaxWidth,
11470
+ contentMaxWidth: gutterWidth,
11011
11471
  variant,
11012
11472
  intensity,
11013
11473
  backgroundImage,
@@ -11019,7 +11479,7 @@ var PageHeader = forwardRef(function PageHeader2({
11019
11479
  as: Heading,
11020
11480
  className,
11021
11481
  stickyTop,
11022
- isFloating,
11482
+ isFloating: floating,
11023
11483
  navbarFloating,
11024
11484
  sidebarFloating,
11025
11485
  containedBreadcrumbs,
@@ -11029,7 +11489,7 @@ var PageHeader = forwardRef(function PageHeader2({
11029
11489
  }
11030
11490
  let header;
11031
11491
  if (variant === "minimal") {
11032
- header = /* @__PURE__ */ jsx(MinimalHeader, { title, badge, breadcrumbs: inlineBreadcrumbs, actions, breadcrumbPill: bcPill, breadcrumbSeparator: bcSep, as: Heading, className, headerRef: isFloating ? void 0 : ref, isFloating, contentMaxWidth });
11492
+ header = /* @__PURE__ */ jsx(MinimalHeader, { title, badge, breadcrumbs: inlineBreadcrumbs, actions, breadcrumbPill: bcPill, breadcrumbSeparator: bcSep, as: Heading, className, headerRef: floating ? void 0 : ref, isFloating: floating, contentMaxWidth: gutterWidth });
11033
11493
  } else if (variant === "tabs-integrated") {
11034
11494
  header = /* @__PURE__ */ jsx(
11035
11495
  TabsIntegratedHeader,
@@ -11046,15 +11506,16 @@ var PageHeader = forwardRef(function PageHeader2({
11046
11506
  breadcrumbSeparator: bcSep,
11047
11507
  as: Heading,
11048
11508
  className,
11049
- headerRef: isFloating ? void 0 : ref,
11050
- isFloating,
11051
- contentMaxWidth
11509
+ headerRef: floating ? void 0 : ref,
11510
+ isFloating: floating,
11511
+ contentMaxWidth: gutterWidth
11052
11512
  }
11053
11513
  );
11054
11514
  } else if (variant === "colourful") {
11055
11515
  header = /* @__PURE__ */ jsx(
11056
11516
  ColourfulHeader,
11057
11517
  {
11518
+ gutter: !insidePaddedColumn,
11058
11519
  title,
11059
11520
  description,
11060
11521
  badge,
@@ -11066,13 +11527,13 @@ var PageHeader = forwardRef(function PageHeader2({
11066
11527
  intensity,
11067
11528
  backgroundImage,
11068
11529
  overlapActive,
11069
- contentMaxWidth,
11530
+ contentMaxWidth: gutterWidth,
11070
11531
  breadcrumbPill: bcPill,
11071
11532
  breadcrumbSeparator: bcSep,
11072
11533
  as: Heading,
11073
11534
  className,
11074
- headerRef: isFloating ? void 0 : ref,
11075
- isFloating,
11535
+ headerRef: floating ? void 0 : ref,
11536
+ isFloating: floating,
11076
11537
  noBottomMargin
11077
11538
  }
11078
11539
  );
@@ -11093,13 +11554,13 @@ var PageHeader = forwardRef(function PageHeader2({
11093
11554
  breadcrumbSeparator: bcSep,
11094
11555
  as: Heading,
11095
11556
  className,
11096
- headerRef: isFloating ? void 0 : ref,
11097
- isFloating,
11098
- contentMaxWidth
11557
+ headerRef: floating ? void 0 : ref,
11558
+ isFloating: floating,
11559
+ contentMaxWidth: gutterWidth
11099
11560
  }
11100
11561
  );
11101
11562
  }
11102
- if (isFloating) {
11563
+ if (floating) {
11103
11564
  return /* @__PURE__ */ jsx(
11104
11565
  "div",
11105
11566
  {
@@ -11231,7 +11692,8 @@ function ColourfulHeader({
11231
11692
  className,
11232
11693
  headerRef,
11233
11694
  isFloating,
11234
- noBottomMargin
11695
+ noBottomMargin,
11696
+ gutter = true
11235
11697
  }) {
11236
11698
  const isBold = intensity === "bold";
11237
11699
  const hasBreadcrumbs = !!(breadcrumbs && breadcrumbs.length > 0);
@@ -11271,11 +11733,12 @@ function ColourfulHeader({
11271
11733
  "div",
11272
11734
  {
11273
11735
  className: cn(
11274
- "relative z-10 w-full mx-auto px-ds-6 pb-ds-6",
11736
+ "relative z-10 w-full mx-auto pb-ds-6",
11737
+ gutter && "px-ds-6",
11275
11738
  hasBreadcrumbs ? "pt-ds-3" : "pt-ds-6"
11276
11739
  ),
11277
11740
  style: {
11278
- maxWidth: contentMaxWidth ?? "var(--ds-bp-xl)",
11741
+ maxWidth: gutter ? contentMaxWidth ?? "var(--ds-bp-xl)" : void 0,
11279
11742
  containerType: "inline-size",
11280
11743
  ...overlapActive ? { paddingBottom: `calc(var(--ds-space-6) + var(--ds-overlap, 0px))` } : {}
11281
11744
  },
@@ -11557,7 +12020,7 @@ function SnappingHeader({
11557
12020
  }
11558
12021
  );
11559
12022
  if (effectiveMaxWidth) {
11560
- return /* @__PURE__ */ jsx("div", { className: cn("w-full mx-auto", collapsed ? "px-ds-3 @md:px-ds-6" : "px-ds-6"), style: { maxWidth: effectiveMaxWidth, containerType: "inline-size" }, children: innerContent });
12023
+ return /* @__PURE__ */ jsx("div", { className: "w-full mx-auto px-ds-6", style: { maxWidth: effectiveMaxWidth, containerType: "inline-size" }, children: innerContent });
11561
12024
  }
11562
12025
  return innerContent;
11563
12026
  })()
@@ -12197,8 +12660,7 @@ var Navbar = forwardRef(function Navbar2({
12197
12660
  hasBorder && "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
12198
12661
  GLASS_BACKDROP
12199
12662
  );
12200
- const isMobile = appShell?.isMobile;
12201
- const padStyle = resolvedSize === "sm" ? "var(--ds-space-2) var(--ds-space-4)" : resolvedSize === "lg" ? `var(--ds-space-2) var(${isMobile ? "--ds-space-3" : "--ds-space-6"})` : `var(--ds-space-1) var(${isMobile ? "--ds-space-3" : "--ds-space-6"})`;
12663
+ const padStyle = resolvedSize === "sm" ? "var(--ds-space-2) var(--ds-space-4)" : resolvedSize === "lg" ? `var(--ds-space-2) var(--ds-space-6)` : `var(--ds-space-1) var(--ds-space-6)`;
12202
12664
  const floatingClasses = isFloating ? cn("rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]", GLASS_SURFACE) : void 0;
12203
12665
  let headerEl;
12204
12666
  if (align === "center") {
@@ -12365,7 +12827,8 @@ function MobileMenuItem({
12365
12827
  }
12366
12828
  var DEFAULT_BUTTON_ATTR = "data-dialog-default-button";
12367
12829
  var sizeClasses = {
12368
- sm: "max-w-sm",
12830
+ // round 16 (the owner's item 5): the small dialog is 448 px, not 384 — a delete confirmation with a typed field was cramped
12831
+ sm: "max-w-md",
12369
12832
  md: "max-w-lg",
12370
12833
  lg: "max-w-2xl",
12371
12834
  xl: "max-w-4xl",
@@ -13484,8 +13947,11 @@ function useToast() {
13484
13947
  if (!ctx) throw new Error("useToast must be used within <ToastProvider>");
13485
13948
  return ctx;
13486
13949
  }
13950
+ function useToastOptional() {
13951
+ return useContext(ToastContext);
13952
+ }
13487
13953
  var toastSeq = 0;
13488
- function ToastProvider({ children, maxVisible = 1, position = "bottom-right", duration = 5e3, mode, pauseOnHover = true }) {
13954
+ function ToastProvider({ children, maxVisible = 1, position = "bottom-right", duration = 5e3, mode, pauseOnHover = true, coalesce = true }) {
13489
13955
  const [queue, setQueue] = useState([]);
13490
13956
  const isMobile = useBreakpoint("sm");
13491
13957
  const dismiss = useCallback((id) => {
@@ -13502,11 +13968,24 @@ function ToastProvider({ children, maxVisible = 1, position = "bottom-right", du
13502
13968
  message,
13503
13969
  variant: options.variant ?? "default",
13504
13970
  duration: options.duration ?? duration,
13505
- action: options.action
13971
+ action: options.action,
13972
+ key: options.key ?? options.variant ?? "default"
13506
13973
  };
13507
- setQueue((q) => q.some((t) => t.id === id) ? q.map((t) => t.id === id ? entry : t) : [...q, entry]);
13974
+ const slots = Math.max(1, maxVisible);
13975
+ setQueue((q) => {
13976
+ if (q.some((t) => t.id === id)) return q.map((t) => t.id === id ? entry : t);
13977
+ if (coalesce && q.length >= slots) {
13978
+ const waiting = q.findIndex((t, i) => i >= slots && t.key === entry.key);
13979
+ if (waiting >= 0) {
13980
+ const next = [...q];
13981
+ next[waiting] = entry;
13982
+ return next;
13983
+ }
13984
+ }
13985
+ return [...q, entry];
13986
+ });
13508
13987
  return id;
13509
- }, [duration]);
13988
+ }, [duration, maxVisible, coalesce]);
13510
13989
  const api = useMemo(() => ({
13511
13990
  toast,
13512
13991
  success: (m, o) => toast(m, { ...o, variant: "success" }),
@@ -13561,6 +14040,75 @@ function ToastProvider({ children, maxVisible = 1, position = "bottom-right", du
13561
14040
  )
13562
14041
  ] });
13563
14042
  }
14043
+ function useDestructiveAction(options) {
14044
+ const { action, title, description, confirmLabel, typeToConfirm, onConfirm, onUndo, undoMessage, severity } = options;
14045
+ const preferences = usePreferences();
14046
+ const toast = useToastOptional();
14047
+ const [open, setOpen] = useState(false);
14048
+ const [typed, setTyped] = useState("");
14049
+ const [busy, setBusy] = useState(false);
14050
+ const key = action ?? preferenceActionKey(title.replace(/\?$/, ""));
14051
+ const mode = preferences.destructiveMode ?? "dialog";
14052
+ const high = severity === "high" || (preferences.highSeverityActions ?? []).includes(key);
14053
+ const label = confirmLabel ?? title.replace(/\?$/, "").split(/\s+/)[0];
14054
+ const listed = (preferences.typeToConfirmActions ?? []).includes(key) || high;
14055
+ const typedText = typeToConfirm && typeToConfirm.trim() ? typeToConfirm : label.toUpperCase();
14056
+ const typedRequired = listed;
14057
+ const undoable = mode === "undo" && !high && typeof onUndo === "function" && toast !== null;
14058
+ const close = () => {
14059
+ setOpen(false);
14060
+ setTyped("");
14061
+ };
14062
+ const failureMessage = (err) => err instanceof Error && err.message ? err.message : `${title.replace(/\?$/, "")} failed. Try again.`;
14063
+ const confirm = async () => {
14064
+ setBusy(true);
14065
+ try {
14066
+ await onConfirm();
14067
+ close();
14068
+ } catch (err) {
14069
+ toast?.error(failureMessage(err));
14070
+ } finally {
14071
+ setBusy(false);
14072
+ }
14073
+ };
14074
+ const run = () => {
14075
+ if (undoable) {
14076
+ void (async () => {
14077
+ try {
14078
+ await onConfirm();
14079
+ } catch (err) {
14080
+ toast.error(failureMessage(err));
14081
+ return;
14082
+ }
14083
+ const seconds = preferences.undoWindowSeconds ?? 5;
14084
+ toast.success(undoMessage ?? `${title.replace(/\?$/, "")} done`, { duration: seconds * 1e3, action: { label: "Undo", onClick: onUndo } });
14085
+ })();
14086
+ return;
14087
+ }
14088
+ setOpen(true);
14089
+ };
14090
+ const matches = !typedRequired || typed.trim().toLowerCase() === typedText.trim().toLowerCase();
14091
+ const element = open ? /* @__PURE__ */ jsx(
14092
+ Dialog,
14093
+ {
14094
+ open,
14095
+ onClose: close,
14096
+ title,
14097
+ description,
14098
+ variant: "destructive",
14099
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
14100
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: close, disabled: busy, children: "Cancel" }),
14101
+ /* @__PURE__ */ jsx(Button, { variant: "destructive", onClick: () => void confirm(), disabled: !matches || busy, loading: busy, children: label })
14102
+ ] }),
14103
+ children: typedRequired && /* @__PURE__ */ jsx(Field, { label: /* @__PURE__ */ jsxs(Fragment, { children: [
14104
+ "Type ",
14105
+ /* @__PURE__ */ jsx("strong", { children: typedText }),
14106
+ " to confirm"
14107
+ ] }), children: /* @__PURE__ */ jsx(Input, { value: typed, onChange: (e) => setTyped(e.target.value), autoComplete: "off" }) })
14108
+ }
14109
+ ) : null;
14110
+ return { run, element, open };
14111
+ }
13564
14112
  var SectionHeading = forwardRef(function SectionHeading2({
13565
14113
  title,
13566
14114
  description,
@@ -13700,6 +14248,6 @@ var TextOverflow = forwardRef(function TextOverflow2({
13700
14248
  return /* @__PURE__ */ jsx(Tooltip, { content: resolvedTooltipContent, children: content });
13701
14249
  });
13702
14250
 
13703
- export { Accordion, ActionPanel, ActionPanelGroup, Alert, AsidePanel, Avatar, AvatarGroup, BottomNav, Breadcrumbs, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Dropdown, DropdownMenu, EmptyState, EntityCell, Field, FileUpload, FormWrapper, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, NumberInput, PageContainer, PageHeader, Pagination, PinInput, Popover, Progress, RadioGroup, Rating, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, Textarea, TimePicker, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, useFormContext, useSidebar, useToast };
14251
+ export { Accordion, ActionPanel, ActionPanelGroup, Alert, AsidePanel, Avatar, AvatarGroup, BottomNav, Breadcrumbs, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Dropdown, DropdownMenu, EmptyState, EntityCell, Field, FileUpload, FormWrapper, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, NumberInput, PageContainer, PageHeader, Pagination, PinInput, Popover, Progress, RadioGroup, Rating, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, Textarea, TimePicker, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, useContentWidth, useDestructiveAction, useFormContext, useSidebar, useToast, useToastOptional };
13704
14252
  //# sourceMappingURL=index.js.map
13705
14253
  //# sourceMappingURL=index.js.map