gridsmith-ui 0.17.1 → 0.17.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { Button, useAppShell, ControlSizeContext, useDirtyGuard, OverlayCloseCtx, DiscardConfirm, Spinner, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-J43HCK7Q.js';
2
- export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell, useUnsavedChanges } from './chunk-J43HCK7Q.js';
1
+ import { Button, useAppShell, REACT_VERSION_PROBLEM, ControlSizeContext, useDirtyGuard, OverlayCloseCtx, DiscardConfirm, Spinner, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-CIVSNXFG.js';
2
+ export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell, useUnsavedChanges } from './chunk-CIVSNXFG.js';
3
3
  import { Skeleton } from './chunk-Z63OSN64.js';
4
4
  export { Skeleton } from './chunk-Z63OSN64.js';
5
- import { FOCUSABLE, usePopupFocus, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberFocus, focusInitial, OVERLAY_CLOSE_ATTR, focusAfterRemoval, rovingTabStop, useGridKeyboard, useNonModalDialog, usePressMarks, useReturnFocus, useCloseOnFocusOut } from './chunk-YQABRUWD.js';
6
- export { useGridKeyboard } from './chunk-YQABRUWD.js';
5
+ import { FOCUSABLE, usePopupFocus, FIELD, ZOOM_KEYS, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberFocus, focusInitial, OVERLAY_CLOSE_ATTR, focusAfterRemoval, rovingTabStop, useGridKeyboard, useNonModalDialog, usePressMarks, useReturnFocus, useCloseOnFocusOut } from './chunk-JXANCXK6.js';
6
+ export { useGridKeyboard } from './chunk-JXANCXK6.js';
7
7
  import { Link } from './chunk-XGSQOMEY.js';
8
8
  export { CodeBlock, Divider, Link } from './chunk-XGSQOMEY.js';
9
9
  import { Badge } from './chunk-PVTGMCVR.js';
@@ -2012,8 +2012,8 @@ function DateRangePicker({
2012
2012
  };
2013
2013
  const previewEnd = value.start && !value.end && hover && hover >= value.start ? hover : null;
2014
2014
  const rangeStart = value.start;
2015
- const rangeEnd = value.end ?? previewEnd;
2016
- const inRange = (d) => !!(rangeStart && rangeEnd && d > rangeStart && d < rangeEnd);
2015
+ const rangeEnd2 = value.end ?? previewEnd;
2016
+ const inRange = (d) => !!(rangeStart && rangeEnd2 && d > rangeStart && d < rangeEnd2);
2017
2017
  const months = [viewDate, addMonths(viewDate, 1)];
2018
2018
  const summary = value.start && value.end ? `${fmt(value.start)} \u2013 ${fmt(value.end)}` : value.start ? `${fmt(value.start)} \u2013 \u2026` : null;
2019
2019
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
@@ -2094,7 +2094,7 @@ function DateRangePicker({
2094
2094
  getMonthGrid(m.getFullYear(), m.getMonth(), weekStartsOn).map((week, w) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5", children: week.map((d) => {
2095
2095
  if (!isSameMonth(d, m)) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, d.toISOString());
2096
2096
  const isStart = !!rangeStart && isSameDay2(d, rangeStart);
2097
- const isEnd = !!rangeEnd && isSameDay2(d, rangeEnd);
2097
+ const isEnd = !!rangeEnd2 && isSameDay2(d, rangeEnd2);
2098
2098
  const between = inRange(d);
2099
2099
  const dis = isDisabledDay(d);
2100
2100
  return /* @__PURE__ */ jsx(
@@ -2325,18 +2325,21 @@ function Field({ label, children, hint, error, required, labelAction, id: idProp
2325
2325
  const { error: errorFlag, ...ariaOnly } = controlProps;
2326
2326
  control = cloneElement(child, isNative ? ariaOnly : { ...ariaOnly, ...errorFlag ? { error: true } : {} });
2327
2327
  }
2328
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-ds-1.5", className), children: [
2329
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-2", children: [
2330
- /* @__PURE__ */ jsxs(Label, { id: labelId, htmlFor: id, children: [
2331
- label,
2332
- required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-ds-0.5 text-[var(--ds-status-error)]", children: "*" })
2328
+ return (
2329
+ // In a settings panel a field keeps the narrow reading width however wide the page is (--ds-form-max, Sprint 25.9 A4)
2330
+ /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-ds-1.5 max-w-[var(--ds-form-max,none)]", className), children: [
2331
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-2", children: [
2332
+ /* @__PURE__ */ jsxs(Label, { id: labelId, htmlFor: id, children: [
2333
+ label,
2334
+ required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-ds-0.5 text-[var(--ds-status-error)]", children: "*" })
2335
+ ] }),
2336
+ labelAction
2333
2337
  ] }),
2334
- labelAction
2335
- ] }),
2336
- /* @__PURE__ */ jsx(FieldContext.Provider, { value: fieldContext, children: control }),
2337
- hint && /* @__PURE__ */ jsx("p", { id: hintId, className: "text-xs text-[var(--ds-text-muted)]", children: hint }),
2338
- hasError && /* @__PURE__ */ jsx(InlineFieldError, { id: errorId, message: error, className: "mt-0" })
2339
- ] });
2338
+ /* @__PURE__ */ jsx(FieldContext.Provider, { value: fieldContext, children: control }),
2339
+ hint && /* @__PURE__ */ jsx("p", { id: hintId, className: "text-xs text-[var(--ds-text-muted)]", children: hint }),
2340
+ hasError && /* @__PURE__ */ jsx(InlineFieldError, { id: errorId, message: error, className: "mt-0" })
2341
+ ] })
2342
+ );
2340
2343
  }
2341
2344
  function TruncFileName({ children, className }) {
2342
2345
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
@@ -2506,7 +2509,8 @@ function SliderRange({
2506
2509
  label,
2507
2510
  "aria-label": ariaLabel,
2508
2511
  "aria-describedby": describedByProp,
2509
- trackStyle
2512
+ trackStyle,
2513
+ formatValue
2510
2514
  }) {
2511
2515
  const field = useField();
2512
2516
  const ownId = useId();
@@ -2519,6 +2523,8 @@ function SliderRange({
2519
2523
  const high = isRange ? value[1] : value;
2520
2524
  const trackRef = useRef(null);
2521
2525
  const pct = (v) => (v - min) / (max - min) * 100;
2526
+ const shown = (v) => formatValue ? formatValue(v) : v;
2527
+ const valueText = (v) => formatValue ? formatValue(v) : void 0;
2522
2528
  const handleChange = useCallback(
2523
2529
  (which, raw) => {
2524
2530
  const v = Math.round(raw / step) * step;
@@ -2571,7 +2577,7 @@ function SliderRange({
2571
2577
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("space-y-ds-1", disabled && "opacity-disabled", className), children: [
2572
2578
  (showLabel || showValue) && /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-ds-2", showLabel ? "justify-between" : "justify-end"), children: [
2573
2579
  showLabel && /* @__PURE__ */ jsx("span", { id: `${ownId}-label`, className: "text-sm font-medium leading-none text-[var(--ds-text-secondary)]", children: label }),
2574
- showValue && /* @__PURE__ */ jsx("span", { className: "text-xs font-mono tabular-nums text-[var(--ds-text-muted)]", children: isRange ? `${low} \u2013 ${high}` : low })
2580
+ showValue && /* @__PURE__ */ jsx("span", { className: "text-xs font-mono tabular-nums text-[var(--ds-text-muted)]", children: isRange ? `${shown(low)} \u2013 ${shown(high)}` : shown(low) })
2575
2581
  ] }),
2576
2582
  isRange && nameId && /* @__PURE__ */ jsxs(Fragment, { children: [
2577
2583
  /* @__PURE__ */ jsx("span", { id: `${ownId}-minimum`, hidden: true, children: "minimum" }),
@@ -2604,6 +2610,7 @@ function SliderRange({
2604
2610
  "aria-valuemin": min,
2605
2611
  "aria-valuemax": high,
2606
2612
  "aria-valuenow": low,
2613
+ "aria-valuetext": valueText(low),
2607
2614
  "aria-invalid": error || void 0,
2608
2615
  onKeyDown: handleKeyDown("low"),
2609
2616
  onPointerDown: handlePointerDown("low"),
@@ -2629,6 +2636,7 @@ function SliderRange({
2629
2636
  "aria-valuemin": isRange ? low : min,
2630
2637
  "aria-valuemax": max,
2631
2638
  "aria-valuenow": high,
2639
+ "aria-valuetext": valueText(high),
2632
2640
  "aria-invalid": error || void 0,
2633
2641
  onKeyDown: handleKeyDown("high"),
2634
2642
  onPointerDown: handlePointerDown("high"),
@@ -4302,7 +4310,7 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4302
4310
  f.message
4303
4311
  ] }, f.id || f.label)) }) }) : null;
4304
4312
  const actionsNode = actions && !drawerFooter ? /* @__PURE__ */ jsx("div", { "data-form-actions": "", className: cn("flex flex-wrap items-center gap-ds-2 pt-ds-2", actionsAlign === "end" ? "justify-end" : "justify-start"), children: actions }) : null;
4305
- return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted }, children: /* @__PURE__ */ jsxs("form", { ref: setRefs, id: drawerFooter ? formId : void 0, onSubmit: handleSubmit, onFocusCapture: userActed, onInputCapture: userActed, className: cn("flex flex-col gap-ds-5", className), noValidate: true, children: [
4313
+ return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted }, children: /* @__PURE__ */ jsxs("form", { ref: setRefs, id: drawerFooter ? formId : void 0, onSubmit: handleSubmit, onFocusCapture: userActed, onInputCapture: userActed, className: cn("flex flex-col gap-ds-5 max-w-[var(--ds-form-max,none)]", className), noValidate: true, children: [
4306
4314
  summaryNode && /* @__PURE__ */ jsx("div", { "data-form-error-summary": "", children: summaryNode }),
4307
4315
  children,
4308
4316
  actionsNode
@@ -5381,11 +5389,27 @@ var columnClasses = {
5381
5389
  3: "grid-cols-3",
5382
5390
  4: "grid-cols-4"
5383
5391
  };
5392
+ var autoColumnClasses = {
5393
+ horizontal: {
5394
+ 2: "grid-cols-[repeat(auto-fill,minmax(max(min(20rem,100%),calc((100%_-_var(--ds-space-8))/2)),1fr))]",
5395
+ 3: "grid-cols-[repeat(auto-fill,minmax(max(min(20rem,100%),calc((100%_-_2*var(--ds-space-8))/3)),1fr))]",
5396
+ 4: "grid-cols-[repeat(auto-fill,minmax(max(min(20rem,100%),calc((100%_-_3*var(--ds-space-8))/4)),1fr))]"
5397
+ },
5398
+ vertical: {
5399
+ 2: "grid-cols-[repeat(auto-fill,minmax(max(min(8rem,100%),calc((100%_-_var(--ds-space-6))/2)),1fr))]",
5400
+ 3: "grid-cols-[repeat(auto-fill,minmax(max(min(8rem,100%),calc((100%_-_2*var(--ds-space-6))/3)),1fr))]",
5401
+ 4: "grid-cols-[repeat(auto-fill,minmax(max(min(8rem,100%),calc((100%_-_3*var(--ds-space-6))/4)),1fr))]"
5402
+ }
5403
+ };
5384
5404
  var spanClasses = {
5385
5405
  2: "col-span-2",
5386
5406
  3: "col-span-3",
5387
5407
  4: "col-span-4"
5388
5408
  };
5409
+ function spanClass(span, columns) {
5410
+ if (!span || span <= 1) return void 0;
5411
+ return span >= columns ? "col-span-full" : spanClasses[span];
5412
+ }
5389
5413
  var stackBreakpoints = {
5390
5414
  sm: "max-sm:grid-cols-1",
5391
5415
  md: "max-md:grid-cols-1",
@@ -5402,6 +5426,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5402
5426
  }, ref) {
5403
5427
  const s = sizeStyles3[size];
5404
5428
  const stackBelow = stackBelowProp ?? (columns > 1 ? "sm" : void 0);
5429
+ const templateClass = (layout) => columns > 1 && stackBelow !== "none" ? autoColumnClasses[layout][columns] : columnClasses[columns];
5405
5430
  if (orientation === "vertical") {
5406
5431
  return /* @__PURE__ */ jsx(
5407
5432
  "dl",
@@ -5409,7 +5434,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5409
5434
  ref,
5410
5435
  className: cn(
5411
5436
  "grid",
5412
- columnClasses[columns],
5437
+ templateClass("vertical"),
5413
5438
  "gap-x-ds-6 gap-y-ds-1",
5414
5439
  stackBelow && stackBelow !== "none" && stackBreakpoints[stackBelow],
5415
5440
  dividers && "divide-y-[length:var(--ds-border-width)] divide-[var(--ds-border-subtle)]",
@@ -5420,7 +5445,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5420
5445
  {
5421
5446
  className: cn(
5422
5447
  s.py,
5423
- item.span && item.span > 1 && spanClasses[item.span]
5448
+ spanClass(item.span, columns)
5424
5449
  ),
5425
5450
  children: [
5426
5451
  /* @__PURE__ */ jsxs(
@@ -5451,7 +5476,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5451
5476
  ref,
5452
5477
  className: cn(
5453
5478
  "grid",
5454
- columnClasses[columns],
5479
+ templateClass("horizontal"),
5455
5480
  "gap-x-ds-8",
5456
5481
  stackBelow && stackBelow !== "none" && stackBreakpoints[stackBelow],
5457
5482
  dividers && "divide-y-[length:var(--ds-border-width)] divide-[var(--ds-border-subtle)]",
@@ -5464,7 +5489,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5464
5489
  "flex items-start justify-between",
5465
5490
  s.py,
5466
5491
  s.gap,
5467
- item.span && item.span > 1 && spanClasses[item.span]
5492
+ spanClass(item.span, columns)
5468
5493
  ),
5469
5494
  children: [
5470
5495
  /* @__PURE__ */ jsxs(
@@ -5581,6 +5606,22 @@ function TruncCell({ children, lines }) {
5581
5606
  if (!isTruncated) return cell;
5582
5607
  return /* @__PURE__ */ jsx(Tooltip, { content: children, children: cell });
5583
5608
  }
5609
+ var TITLE_CLASSES = "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] break-words";
5610
+ var FRAME_CLASSES = cn(
5611
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
5612
+ "rounded-[var(--ds-radius-md)]",
5613
+ GLASS_SURFACE,
5614
+ "bg-[var(--ds-bg-surface)]"
5615
+ );
5616
+ function TableHeading({ title, description, actions, titleId, descriptionId, inset }) {
5617
+ return /* @__PURE__ */ jsxs("div", { "data-table-heading": "", className: cn("flex flex-wrap items-start justify-between gap-ds-4", inset ? "px-ds-5 py-ds-4" : "pb-ds-4"), children: [
5618
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-ds-1.5 grow basis-1/2 [min-width:min-content] break-words", children: [
5619
+ /* @__PURE__ */ jsx("h3", { id: titleId, className: TITLE_CLASSES, children: title }),
5620
+ description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "text-sm text-[var(--ds-text-muted)]", children: description })
5621
+ ] }),
5622
+ actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0 ml-auto", children: actions })
5623
+ ] });
5624
+ }
5584
5625
  function Table({
5585
5626
  columns,
5586
5627
  data,
@@ -5601,8 +5642,14 @@ function Table({
5601
5642
  emptyState,
5602
5643
  onRowClick,
5603
5644
  mobileBreakpoint: mobileBreakpointProp,
5645
+ title,
5646
+ description,
5647
+ actions,
5604
5648
  ref
5605
5649
  }) {
5650
+ const headingId = useId();
5651
+ const titleId = `${headingId}-title`;
5652
+ const descriptionId = `${headingId}-description`;
5606
5653
  const [internalSortKey, setInternalSortKey] = useState(null);
5607
5654
  const [internalSortDir, setInternalSortDir] = useState(null);
5608
5655
  const [expandedRows, setExpandedRows] = useState(/* @__PURE__ */ new Set());
@@ -5669,14 +5716,21 @@ function Table({
5669
5716
  const preferences = useShellPreferences();
5670
5717
  const mobileBreakpoint = mobileBreakpointProp ?? preferences.tableToCardsBreakpoint ?? "md";
5671
5718
  const isMobile = useBreakpoint(mobileBreakpoint);
5719
+ const cardHeading = title ? /* @__PURE__ */ jsx(TableHeading, { title, description, actions, titleId, descriptionId, inset: false }) : null;
5720
+ const titledCards = (body) => /* @__PURE__ */ jsxs("div", { ref, "data-ds-list": "", "data-gs": "table", className: cn("flex flex-col", className), children: [
5721
+ cardHeading,
5722
+ body
5723
+ ] });
5672
5724
  if (isMobile) {
5673
5725
  if (loading) {
5674
- return /* @__PURE__ */ jsx("div", { ref, "data-ds-list": "", className: cn("flex items-center justify-center py-ds-10", className), children: /* @__PURE__ */ jsx(Spinner, { label: "Loading table data" }) });
5726
+ if (title) return titledCards(/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-ds-10", children: /* @__PURE__ */ jsx(Spinner, { label: "Loading table data" }) }));
5727
+ return /* @__PURE__ */ jsx("div", { ref, "data-ds-list": "", "data-gs": "table", className: cn("flex items-center justify-center py-ds-10", className), children: /* @__PURE__ */ jsx(Spinner, { label: "Loading table data" }) });
5675
5728
  }
5676
5729
  if (data.length === 0 && emptyState) {
5677
- return /* @__PURE__ */ jsx("div", { ref, "data-ds-list": "", className: cn("py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", className), children: emptyState });
5730
+ if (title) return titledCards(/* @__PURE__ */ jsx("div", { className: "py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", children: emptyState }));
5731
+ return /* @__PURE__ */ jsx("div", { ref, "data-ds-list": "", "data-gs": "table", className: cn("py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", className), children: emptyState });
5678
5732
  }
5679
- return /* @__PURE__ */ jsx(TableCardCtx, { value: true, children: /* @__PURE__ */ jsx("div", { ref, "data-ds-list": "", className: cn("flex flex-col gap-ds-3 overflow-y-auto", className), children: sortedWithIndices.map(({ row, originalIdx }, i) => {
5733
+ const cards = /* @__PURE__ */ jsx(TableCardCtx, { value: true, children: /* @__PURE__ */ jsx("div", { ref: title ? void 0 : ref, "data-ds-list": title ? void 0 : "", "data-gs": title ? void 0 : "table", className: cn("flex flex-col gap-ds-3 overflow-y-auto", title ? "min-h-0" : className), children: sortedWithIndices.map(({ row, originalIdx }, i) => {
5680
5734
  const isSelected = selectedSet.has(originalIdx);
5681
5735
  return /* @__PURE__ */ jsxs(
5682
5736
  "div",
@@ -5710,173 +5764,179 @@ function Table({
5710
5764
  i
5711
5765
  );
5712
5766
  }) }) });
5767
+ return title ? titledCards(cards) : cards;
5713
5768
  }
5714
- return /* @__PURE__ */ jsx(
5715
- "div",
5716
- {
5717
- ref,
5718
- "data-ds-list": "",
5719
- className: cn(
5720
- "w-full overflow-x-auto",
5721
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
5722
- "rounded-[var(--ds-radius-md)]",
5723
- GLASS_SURFACE,
5724
- "bg-[var(--ds-bg-surface)]",
5725
- className
5726
- ),
5727
- children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
5728
- /* @__PURE__ */ jsx("thead", { className: cn("bg-[var(--ds-bg-muted)]", stickyHeader && "sticky top-0 z-10"), children: /* @__PURE__ */ jsxs("tr", { className: "border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", children: [
5729
- selectable && /* @__PURE__ */ jsx("th", { className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3"), children: /* @__PURE__ */ jsx(
5730
- Checkbox,
5731
- {
5732
- checked: allSelected,
5733
- indeterminate: someSelected,
5734
- onChange: toggleAll
5735
- }
5736
- ) }),
5737
- expandable && /* @__PURE__ */ jsx("th", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3") }),
5738
- columns.map((col) => {
5739
- const isSortable = sortable || col.sortable;
5740
- const isActiveSort = sortKey === col.key;
5741
- const headerKind = kindOf(col);
5742
- const headerAlign = col.align ?? (headerKind === "number" ? "right" : "left");
5743
- const shrink = (headerKind === "number" || headerKind === "date") && !col.maxWidth;
5744
- return /* @__PURE__ */ jsx(
5745
- "th",
5746
- {
5747
- className: cn(
5748
- density === "compact" ? "px-ds-3 py-ds-2" : "px-ds-4 py-ds-3",
5749
- shrink && "w-px whitespace-nowrap",
5750
- "font-[var(--ds-weight-heading)] text-[var(--ds-text-secondary)]",
5751
- "text-xs uppercase tracking-wider",
5752
- headerAlign === "right" ? "text-right" : headerAlign === "center" ? "text-center" : "text-left",
5753
- isSortable && "cursor-pointer select-none hover:text-[var(--ds-text-primary)]"
5754
- ),
5755
- style: col.maxWidth || col.width || col.minWidth ? { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth } : void 0,
5756
- onClick: isSortable ? () => handleSort(col.key) : void 0,
5757
- children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-ds-1", children: [
5758
- col.header,
5759
- isSortable && isActiveSort && /* @__PURE__ */ jsx(
5769
+ const table = /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", ...title ? { "aria-labelledby": titleId, "aria-describedby": description ? descriptionId : void 0 } : {}, children: [
5770
+ /* @__PURE__ */ jsx("thead", { className: cn("bg-[var(--ds-bg-muted)]", stickyHeader && "sticky top-0 z-10"), children: /* @__PURE__ */ jsxs("tr", { className: "border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", children: [
5771
+ selectable && /* @__PURE__ */ jsx("th", { className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3"), children: /* @__PURE__ */ jsx(
5772
+ Checkbox,
5773
+ {
5774
+ checked: allSelected,
5775
+ indeterminate: someSelected,
5776
+ onChange: toggleAll
5777
+ }
5778
+ ) }),
5779
+ expandable && /* @__PURE__ */ jsx("th", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3") }),
5780
+ columns.map((col) => {
5781
+ const isSortable = sortable || col.sortable;
5782
+ const isActiveSort = sortKey === col.key;
5783
+ const headerKind = kindOf(col);
5784
+ const headerAlign = col.align ?? (headerKind === "number" ? "right" : "left");
5785
+ const shrink = (headerKind === "number" || headerKind === "date") && !col.maxWidth;
5786
+ return /* @__PURE__ */ jsx(
5787
+ "th",
5788
+ {
5789
+ className: cn(
5790
+ density === "compact" ? "px-ds-3 py-ds-2" : "px-ds-4 py-ds-3",
5791
+ shrink && "w-px whitespace-nowrap",
5792
+ "font-[var(--ds-weight-heading)] text-[var(--ds-text-secondary)]",
5793
+ "text-xs uppercase tracking-wider",
5794
+ headerAlign === "right" ? "text-right" : headerAlign === "center" ? "text-center" : "text-left",
5795
+ isSortable && "cursor-pointer select-none hover:text-[var(--ds-text-primary)]"
5796
+ ),
5797
+ style: col.maxWidth || col.width || col.minWidth ? { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth } : void 0,
5798
+ onClick: isSortable ? () => handleSort(col.key) : void 0,
5799
+ children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-ds-1", children: [
5800
+ col.header,
5801
+ isSortable && isActiveSort && /* @__PURE__ */ jsx(
5802
+ Icon,
5803
+ {
5804
+ name: "chevron-right",
5805
+ size: 12,
5806
+ className: cn(
5807
+ "transition-transform",
5808
+ sortDir === "asc" ? "-rotate-90" : "rotate-90"
5809
+ )
5810
+ }
5811
+ )
5812
+ ] })
5813
+ },
5814
+ col.key
5815
+ );
5816
+ })
5817
+ ] }) }),
5818
+ /* @__PURE__ */ jsx("tbody", { children: loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5819
+ "td",
5820
+ {
5821
+ colSpan: columns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
5822
+ className: "px-ds-4 py-ds-10 text-center",
5823
+ children: /* @__PURE__ */ jsx(Spinner, { label: "Loading table data" })
5824
+ }
5825
+ ) }) : data.length === 0 && emptyState ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5826
+ "td",
5827
+ {
5828
+ colSpan: columns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
5829
+ className: "px-ds-4 py-ds-10 text-center text-sm text-[var(--ds-text-muted)]",
5830
+ children: emptyState
5831
+ }
5832
+ ) }) : sortedWithIndices.map(({ row, originalIdx }, i) => {
5833
+ const isSelected = selectedSet.has(originalIdx);
5834
+ const isExpanded = expandedRows.has(i);
5835
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
5836
+ /* @__PURE__ */ jsxs(
5837
+ "tr",
5838
+ {
5839
+ className: cn(
5840
+ "border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] last:border-b-0",
5841
+ "transition-colors",
5842
+ isSelected ? "bg-[var(--ds-accent-subtle)]" : striped && i % 2 === 1 ? "bg-[var(--ds-bg-muted)]/50" : "hover:bg-[var(--ds-bg-muted)]",
5843
+ onRowClick && "cursor-pointer focus-visible:outline-none focus-visible:shadow-[inset_var(--ds-shadow-focus)]"
5844
+ ),
5845
+ onClick: onRowClick ? () => onRowClick(row, originalIdx) : void 0,
5846
+ tabIndex: onRowClick ? 0 : void 0,
5847
+ onKeyDown: onRowClick ? (e) => {
5848
+ if (e.target !== e.currentTarget) return;
5849
+ if (e.key === "Enter" || e.key === " ") {
5850
+ e.preventDefault();
5851
+ onRowClick(row, originalIdx);
5852
+ }
5853
+ } : void 0,
5854
+ children: [
5855
+ selectable && /* @__PURE__ */ jsx("td", { className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3"), children: /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: () => toggleRow(originalIdx) }) }),
5856
+ expandable && /* @__PURE__ */ jsx("td", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3"), children: /* @__PURE__ */ jsx(
5857
+ "button",
5858
+ {
5859
+ onClick: () => toggleExpand(i),
5860
+ className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] cursor-pointer",
5861
+ "aria-expanded": isExpanded,
5862
+ "aria-label": "Expand row",
5863
+ children: /* @__PURE__ */ jsx(
5760
5864
  Icon,
5761
5865
  {
5762
5866
  name: "chevron-right",
5763
- size: 12,
5764
- className: cn(
5765
- "transition-transform",
5766
- sortDir === "asc" ? "-rotate-90" : "rotate-90"
5767
- )
5867
+ size: 14,
5868
+ className: cn("transition-transform", isExpanded && "rotate-90")
5768
5869
  }
5769
5870
  )
5770
- ] })
5771
- },
5772
- col.key
5773
- );
5774
- })
5775
- ] }) }),
5776
- /* @__PURE__ */ jsx("tbody", { children: loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5777
- "td",
5778
- {
5779
- colSpan: columns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
5780
- className: "px-ds-4 py-ds-10 text-center",
5781
- children: /* @__PURE__ */ jsx(Spinner, { label: "Loading table data" })
5871
+ }
5872
+ ) }),
5873
+ columns.map((col) => {
5874
+ const truncLines = col.truncate === true ? 1 : col.truncate ? col.truncate.lines : 0;
5875
+ const kind = kindOf(col);
5876
+ const alignClass = col.align ? col.align === "right" ? "text-right" : col.align === "center" ? "text-center" : "text-left" : kind === "number" ? "text-right" : "text-left";
5877
+ const cellContent = row[col.key];
5878
+ const shrink = (kind === "number" || kind === "date") && !col.maxWidth;
5879
+ let inner;
5880
+ if (truncLines > 0) {
5881
+ inner = /* @__PURE__ */ jsx(TruncCell, { lines: truncLines, children: cellContent });
5882
+ } else if (kind === "email") {
5883
+ inner = /* @__PURE__ */ jsx("span", { className: "max-w-[40ch] truncate inline-block align-bottom", title: typeof cellContent === "string" ? cellContent : void 0, children: cellContent });
5884
+ } else {
5885
+ inner = cellContent;
5886
+ }
5887
+ const align = col.align ?? (kind === "number" ? "right" : "left");
5888
+ if (align !== "left" && cellContent !== null && cellContent !== void 0 && typeof cellContent !== "string" && typeof cellContent !== "number") {
5889
+ inner = /* @__PURE__ */ jsx("div", { className: cn("flex items-center", align === "center" ? "justify-center" : "justify-end"), children: inner });
5890
+ }
5891
+ return /* @__PURE__ */ jsx(
5892
+ "td",
5893
+ {
5894
+ className: cn(
5895
+ density === "compact" ? "px-ds-3 py-ds-2" : "px-ds-4 py-ds-3",
5896
+ "text-[var(--ds-text-primary)]",
5897
+ alignClass,
5898
+ shrink && "w-px whitespace-nowrap"
5899
+ ),
5900
+ style: col.maxWidth || col.width || col.minWidth ? { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth } : void 0,
5901
+ children: inner
5902
+ },
5903
+ col.key
5904
+ );
5905
+ })
5906
+ ]
5782
5907
  }
5783
- ) }) : data.length === 0 && emptyState ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5908
+ ),
5909
+ expandable && isExpanded && renderExpanded && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5784
5910
  "td",
5785
5911
  {
5786
- colSpan: columns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
5787
- className: "px-ds-4 py-ds-10 text-center text-sm text-[var(--ds-text-muted)]",
5788
- children: emptyState
5912
+ colSpan: columns.length + (selectable ? 1 : 0) + 1,
5913
+ className: "bg-[var(--ds-bg-muted)] px-ds-6 py-ds-4 text-sm text-[var(--ds-text-primary)]",
5914
+ children: renderExpanded(row, i)
5789
5915
  }
5790
- ) }) : sortedWithIndices.map(({ row, originalIdx }, i) => {
5791
- const isSelected = selectedSet.has(originalIdx);
5792
- const isExpanded = expandedRows.has(i);
5793
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
5794
- /* @__PURE__ */ jsxs(
5795
- "tr",
5796
- {
5797
- className: cn(
5798
- "border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] last:border-b-0",
5799
- "transition-colors",
5800
- isSelected ? "bg-[var(--ds-accent-subtle)]" : striped && i % 2 === 1 ? "bg-[var(--ds-bg-muted)]/50" : "hover:bg-[var(--ds-bg-muted)]",
5801
- onRowClick && "cursor-pointer focus-visible:outline-none focus-visible:shadow-[inset_var(--ds-shadow-focus)]"
5802
- ),
5803
- onClick: onRowClick ? () => onRowClick(row, originalIdx) : void 0,
5804
- tabIndex: onRowClick ? 0 : void 0,
5805
- onKeyDown: onRowClick ? (e) => {
5806
- if (e.target !== e.currentTarget) return;
5807
- if (e.key === "Enter" || e.key === " ") {
5808
- e.preventDefault();
5809
- onRowClick(row, originalIdx);
5810
- }
5811
- } : void 0,
5812
- children: [
5813
- selectable && /* @__PURE__ */ jsx("td", { className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3"), children: /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: () => toggleRow(originalIdx) }) }),
5814
- expandable && /* @__PURE__ */ jsx("td", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3"), children: /* @__PURE__ */ jsx(
5815
- "button",
5816
- {
5817
- onClick: () => toggleExpand(i),
5818
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] cursor-pointer",
5819
- "aria-expanded": isExpanded,
5820
- "aria-label": "Expand row",
5821
- children: /* @__PURE__ */ jsx(
5822
- Icon,
5823
- {
5824
- name: "chevron-right",
5825
- size: 14,
5826
- className: cn("transition-transform", isExpanded && "rotate-90")
5827
- }
5828
- )
5829
- }
5830
- ) }),
5831
- columns.map((col) => {
5832
- const truncLines = col.truncate === true ? 1 : col.truncate ? col.truncate.lines : 0;
5833
- const kind = kindOf(col);
5834
- const alignClass = col.align ? col.align === "right" ? "text-right" : col.align === "center" ? "text-center" : "text-left" : kind === "number" ? "text-right" : "text-left";
5835
- const cellContent = row[col.key];
5836
- const shrink = (kind === "number" || kind === "date") && !col.maxWidth;
5837
- let inner;
5838
- if (truncLines > 0) {
5839
- inner = /* @__PURE__ */ jsx(TruncCell, { lines: truncLines, children: cellContent });
5840
- } else if (kind === "email") {
5841
- inner = /* @__PURE__ */ jsx("span", { className: "max-w-[40ch] truncate inline-block align-bottom", title: typeof cellContent === "string" ? cellContent : void 0, children: cellContent });
5842
- } else {
5843
- inner = cellContent;
5844
- }
5845
- const align = col.align ?? (kind === "number" ? "right" : "left");
5846
- if (align !== "left" && cellContent !== null && cellContent !== void 0 && typeof cellContent !== "string" && typeof cellContent !== "number") {
5847
- inner = /* @__PURE__ */ jsx("div", { className: cn("flex items-center", align === "center" ? "justify-center" : "justify-end"), children: inner });
5848
- }
5849
- return /* @__PURE__ */ jsx(
5850
- "td",
5851
- {
5852
- className: cn(
5853
- density === "compact" ? "px-ds-3 py-ds-2" : "px-ds-4 py-ds-3",
5854
- "text-[var(--ds-text-primary)]",
5855
- alignClass,
5856
- shrink && "w-px whitespace-nowrap"
5857
- ),
5858
- style: col.maxWidth || col.width || col.minWidth ? { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth } : void 0,
5859
- children: inner
5860
- },
5861
- col.key
5862
- );
5863
- })
5864
- ]
5865
- }
5866
- ),
5867
- expandable && isExpanded && renderExpanded && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5868
- "td",
5869
- {
5870
- colSpan: columns.length + (selectable ? 1 : 0) + 1,
5871
- className: "bg-[var(--ds-bg-muted)] px-ds-6 py-ds-4 text-sm text-[var(--ds-text-primary)]",
5872
- children: renderExpanded(row, i)
5873
- }
5874
- ) })
5875
- ] }, i);
5876
- }) })
5877
- ] })
5878
- }
5879
- );
5916
+ ) })
5917
+ ] }, i);
5918
+ }) })
5919
+ ] });
5920
+ if (!title) {
5921
+ return /* @__PURE__ */ jsx(
5922
+ "div",
5923
+ {
5924
+ ref,
5925
+ "data-ds-list": "",
5926
+ "data-gs": "table",
5927
+ className: cn(
5928
+ "w-full overflow-x-auto",
5929
+ FRAME_CLASSES,
5930
+ className
5931
+ ),
5932
+ children: table
5933
+ }
5934
+ );
5935
+ }
5936
+ return /* @__PURE__ */ jsxs("div", { ref, "data-ds-list": "", "data-gs": "table", className: cn("w-full flex flex-col", FRAME_CLASSES, className), children: [
5937
+ /* @__PURE__ */ jsx(TableHeading, { title, description, actions, titleId, descriptionId, inset: true }),
5938
+ /* @__PURE__ */ jsx("div", { "data-table-scroll": "", className: "min-h-0 overflow-x-auto rounded-b-[inherit]", children: table })
5939
+ ] });
5880
5940
  }
5881
5941
  function optionsKeyForFit(options) {
5882
5942
  return options.map((o) => `${o.value}:${o.label}`).join("|");
@@ -8386,6 +8446,7 @@ function Carousel({
8386
8446
  "div",
8387
8447
  {
8388
8448
  ref,
8449
+ "data-gs-clip": "",
8389
8450
  className: cn(
8390
8451
  "relative overflow-hidden rounded-[var(--ds-radius-md)]",
8391
8452
  "bg-[var(--ds-bg-muted)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
@@ -9329,12 +9390,19 @@ var DAY_MS = 864e5;
9329
9390
  var DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
9330
9391
  var utcDay = (y, m, d) => Math.round(Date.UTC(y, m, d) / DAY_MS);
9331
9392
  function dayOfDate(d) {
9332
- if (Number.isNaN(d.getTime())) return NaN;
9393
+ const t = d.getTime();
9394
+ if (Number.isNaN(t)) return NaN;
9395
+ const day = utcDay(d.getFullYear(), d.getMonth(), d.getDate());
9396
+ const first = new Date(t);
9397
+ first.setHours(0, 0, 0, 0);
9398
+ if (first.getTime() === t) return day;
9333
9399
  const ms = ((d.getHours() * 60 + d.getMinutes()) * 60 + d.getSeconds()) * 1e3 + d.getMilliseconds();
9334
- return utcDay(d.getFullYear(), d.getMonth(), d.getDate()) + ms / DAY_MS;
9400
+ return day + ms / DAY_MS;
9335
9401
  }
9336
9402
  function dayOf(value) {
9403
+ if (typeof value === "number") return Number.isFinite(value) ? value : NaN;
9337
9404
  if (value instanceof Date) return dayOfDate(value);
9405
+ if (typeof value !== "string") return NaN;
9338
9406
  const m = DATE_ONLY.exec(value.trim());
9339
9407
  if (!m) return dayOfDate(new Date(value));
9340
9408
  const [y, mo, d] = [Number(m[1]), Number(m[2]) - 1, Number(m[3])];
@@ -9347,10 +9415,15 @@ function dateOfDay(day) {
9347
9415
  const u = new Date(whole * DAY_MS);
9348
9416
  return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole) * DAY_MS));
9349
9417
  }
9418
+ function isWholeDay(value) {
9419
+ return typeof value === "string" ? DATE_ONLY.test(value.trim()) : Number.isInteger(dayOf(value));
9420
+ }
9350
9421
  function endDayOf(value) {
9351
9422
  const day = dayOf(value);
9352
- const wholeDay2 = typeof value === "string" ? DATE_ONLY.test(value.trim()) : Number.isInteger(day);
9353
- return wholeDay2 ? day + 1 : day;
9423
+ return typeof value !== "number" && isWholeDay(value) ? day + 1 : day;
9424
+ }
9425
+ function rangeEnd(start, end) {
9426
+ return end instanceof Date && !isWholeDay(start) ? dayOf(end) : endDayOf(end);
9354
9427
  }
9355
9428
  var MINOR = { day: "day", week: "week", month: "month", quarter: "quarter" };
9356
9429
  var MAJOR = { day: "month", week: "month", month: "year", quarter: "year" };
@@ -9408,26 +9481,33 @@ function labelsOf(unit, role, day, locale) {
9408
9481
  forms = role === "major" ? [f({ month: "long", year: "numeric" }), f({ month: "short", year: "numeric" }), f({ month: "short" }), f({ month: "narrow" })] : [f({ month: "long" }), f({ month: "short" }), f({ month: "narrow" })];
9409
9482
  } else if (unit === "quarter") forms = [`Q${Math.floor(new Date(day * DAY_MS).getUTCMonth() / 3) + 1}`];
9410
9483
  else forms = [f({ year: "numeric" }), f({ year: "2-digit" })];
9411
- return [...new Set(forms)];
9484
+ return forms;
9412
9485
  }
9413
9486
  var DENSITY = { day: 56, week: 16, month: 4, quarter: 1.5 };
9414
9487
  var todays = /* @__PURE__ */ new WeakMap();
9488
+ var awaiting = /* @__PURE__ */ new WeakSet();
9489
+ function awaitsToday(scale) {
9490
+ return awaiting.has(scale);
9491
+ }
9415
9492
  function calendarDay(day) {
9416
9493
  const d = Math.floor(day);
9417
9494
  return { day: d, weekday: weekday(d), key: keyOf("day", d) };
9418
9495
  }
9419
9496
  function ticks(unit, role, s) {
9420
9497
  const out = [];
9421
- for (let p = periodStart(unit, s.startDay, s.weekStartsOn); p < s.endDay; p = periodAfter(unit, p)) {
9498
+ for (let p = periodStart(unit, s.startDay, s.weekStartsOn); p < s.endDay; ) {
9499
+ const next = periodAfter(unit, p);
9422
9500
  const from = Math.max(p, s.startDay);
9423
- const to = Math.min(periodAfter(unit, p), s.endDay);
9501
+ const to = Math.min(next, s.endDay);
9424
9502
  out.push({ key: keyOf(unit, p), day: from, x: (from - s.startDay) * s.pxPerDay, width: (to - from) * s.pxPerDay, weekday: weekday(from), labels: labelsOf(unit, role, p, s.locale) });
9503
+ if (!(next > p)) break;
9504
+ p = next;
9425
9505
  }
9426
9506
  return out;
9427
9507
  }
9428
9508
  function build(s) {
9429
9509
  const { startDay, pxPerDay } = s;
9430
- const x = (value) => (startIndex(value) - startDay) * pxPerDay;
9510
+ const x = (value) => (dayOf(value) - startDay) * pxPerDay;
9431
9511
  const inRange = (d) => d >= startDay && d < s.endDay;
9432
9512
  const todayDay = Math.floor(s.todayValue);
9433
9513
  const scale = {
@@ -9437,9 +9517,9 @@ function build(s) {
9437
9517
  pxPerDay,
9438
9518
  width: (s.endDay - startDay) * pxPerDay,
9439
9519
  x,
9440
- span: (start, end = start) => {
9520
+ span: (start, end) => {
9441
9521
  const x0 = x(start);
9442
- return { x: x0, width: Math.max(0, (endIndex(end) - startDay) * pxPerDay - x0) };
9522
+ return { x: x0, width: Math.max(0, (rangeEnd(start, end ?? start) - startDay) * pxPerDay - x0) };
9443
9523
  },
9444
9524
  dayAt: (px) => startDay + px / pxPerDay,
9445
9525
  major: ticks(MAJOR[s.zoom], "major", s),
@@ -9452,9 +9532,7 @@ function build(s) {
9452
9532
  todays.set(scale, s.todayValue);
9453
9533
  return scale;
9454
9534
  }
9455
- var toMinute = (day) => Math.round(day * 1440) / 1440;
9456
- var startIndex = (value) => typeof value === "number" ? value : dayOf(value);
9457
- var endIndex = (value) => typeof value === "number" ? value : endDayOf(value);
9535
+ var toMinute = (day) => Math.floor(day * 1440 + 1e-6) / 1440;
9458
9536
  function scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, pxPerDay, pad3) {
9459
9537
  const fallback = Number.isFinite(todayValue) ? Math.floor(todayValue) : 0;
9460
9538
  const a = Number.isFinite(from) ? from : fallback;
@@ -9470,28 +9548,44 @@ function scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, pxPerDay, p
9470
9548
  }
9471
9549
  return build({ zoom, startDay, endDay, pxPerDay: pxPerDay > 0 ? pxPerDay : DENSITY[zoom], todayValue, weekStartsOn, locale });
9472
9550
  }
9473
- function extendTimeScale(scale, width) {
9474
- if (!(width > scale.width) || !(scale.pxPerDay > 0)) return scale;
9551
+ function extendedEnd(scale, width) {
9552
+ if (!(width > scale.width) || !(scale.pxPerDay > 0)) return scale.endDay;
9475
9553
  const unit = MINOR[scale.zoom];
9476
9554
  const last = scale.startDay + Math.ceil(width / scale.pxPerDay) - 1;
9477
- const endDay = periodAfter(unit, periodStart(unit, last, scale.weekStartsOn));
9555
+ return periodAfter(unit, periodStart(unit, last, scale.weekStartsOn));
9556
+ }
9557
+ function extendTimeScaleTo(scale, endDay) {
9558
+ if (!(endDay > scale.endDay)) return scale;
9478
9559
  const todayValue = todays.get(scale) ?? scale.now ?? scale.today ?? NaN;
9479
- return build({ zoom: scale.zoom, startDay: scale.startDay, endDay, pxPerDay: scale.pxPerDay, todayValue, weekStartsOn: scale.weekStartsOn, locale: scale.locale });
9560
+ const extended = build({ zoom: scale.zoom, startDay: scale.startDay, endDay, pxPerDay: scale.pxPerDay, todayValue, weekStartsOn: scale.weekStartsOn, locale: scale.locale });
9561
+ if (awaiting.has(scale)) awaiting.add(extended);
9562
+ return extended;
9480
9563
  }
9564
+ var noSubscription = () => () => {
9565
+ };
9566
+ var inBrowser = () => true;
9567
+ var onServer = () => false;
9481
9568
  function useTimeScale({ start, end, zoom, today, weekStartsOn = 1, locale, pxPerDay, pad: pad3 = 0 }) {
9569
+ const readable = useSyncExternalStore(noSubscription, inBrowser, onServer);
9482
9570
  const [mounted] = useState(() => toMinute(dayOf(/* @__PURE__ */ new Date())));
9483
- const from = startIndex(start);
9484
- const to = endIndex(end);
9485
- const todayValue = today === void 0 ? mounted : toMinute(dayOf(today));
9571
+ const pending = today === void 0 && !readable;
9572
+ const from = dayOf(start);
9573
+ const to = rangeEnd(start, end);
9574
+ const todayValue = today !== void 0 ? toMinute(dayOf(today)) : pending ? NaN : mounted;
9486
9575
  const density = pxPerDay?.[zoom] ?? DENSITY[zoom];
9487
- return useMemo(() => scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, density, pad3), [from, to, zoom, todayValue, weekStartsOn, locale, density, pad3]);
9576
+ return useMemo(() => {
9577
+ const scale = scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, density, pad3);
9578
+ if (pending) awaiting.add(scale);
9579
+ return scale;
9580
+ }, [from, to, zoom, todayValue, weekStartsOn, locale, density, pad3, pending]);
9488
9581
  }
9489
9582
  function timeExtent(ranges) {
9490
9583
  let start = Infinity;
9491
9584
  let end = -Infinity;
9492
9585
  for (const r of ranges) {
9586
+ if (r == null) continue;
9493
9587
  const s = dayOf(r.start);
9494
- const e = endDayOf(r.end ?? r.start);
9588
+ const e = r.end == null ? endDayOf(r.start) : rangeEnd(r.start, r.end);
9495
9589
  if (Number.isFinite(s)) start = Math.min(start, s);
9496
9590
  if (Number.isFinite(e)) end = Math.max(end, e);
9497
9591
  }
@@ -9499,7 +9593,6 @@ function timeExtent(ranges) {
9499
9593
  }
9500
9594
  var DAY_WORDS = { day: "numeric", month: "long" };
9501
9595
  var TIME_WORDS = { hour: "2-digit", minute: "2-digit" };
9502
- var wholeDay = (value) => typeof value === "string" ? DATE_ONLY.test(value.trim()) : Number.isInteger(value instanceof Date ? dayOf(value) : value);
9503
9596
  var SEPARATOR = /\s*[\u2010-\u2015\u2212~\uff5e\u301c-]\s*/;
9504
9597
  var english = (f) => new Intl.Locale(f.resolvedOptions().locale).language === "en";
9505
9598
  function range(f, a, b, to) {
@@ -9518,11 +9611,13 @@ function range(f, a, b, to) {
9518
9611
  if (!joined) out.splice(lastOfStart + 1, 0, to);
9519
9612
  return out.join("");
9520
9613
  }
9521
- function formatDateRange(start, end = start, { locale, to, day = DAY_WORDS, time = TIME_WORDS } = {}) {
9522
- const from = startIndex(start);
9523
- const stop = endIndex(end);
9614
+ function formatDateRange(start, end, words) {
9615
+ const { locale, to, day = DAY_WORDS, time = TIME_WORDS } = words ?? {};
9616
+ const until = end ?? start;
9617
+ const from = dayOf(start);
9618
+ const stop = rangeEnd(start, until);
9524
9619
  if (!Number.isFinite(from) || !Number.isFinite(stop)) return "";
9525
- const timed = !wholeDay(start) || !wholeDay(end);
9620
+ const timed = !isWholeDay(start) || !isWholeDay(until);
9526
9621
  const first = timed ? from : Math.floor(from);
9527
9622
  const last = timed ? Math.max(from, stop) : Math.max(first, Math.ceil(stop) - 1);
9528
9623
  const a = new Date(first * DAY_MS);
@@ -9552,7 +9647,7 @@ function createScrollStore() {
9552
9647
  var TimeViewportCtx = createContext(null);
9553
9648
  var TimeViewportScrollCtx = createContext(null);
9554
9649
  var UNMEASURED = { scrollLeft: 0, visibleWidth: 0 };
9555
- var noSubscription = () => () => {
9650
+ var noSubscription2 = () => () => {
9556
9651
  };
9557
9652
  var unmeasured = () => UNMEASURED;
9558
9653
  function useTimeViewportLayout() {
@@ -9562,7 +9657,7 @@ function useTimeViewportLayout() {
9562
9657
  }
9563
9658
  function useTimeViewport() {
9564
9659
  const store = useContext(TimeViewportScrollCtx);
9565
- const scroll = useSyncExternalStore(store?.subscribe ?? noSubscription, store?.get ?? unmeasured, unmeasured);
9660
+ const scroll = useSyncExternalStore(store?.subscribe ?? noSubscription2, store?.get ?? unmeasured, unmeasured);
9566
9661
  return { ...useTimeViewportLayout(), ...scroll };
9567
9662
  }
9568
9663
  function fitLabels(boxes, measure, { gap = 8, align = "start", visible } = {}) {
@@ -9812,17 +9907,16 @@ var ROW_HEIGHT = 40;
9812
9907
  var MARGIN = 16;
9813
9908
  var WHEEL_STEP = 50;
9814
9909
  var WHEEL_PAUSE = 250;
9815
- var FIELD = "input, select, textarea, [contenteditable='true']";
9816
9910
  var border = "border-[var(--ds-border-subtle)]";
9817
- function writeScroll(el, visibleWidth) {
9818
- el.style.setProperty("--time-scroll", String(el.scrollLeft));
9819
- el.style.setProperty("--time-view-end", String(el.scrollLeft + visibleWidth));
9911
+ function writeScroll(rows, left, visibleWidth) {
9912
+ rows?.style.setProperty("--time-scroll", String(left));
9913
+ rows?.style.setProperty("--time-view-end", String(left + visibleWidth));
9820
9914
  }
9821
9915
  var TimeViewport = forwardRef(function TimeViewport2({
9822
9916
  scale,
9823
9917
  onZoomChange,
9824
9918
  zooms = TIME_ZOOMS,
9825
- labelWidth: labelWidthProp = 240,
9919
+ labelWidth = 240,
9826
9920
  labelHeader,
9827
9921
  header,
9828
9922
  toolbar,
@@ -9837,6 +9931,7 @@ var TimeViewport = forwardRef(function TimeViewport2({
9837
9931
  }, ref) {
9838
9932
  const scrollerRef = useRef(null);
9839
9933
  const headerRef = useRef(null);
9934
+ const rowsRef = useRef(null);
9840
9935
  const [frame, setFrame] = useState({ width: 0, header: 0 });
9841
9936
  useLayoutEffect(() => {
9842
9937
  const el = scrollerRef.current;
@@ -9854,19 +9949,22 @@ var TimeViewport = forwardRef(function TimeViewport2({
9854
9949
  return () => ro.disconnect();
9855
9950
  }, []);
9856
9951
  const phone = frame.width > 0 && frame.width < PHONE_BELOW;
9857
- const labelWidth = phone ? Math.min(labelWidthProp, Math.max(MIN_LABEL, Math.round(frame.width * 0.36))) : labelWidthProp;
9858
- const visibleWidth = Math.max(0, frame.width - labelWidth);
9859
- const drawn = useMemo(() => extendTimeScale(scale, visibleWidth), [scale, visibleWidth]);
9952
+ const pinnedWidth = phone ? Math.min(labelWidth, Math.max(MIN_LABEL, Math.round(frame.width * 0.36))) : labelWidth;
9953
+ const visibleWidth = Math.max(0, frame.width - pinnedWidth);
9954
+ const drawnEnd = extendedEnd(scale, visibleWidth);
9955
+ const drawn = useMemo(() => extendTimeScaleTo(scale, drawnEnd), [scale, drawnEnd]);
9860
9956
  const [store] = useState(createScrollStore);
9861
- const lastLeft = useRef(0);
9862
- const latest = useLatest({ drawn, visibleWidth, labelWidth, headerHeight: frame.header });
9957
+ const wantLeft = useRef(0);
9958
+ const latest = useLatest({ drawn, visibleWidth, labelWidth: pinnedWidth, headerHeight: frame.header });
9863
9959
  const setLeft = useCallback((left) => {
9864
9960
  const el = scrollerRef.current;
9865
9961
  if (!el) return;
9866
- el.scrollLeft = Math.max(0, left);
9867
- lastLeft.current = el.scrollLeft;
9868
- writeScroll(el, latest.current.visibleWidth);
9869
- store.set({ scrollLeft: el.scrollLeft, visibleWidth: latest.current.visibleWidth });
9962
+ const target = Math.max(0, left);
9963
+ el.scrollLeft = target;
9964
+ const now = el.scrollLeft;
9965
+ wantLeft.current = Math.abs(now - target) < 1 ? target : now;
9966
+ writeScroll(rowsRef.current, now, latest.current.visibleWidth);
9967
+ store.set({ scrollLeft: now, visibleWidth: latest.current.visibleWidth });
9870
9968
  }, [store, latest]);
9871
9969
  const api = useMemo(() => {
9872
9970
  const ensureVisible = (x0, x1) => {
@@ -9889,9 +9987,18 @@ var TimeViewport = forwardRef(function TimeViewport2({
9889
9987
  },
9890
9988
  page(direction) {
9891
9989
  const { drawn: s, visibleWidth: vw } = latest.current;
9892
- const target = (scrollerRef.current?.scrollLeft ?? 0) + direction * vw;
9893
- const tick = s.minor.reduce((best, t) => Math.abs(t.x - target) < Math.abs(best - target) ? t.x : best, Infinity);
9894
- setLeft(Number.isFinite(tick) ? tick : target);
9990
+ const left = scrollerRef.current?.scrollLeft ?? 0;
9991
+ const target = left + direction * vw;
9992
+ let to = target;
9993
+ let gap = vw / 2;
9994
+ for (const t of s.minor) {
9995
+ const d = Math.abs(t.x - target);
9996
+ if (d <= gap && (t.x - left) * direction >= 1) {
9997
+ to = t.x;
9998
+ gap = d;
9999
+ }
10000
+ }
10001
+ setLeft(to);
9895
10002
  },
9896
10003
  ensureVisible,
9897
10004
  reveal(target) {
@@ -9912,29 +10019,34 @@ var TimeViewport = forwardRef(function TimeViewport2({
9912
10019
  useImperativeHandle(ref, () => api, [api]);
9913
10020
  const todayAtX = todayAt === "now" ? drawn.now === null ? null : drawn.x(drawn.now) : drawn.today === null ? null : drawn.x(drawn.today) + drawn.pxPerDay / 2;
9914
10021
  const todayX = showToday ? todayAtX : null;
9915
- const layout = useMemo(() => ({ scale: drawn, labelWidth, showToday, todayX, compact: phone, ...api }), [drawn, labelWidth, showToday, todayX, phone, api]);
10022
+ const layout = useMemo(() => ({ scale: drawn, labelWidth: pinnedWidth, showToday, todayX, compact: phone, ...api }), [drawn, pinnedWidth, showToday, todayX, phone, api]);
9916
10023
  const pending = useRef(null);
9917
10024
  const applied = useRef(null);
9918
10025
  useLayoutEffect(() => {
9919
10026
  const el = scrollerRef.current;
9920
10027
  if (!el || visibleWidth <= 0) return;
9921
- writeScroll(el, visibleWidth);
9922
- store.set({ scrollLeft: el.scrollLeft, visibleWidth });
10028
+ const left = el.scrollLeft;
10029
+ const publish = () => {
10030
+ writeScroll(rowsRef.current, left, visibleWidth);
10031
+ store.set({ scrollLeft: left, visibleWidth });
10032
+ };
9923
10033
  const prev = applied.current;
9924
- applied.current = { startDay: drawn.startDay, pxPerDay: drawn.pxPerDay, visibleWidth };
10034
+ let to = null;
9925
10035
  if (!prev) {
10036
+ if (initialDate === void 0 && awaitsToday(drawn)) return publish();
9926
10037
  const at = initialDate !== void 0 ? drawn.x(initialDate) : todayAtX;
9927
- if (at !== null && Number.isFinite(at)) setLeft(at - visibleWidth / 2);
9928
- return;
10038
+ if (at !== null && Number.isFinite(at)) to = at - visibleWidth / 2;
10039
+ } else if (prev.startDay !== drawn.startDay || prev.pxPerDay !== drawn.pxPerDay || prev.visibleWidth !== visibleWidth) {
10040
+ const want = pending.current;
10041
+ pending.current = null;
10042
+ const anchor = want && want.zoom === drawn.zoom ? want : { day: prev.startDay + (wantLeft.current + prev.visibleWidth / 2) / prev.pxPerDay, at: visibleWidth / 2 };
10043
+ to = drawn.x(anchor.day) - anchor.at;
9929
10044
  }
9930
- const rescaled = prev.startDay !== drawn.startDay || prev.pxPerDay !== drawn.pxPerDay;
9931
- if (!rescaled && prev.visibleWidth === visibleWidth) return;
9932
- const want = pending.current;
9933
- pending.current = null;
9934
- const anchor = want && want.zoom === drawn.zoom ? want : { day: prev.startDay + (lastLeft.current + prev.visibleWidth / 2) / prev.pxPerDay, at: visibleWidth / 2 };
9935
- setLeft(drawn.x(anchor.day) - anchor.at);
10045
+ applied.current = { startDay: drawn.startDay, pxPerDay: drawn.pxPerDay, visibleWidth };
10046
+ if (to === null) return publish();
10047
+ setLeft(to);
9936
10048
  const focused = document.activeElement;
9937
- if (focused instanceof HTMLElement && focused !== el && el.contains(focused)) api.reveal(focused);
10049
+ if (prev && focused instanceof HTMLElement && focused !== el && el.contains(focused)) api.reveal(focused);
9938
10050
  }, [drawn, visibleWidth, initialDate, todayAtX, setLeft, store, api]);
9939
10051
  const shaded = [];
9940
10052
  if (shadeDays && drawn.pxPerDay >= SHADE_MIN) {
@@ -9956,7 +10068,7 @@ var TimeViewport = forwardRef(function TimeViewport2({
9956
10068
  const f = document.activeElement;
9957
10069
  if (!(f instanceof HTMLElement) || f === el || !el.contains(f)) return void 0;
9958
10070
  const r = f.getBoundingClientRect();
9959
- const x = (r.left + r.right) / 2 - el.getBoundingClientRect().left - labelWidth;
10071
+ const x = (r.left + r.right) / 2 - el.getBoundingClientRect().left - pinnedWidth;
9960
10072
  return x >= 0 && x <= visibleWidth ? x : void 0;
9961
10073
  };
9962
10074
  const zoom = (direction, at) => {
@@ -9980,10 +10092,11 @@ var TimeViewport = forwardRef(function TimeViewport2({
9980
10092
  };
9981
10093
  const onKeyDown = (e) => {
9982
10094
  if (e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey) return;
10095
+ if (!e.currentTarget.contains(e.target)) return;
9983
10096
  if (e.target instanceof HTMLElement && e.target.closest(FIELD)) return;
9984
- if (e.key === "+" || e.key === "=") {
10097
+ if (ZOOM_KEYS.in.includes(e.key)) {
9985
10098
  if (zoom(-1)) e.preventDefault();
9986
- } else if (e.key === "-" || e.key === "\u2212") {
10099
+ } else if (ZOOM_KEYS.out.includes(e.key)) {
9987
10100
  if (zoom(1)) e.preventDefault();
9988
10101
  } else if (e.shiftKey && (e.key === "ArrowLeft" || e.key === "ArrowRight")) {
9989
10102
  e.preventDefault();
@@ -9993,13 +10106,15 @@ var TimeViewport = forwardRef(function TimeViewport2({
9993
10106
  const wheel = useRef({ sum: 0, at: -Infinity });
9994
10107
  const onWheel = useLatest((e) => {
9995
10108
  if (!(e.ctrlKey || e.metaKey) || !onZoomChange) return;
9996
- e.preventDefault();
9997
10109
  const w = wheel.current;
9998
- if (e.timeStamp - w.at < WHEEL_PAUSE) return;
10110
+ if (e.timeStamp - w.at < WHEEL_PAUSE) return e.preventDefault();
10111
+ const direction = e.deltaY < 0 ? -1 : e.deltaY > 0 ? 1 : 0;
10112
+ if (direction === 0 || zoomTo(direction) === null) return;
10113
+ e.preventDefault();
9999
10114
  w.sum += e.deltaY * (e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? 400 : 1);
10000
10115
  if (Math.abs(w.sum) < WHEEL_STEP) return;
10001
10116
  const el = scrollerRef.current;
10002
- const x = el ? e.clientX - el.getBoundingClientRect().left - labelWidth : NaN;
10117
+ const x = el ? e.clientX - el.getBoundingClientRect().left - pinnedWidth : NaN;
10003
10118
  zoom(w.sum < 0 ? -1 : 1, x >= 0 && x <= visibleWidth ? x : void 0);
10004
10119
  wheel.current = { sum: 0, at: e.timeStamp };
10005
10120
  });
@@ -10030,9 +10145,10 @@ var TimeViewport = forwardRef(function TimeViewport2({
10030
10145
  const onScroll = () => {
10031
10146
  const el = scrollerRef.current;
10032
10147
  if (!el) return;
10033
- lastLeft.current = el.scrollLeft;
10034
- writeScroll(el, visibleWidth);
10035
- store.set({ scrollLeft: el.scrollLeft, visibleWidth });
10148
+ const left = el.scrollLeft;
10149
+ if (Math.abs(left - wantLeft.current) >= 1) wantLeft.current = left;
10150
+ writeScroll(rowsRef.current, left, visibleWidth);
10151
+ store.set({ scrollLeft: left, visibleWidth });
10036
10152
  };
10037
10153
  const headerNode = header === void 0 ? /* @__PURE__ */ jsx(TimeScaleHeader, {}) : header;
10038
10154
  const hasHeader = headerNode != null || labelHeader != null;
@@ -10064,14 +10180,14 @@ var TimeViewport = forwardRef(function TimeViewport2({
10064
10180
  tabIndex: tabStop ? 0 : void 0,
10065
10181
  onScroll,
10066
10182
  className: "peer h-full overflow-auto isolate outline-none",
10067
- style: { scrollPaddingInlineStart: labelWidth, scrollPaddingTop: frame.header },
10068
- children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col", style: { width: labelWidth + drawn.width }, children: [
10183
+ style: { scrollPaddingInlineStart: pinnedWidth, scrollPaddingTop: frame.header },
10184
+ children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col", style: { width: pinnedWidth + drawn.width }, children: [
10069
10185
  hasHeader && /* @__PURE__ */ jsxs("div", { ref: headerRef, className: cn("sticky top-0 z-[3] flex shrink-0 bg-[var(--ds-bg-surface)] border-b-[length:var(--ds-border-width)]", border), children: [
10070
10186
  /* @__PURE__ */ jsx(
10071
10187
  "div",
10072
10188
  {
10073
10189
  className: cn("sticky start-0 z-[1] flex shrink-0 items-end px-ds-3 py-ds-1.5 bg-[var(--ds-bg-surface)] border-e-[length:var(--ds-border-width)] text-xs font-medium text-[var(--ds-text-secondary)]", border),
10074
- style: { width: labelWidth },
10190
+ style: { width: pinnedWidth },
10075
10191
  children: labelHeader
10076
10192
  }
10077
10193
  ),
@@ -10090,18 +10206,18 @@ var TimeViewport = forwardRef(function TimeViewport2({
10090
10206
  )
10091
10207
  ] }),
10092
10208
  /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
10093
- /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-y-0", style: { insetInlineStart: labelWidth, width: drawn.width }, children: [
10209
+ /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-y-0", style: { insetInlineStart: pinnedWidth, width: drawn.width }, children: [
10094
10210
  shaded.map((b) => /* @__PURE__ */ jsx("span", { "data-shaded-days": "", className: "absolute inset-y-0 bg-[var(--ds-bg-muted)] opacity-70", style: { insetInlineStart: b.x, width: b.width } }, b.key)),
10095
10211
  drawn.minor.slice(1).map((t) => /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 border-s-[length:1px] border-[var(--ds-border-subtle)]", style: { insetInlineStart: t.x } }, t.key)),
10096
10212
  todayX !== null && /* @__PURE__ */ jsx("span", { "data-today-line": "", className: "absolute inset-y-0 -translate-x-1/2 border-s-2 border-[var(--ds-accent-primary)]", style: { insetInlineStart: todayX } })
10097
10213
  ] }),
10098
- children,
10099
- /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: cn("sticky start-0 z-[2] min-h-0 flex-1 bg-[var(--ds-bg-surface)] border-e-[length:var(--ds-border-width)]", border), style: { width: labelWidth } })
10214
+ /* @__PURE__ */ jsx("div", { ref: rowsRef, "data-time-rows": "", className: "contents", children }),
10215
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: cn("sticky start-0 z-[2] min-h-0 flex-1 bg-[var(--ds-bg-surface)] border-e-[length:var(--ds-border-width)]", border), style: { width: pinnedWidth } })
10100
10216
  ] })
10101
10217
  ] })
10102
10218
  }
10103
10219
  ),
10104
- /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 peer-focus-visible:shadow-[inset_0_0_0_2px_var(--ds-border-focus)]" })
10220
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 peer-focus-visible:shadow-[inset_0_0_0_2px_var(--ds-border-focus)] peer-focus-visible:outline-2 peer-focus-visible:-outline-offset-2 peer-focus-visible:outline-transparent" })
10105
10221
  ] })
10106
10222
  ] }) }) })
10107
10223
  );
@@ -10138,7 +10254,7 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
10138
10254
  const shows = (i) => i === 0 ? fits(0) : `calc(${[fits(i), ...labels.slice(0, i).map((_, j) => `(1 - ${fits(j)})`)].join(" * ")})`;
10139
10255
  const named = props["aria-label"] !== void 0 || props["aria-labelledby"] !== void 0;
10140
10256
  label = /* @__PURE__ */ jsxs(Fragment, { children: [
10141
- /* @__PURE__ */ jsx("span", { "data-time-bar-label": "", "aria-hidden": "true", className: "sticky grid overflow-hidden whitespace-nowrap", style: { ...sticky, paddingInline: inset }, children: labels.map((text, i) => /* @__PURE__ */ jsx("span", { ref: i === 0 ? textRef : void 0, "data-time-bar-form": "", className: "justify-self-start [grid-area:1/1]", style: { opacity: shows(i) }, children: text }, i)) }),
10257
+ /* @__PURE__ */ jsx("span", { "data-time-bar-label": "", "data-gs-clip": "", "aria-hidden": "true", className: "sticky grid overflow-hidden whitespace-nowrap", style: { ...sticky, paddingInline: inset }, children: labels.map((text, i) => /* @__PURE__ */ jsx("span", { ref: i === 0 ? textRef : void 0, "data-time-bar-form": "", className: "justify-self-start [grid-area:1/1]", style: { opacity: shows(i) }, children: text }, i)) }),
10142
10258
  !named && /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels[0] })
10143
10259
  ] });
10144
10260
  } else if (children != null && children !== false) {
@@ -10146,6 +10262,7 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
10146
10262
  "span",
10147
10263
  {
10148
10264
  "data-time-bar-label": "",
10265
+ "data-gs-clip": "",
10149
10266
  className: "sticky flex min-w-0 items-center gap-ds-1 overflow-hidden whitespace-nowrap",
10150
10267
  style: { ...sticky, paddingInline: INSET, opacity: `clamp(0, calc((${inView} - ${MIN_TITLE}) * 1000), 1)` },
10151
10268
  children: typeof children === "string" ? /* @__PURE__ */ jsx("span", { className: "truncate", children }) : children
@@ -10157,7 +10274,8 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
10157
10274
  {
10158
10275
  ...props,
10159
10276
  ref: barRef,
10160
- className: cn("absolute top-ds-2 bottom-ds-2 flex items-center overflow-clip rounded-[var(--ds-radius-sm)] outline-none focus-visible:shadow-[var(--ds-shadow-focus)]", className),
10277
+ "data-gs-clip": "",
10278
+ className: cn("absolute top-ds-2 bottom-ds-2 flex items-center overflow-clip rounded-[var(--ds-radius-sm)] outline-none focus-visible:outline-hidden focus-visible:shadow-[var(--ds-shadow-focus)]", className),
10161
10279
  style: { insetInlineStart: x, width: drawnWidth, ...style },
10162
10280
  children: label
10163
10281
  }
@@ -10175,13 +10293,39 @@ var HEAD = 6;
10175
10293
  var BESIDE = STUB + 6;
10176
10294
  var groupKey = (id) => `group:${id}`;
10177
10295
  var itemKey = (id) => `item:${id}`;
10296
+ var asDate = (value) => value instanceof Date || typeof value === "string" ? value : void 0;
10297
+ function datesOf(item) {
10298
+ const start = asDate(item.start);
10299
+ if (start === void 0 || !Number.isFinite(dayOf(start))) return null;
10300
+ if (item.milestone || item.end === void 0 || item.end === null) return { start, end: start };
10301
+ const end = asDate(item.end);
10302
+ return end !== void 0 && Number.isFinite(endDayOf(end)) ? { start, end } : null;
10303
+ }
10304
+ function uniqueById(list) {
10305
+ const seen = /* @__PURE__ */ new Set();
10306
+ const repeated = /* @__PURE__ */ new Set();
10307
+ const unique = [];
10308
+ for (const entry of list) {
10309
+ if (seen.has(entry.id)) repeated.add(entry.id);
10310
+ else {
10311
+ seen.add(entry.id);
10312
+ unique.push(entry);
10313
+ }
10314
+ }
10315
+ return repeated.size ? { unique, repeated: [...repeated] } : { unique: list, repeated: [] };
10316
+ }
10178
10317
  function ganttRows(items, groups, collapsed) {
10179
10318
  const known = new Set(groups.map((g) => g.id));
10180
10319
  const byGroup = /* @__PURE__ */ new Map();
10181
10320
  const loose = [];
10182
10321
  for (const item of items) {
10183
- if (item.group === void 0 || !known.has(item.group)) loose.push(item);
10184
- else byGroup.set(item.group, [...byGroup.get(item.group) ?? [], item]);
10322
+ if (item.group === void 0 || !known.has(item.group)) {
10323
+ loose.push(item);
10324
+ continue;
10325
+ }
10326
+ const list = byGroup.get(item.group);
10327
+ if (list) list.push(item);
10328
+ else byGroup.set(item.group, [item]);
10185
10329
  }
10186
10330
  const shown = groups.filter((g) => byGroup.has(g.id));
10187
10331
  const topLevel = shown.length + loose.length;
@@ -10199,16 +10343,23 @@ function ganttRows(items, groups, collapsed) {
10199
10343
  return rows;
10200
10344
  }
10201
10345
  function itemShape(item, scale) {
10346
+ const dates = datesOf(item);
10347
+ if (!dates) return { kind: item.milestone ? "milestone" : "bar", x0: NaN, x1: NaN, in: NaN, out: NaN };
10202
10348
  if (item.milestone) {
10203
- const centre = scale.x((dayOf(item.start) + endDayOf(item.start)) / 2);
10349
+ const centre = scale.x((dayOf(dates.start) + endDayOf(dates.start)) / 2);
10204
10350
  return { kind: "milestone", x0: centre - TIP, x1: centre + TIP, in: centre - TIP, out: centre + TIP };
10205
10351
  }
10206
- const { x, width } = scale.span(item.start, item.end ?? item.start);
10352
+ const { x, width } = scale.span(dates.start, dates.end);
10207
10353
  const x1 = x + Math.max(width, MIN_BAR);
10208
10354
  return { kind: "bar", x0: x, x1, in: x, out: x1 };
10209
10355
  }
10210
- function groupExtent(items) {
10211
- return timeExtent(items.map((i) => ({ start: i.start, end: i.milestone ? void 0 : i.end })));
10356
+ function itemsExtent(items) {
10357
+ const ranges = [];
10358
+ for (const item of items) {
10359
+ const dates = datesOf(item);
10360
+ if (dates) ranges.push(dates);
10361
+ }
10362
+ return timeExtent(ranges);
10212
10363
  }
10213
10364
  var drawable = (shape) => !!shape && Number.isFinite(shape.x0) && Number.isFinite(shape.x1);
10214
10365
  function ganttArrows(items, rows, shapeOf, collapsed) {
@@ -10249,19 +10400,37 @@ function labelPlace(shape, textWidth, scaleWidth, { pad: pad3 = 8, gap = BESIDE
10249
10400
  return shape.x0 - gap - textWidth >= 0 ? "before" : "after";
10250
10401
  }
10251
10402
  var TONES = {
10252
- neutral: { fill: "bg-[var(--ds-status-neutral)]", on: "text-[var(--ds-text-on-neutral)]" },
10253
- info: { fill: "bg-[var(--ds-status-info)]", on: "text-[var(--ds-text-on-info)]" },
10403
+ neutral: { fill: "bg-[var(--ds-status-neutral)]", on: "text-[var(--ds-text-on-neutral)]", icon: "minus" },
10404
+ info: { fill: "bg-[var(--ds-status-info)]", on: "text-[var(--ds-text-on-info)]", icon: "info" },
10254
10405
  success: { fill: "bg-[var(--ds-status-success)]", on: "text-[var(--ds-text-on-success)]", icon: "check" },
10255
10406
  warning: { fill: "bg-[var(--ds-status-warning)]", on: "text-[var(--ds-text-on-warning)]", icon: "warning" },
10256
10407
  error: { fill: "bg-[var(--ds-status-error)]", on: "text-[var(--ds-text-on-error)]", icon: "error" },
10257
- accent: { fill: "bg-[var(--ds-accent-primary)]", on: "text-[var(--ds-text-on-primary)]" }
10408
+ accent: { fill: "bg-[var(--ds-accent-primary)]", on: "text-[var(--ds-text-on-primary)]", icon: "star" }
10258
10409
  };
10259
10410
  var ICON = 12;
10260
10411
  var ICON_GAP = 4;
10261
10412
  var LEVEL_INDENT = 16;
10262
10413
  var TARGET = 24;
10414
+ var NO_DATES = "no dates";
10263
10415
  var KNOCKOUT = 2;
10416
+ var EDGE = "border-[length:1px] border-transparent";
10417
+ var EDGE_WIDTH = 1;
10264
10418
  var SELECTED = "outline-solid outline-2 outline-offset-2 outline-[var(--ds-text-primary)]";
10419
+ var UNDATED_INSET = 16;
10420
+ var NO_GROUPS = [];
10421
+ var NO_STATUSES = {};
10422
+ var warned = /* @__PURE__ */ new Set();
10423
+ function warnOnce(message) {
10424
+ let dev = false;
10425
+ try {
10426
+ dev = process.env.NODE_ENV !== "production";
10427
+ } catch {
10428
+ dev = false;
10429
+ }
10430
+ if (!dev || warned.has(message)) return;
10431
+ warned.add(message);
10432
+ console.warn(`Gantt: ${message}`);
10433
+ }
10265
10434
  function GanttArrows({ arrows, width, height }) {
10266
10435
  const { labelWidth } = useTimeViewportLayout();
10267
10436
  return /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", "data-gantt-arrows": "", className: "pointer-events-none absolute top-0 overflow-visible", style: { insetInlineStart: labelWidth, width, height }, children: arrows.map((a) => /* @__PURE__ */ jsxs("g", { "data-from": a.from, "data-to": a.to, "data-routed": a.routed || void 0, className: "text-[var(--ds-text-muted)]", children: [
@@ -10269,23 +10438,47 @@ function GanttArrows({ arrows, width, height }) {
10269
10438
  /* @__PURE__ */ jsx("path", { d: a.head, fill: "currentColor" })
10270
10439
  ] }, a.key)) });
10271
10440
  }
10441
+ function Cue({ name, size }) {
10442
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-gantt-cue": name, className: "flex shrink-0", children: /* @__PURE__ */ jsx(Icon, { name, size }) });
10443
+ }
10272
10444
  function Legend({ statuses }) {
10273
10445
  const entries = Object.entries(statuses);
10274
10446
  if (!entries.length) return null;
10275
10447
  return /* @__PURE__ */ jsx("ul", { "aria-label": "Status key", className: "ms-auto flex flex-wrap items-center gap-x-ds-3 gap-y-ds-1 ps-ds-2 text-xs text-[var(--ds-text-secondary)]", children: entries.map(([key, s]) => {
10276
10448
  const tone = TONES[s.tone] ?? TONES.neutral;
10277
10449
  return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-ds-1.5", children: [
10278
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("grid size-4 shrink-0 place-items-center rounded-[var(--ds-radius-sm)]", tone.fill, tone.on), children: tone.icon && /* @__PURE__ */ jsx(Icon, { name: tone.icon, size: 10 }) }),
10450
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("grid size-4 shrink-0 place-items-center rounded-[var(--ds-radius-sm)]", EDGE, tone.fill, tone.on), children: /* @__PURE__ */ jsx(Cue, { name: s.icon ?? tone.icon, size: 10 }) }),
10279
10451
  s.label
10280
10452
  ] }, key);
10281
10453
  }) });
10282
10454
  }
10455
+ function NoDates({ cue, selected, className, ...props }) {
10456
+ const { labelWidth } = useTimeViewportLayout();
10457
+ return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center", children: /* @__PURE__ */ jsxs(
10458
+ "span",
10459
+ {
10460
+ ...props,
10461
+ "data-gantt-shape": "undated",
10462
+ className: cn(
10463
+ "sticky flex cursor-pointer items-center gap-ds-1 whitespace-nowrap rounded-[var(--ds-radius-sm)] border-[length:1px] border-dashed border-[var(--ds-border-default)] px-ds-1.5 py-ds-0.5 text-xs text-[var(--ds-text-secondary)]",
10464
+ "outline-none focus-visible:outline-hidden focus-visible:shadow-[var(--ds-shadow-focus)]",
10465
+ selected && SELECTED,
10466
+ className
10467
+ ),
10468
+ style: { insetInlineStart: labelWidth + UNDATED_INSET },
10469
+ children: [
10470
+ cue,
10471
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "No dates" })
10472
+ ]
10473
+ }
10474
+ ) });
10475
+ }
10283
10476
  function Gantt({
10284
10477
  items,
10285
- groups = [],
10478
+ groups = NO_GROUPS,
10286
10479
  selectedId,
10287
10480
  onSelect,
10288
- statuses = {},
10481
+ statuses = NO_STATUSES,
10289
10482
  zoom: zoomProp,
10290
10483
  defaultZoom = "week",
10291
10484
  onZoomChange,
@@ -10293,6 +10486,7 @@ function Gantt({
10293
10486
  weekStartsOn,
10294
10487
  locale,
10295
10488
  renderLabel,
10489
+ describe,
10296
10490
  labelHeader = "Task",
10297
10491
  toolbar,
10298
10492
  "aria-label": ariaLabel,
@@ -10304,31 +10498,53 @@ function Gantt({
10304
10498
  if (zoomProp === void 0) setZoomState(next);
10305
10499
  onZoomChange?.(next);
10306
10500
  };
10501
+ const zoomable = zoomProp === void 0 || onZoomChange !== void 0;
10307
10502
  const [selectedState, setSelectedState] = useState(null);
10308
- const selected = selectedId !== void 0 ? selectedId : selectedState;
10503
+ const [controlled, setControlled] = useState(selectedId !== void 0);
10504
+ const [overridden, setOverridden] = useState(null);
10505
+ if (!controlled && selectedId !== void 0) {
10506
+ setControlled(true);
10507
+ if (selectedState !== null && selectedState !== selectedId) setOverridden(selectedState);
10508
+ }
10509
+ const isControlled = controlled || selectedId !== void 0;
10510
+ const selected = isControlled ? selectedId ?? null : selectedState;
10511
+ useEffect(() => {
10512
+ if (overridden !== null) {
10513
+ warnOnce("selectedId was given a value after the Gantt had kept a selection of its own. To control the selection, pass selectedId from the first render (null for none); to let the Gantt keep it, leave selectedId out.");
10514
+ }
10515
+ }, [overridden]);
10309
10516
  const select = (item) => {
10310
- if (selectedId === void 0) setSelectedState(item?.id ?? null);
10517
+ if (!isControlled) setSelectedState(item?.id ?? null);
10311
10518
  onSelect?.(item);
10312
10519
  };
10313
10520
  const [collapsed, setCollapsed] = useState(() => new Set(groups.filter((g) => g.defaultCollapsed).map((g) => g.id)));
10314
- const extent = useMemo(() => timeExtent(items.map((i) => ({ start: i.start, end: i.milestone ? void 0 : i.end }))), [items]);
10521
+ const plan = useMemo(() => uniqueById(items), [items]);
10522
+ const phases = useMemo(() => uniqueById(groups), [groups]);
10523
+ useEffect(() => {
10524
+ const quoted = (ids) => ids.map((id) => `"${id}"`).join(", ");
10525
+ if (plan.repeated.length) warnOnce(`more than one item has the id ${quoted(plan.repeated)}; only the first of each is shown.`);
10526
+ if (phases.repeated.length) warnOnce(`more than one group has the id ${quoted(phases.repeated)}; only the first of each is shown.`);
10527
+ }, [plan, phases]);
10528
+ const tasks = plan.unique;
10529
+ const extent = useMemo(() => itemsExtent(tasks), [tasks]);
10315
10530
  const scale = useTimeScale({ start: extent.start, end: extent.end, zoom, today, weekStartsOn, locale, pad: 1 });
10316
- const rows = useMemo(() => ganttRows(items, groups, collapsed), [items, groups, collapsed]);
10317
- const shapes = useMemo(() => new Map(items.map((i) => [i.id, itemShape(i, scale)])), [items, scale]);
10318
- const extents = useMemo(() => new Map(rows.filter((r) => r.children).map((r) => [r.group.id, groupExtent(r.children)])), [rows]);
10319
- const arrows = useMemo(() => ganttArrows(items, rows, (i) => shapes.get(i.id), collapsed), [items, rows, shapes, collapsed]);
10531
+ const rows = useMemo(() => ganttRows(tasks, phases.unique, collapsed), [tasks, phases, collapsed]);
10532
+ const shapes = useMemo(() => new Map(tasks.map((i) => [i.id, itemShape(i, scale)])), [tasks, scale]);
10533
+ const extents = useMemo(() => new Map(rows.filter((r) => r.children).map((r) => [r.group.id, itemsExtent(r.children)])), [rows]);
10534
+ const arrows = useMemo(() => ganttArrows(tasks, rows, (i) => shapes.get(i.id), collapsed), [tasks, rows, shapes, collapsed]);
10320
10535
  const byKey = useMemo(() => new Map(rows.map((r) => [r.key, r])), [rows]);
10321
10536
  const names = useMemo(() => {
10322
- const byId = new Map(items.map((i) => [i.id, i]));
10537
+ const byId = new Map(tasks.map((i) => [i.id, i]));
10323
10538
  const list = new Intl.ListFormat(locale, { type: "conjunction" });
10324
10539
  const out = /* @__PURE__ */ new Map();
10325
- for (const item of items) {
10540
+ for (const item of tasks) {
10541
+ const dates = datesOf(item);
10326
10542
  const after = (item.dependsOn ?? []).flatMap((id) => byId.has(id) && id !== item.id ? [byId.get(id).title] : []);
10327
10543
  const status = item.status === void 0 ? void 0 : statuses[item.status];
10328
10544
  out.set(itemKey(item.id), [
10329
10545
  item.title,
10330
10546
  item.milestone ? "milestone" : null,
10331
- formatDateRange(item.start, item.milestone ? item.start : item.end ?? item.start, { locale }),
10547
+ dates ? formatDateRange(dates.start, dates.end, { locale }) : NO_DATES,
10332
10548
  status?.label ?? null,
10333
10549
  after.length ? `after ${list.format(after)}` : null
10334
10550
  ].filter((part) => part).join(", "));
@@ -10337,31 +10553,66 @@ function Gantt({
10337
10553
  if (!row.group || !row.children) continue;
10338
10554
  const e = extents.get(row.group.id);
10339
10555
  const count = row.children.length === 1 ? "1 item" : `${row.children.length} items`;
10340
- out.set(row.key, [row.group.title, formatDateRange(e.start, e.end, { locale }), count].filter((part) => part).join(", "));
10556
+ const span = Number.isFinite(e.start) ? formatDateRange(e.start, e.end, { locale }) : NO_DATES;
10557
+ out.set(row.key, [row.group.title, span, count].filter((part) => part).join(", "));
10341
10558
  }
10342
10559
  return out;
10343
- }, [items, rows, extents, statuses, locale]);
10560
+ }, [tasks, rows, extents, statuses, locale]);
10561
+ const nameOf = (row) => {
10562
+ const name = names.get(row.key) ?? "";
10563
+ const words = row.item && describe ? describe(row.item) : "";
10564
+ return words ? `${name}, ${words}` : name;
10565
+ };
10566
+ const regionLabel = useMemo(() => {
10567
+ const span = Number.isFinite(extent.start) ? formatDateRange(extent.start, extent.end, { locale }) : "";
10568
+ return `${ariaLabel}, ${span || NO_DATES}`;
10569
+ }, [ariaLabel, extent, locale]);
10570
+ const hasStatuses = useMemo(() => Object.keys(statuses).length > 0, [statuses]);
10571
+ const toneOf = (item) => {
10572
+ const status = item.status === void 0 ? void 0 : statuses[item.status];
10573
+ const tone = TONES[status?.tone ?? "neutral"] ?? TONES.neutral;
10574
+ return { tone, cue: hasStatuses ? status?.icon ?? tone.icon : void 0 };
10575
+ };
10344
10576
  const probeRef = useRef(null);
10345
10577
  const measure = useTextMeasure(probeRef);
10346
10578
  const treeRef = useRef(null);
10347
10579
  const viewportRef = useRef(null);
10348
10580
  const [activeState, setActive] = useState(null);
10581
+ const [focusWithin, setFocusWithin] = useState(false);
10349
10582
  const selectedKey = selected === null ? null : itemKey(selected);
10350
- const active = activeState !== null && byKey.has(activeState) ? activeState : selectedKey !== null && byKey.has(selectedKey) ? selectedKey : rows[0]?.key ?? "";
10351
- const toggle = (id) => {
10352
- const key = groupKey(id);
10353
- const closing = !collapsed.has(id);
10354
- setCollapsed((cur) => {
10355
- const next = new Set(cur);
10356
- if (next.has(id)) next.delete(id);
10357
- else next.add(id);
10358
- return next;
10359
- });
10360
- if (closing && byKey.get(active)?.parentKey === key) {
10361
- setActive(key);
10362
- if (treeRef.current?.contains(document.activeElement)) grid.focus(key);
10583
+ const selectedRow = selectedKey !== null && byKey.has(selectedKey) ? selectedKey : null;
10584
+ const lastFocused = activeState !== null && byKey.has(activeState) ? activeState : null;
10585
+ const active = (focusWithin ? lastFocused : null) ?? selectedRow ?? lastFocused ?? rows[0]?.key ?? "";
10586
+ const focusedRow = useRef(null);
10587
+ useLayoutEffect(() => {
10588
+ const was = focusedRow.current;
10589
+ if (!was) return;
10590
+ const row = byKey.get(was.key);
10591
+ if (row) {
10592
+ if (row.index !== was.index) focusedRow.current = { key: was.key, index: row.index };
10593
+ return;
10363
10594
  }
10364
- };
10595
+ focusedRow.current = null;
10596
+ const now = document.activeElement;
10597
+ if (now && now !== document.body && now.isConnected) return;
10598
+ const tree = treeRef.current;
10599
+ if (tree) focusAfterRemoval(tree.querySelectorAll("[data-grid-key]"), was.index);
10600
+ }, [byKey]);
10601
+ const lastSelected = useRef(selected);
10602
+ useEffect(() => {
10603
+ if (lastSelected.current === selected) return;
10604
+ lastSelected.current = selected;
10605
+ const tree = treeRef.current;
10606
+ if (selected === null || !tree || tree.contains(document.activeElement)) return;
10607
+ const el = tree.querySelector(`[data-grid-key="${CSS.escape(itemKey(selected))}"]`);
10608
+ if (el) viewportRef.current?.reveal(el);
10609
+ }, [selected]);
10610
+ const toggle = (id) => setCollapsed((cur) => {
10611
+ const next = new Set(cur);
10612
+ if (next.has(id)) next.delete(id);
10613
+ else next.add(id);
10614
+ return next;
10615
+ });
10365
10616
  const activate = (row) => {
10366
10617
  if (row.group) toggle(row.group.id);
10367
10618
  else if (row.item) select(row.item);
@@ -10417,6 +10668,11 @@ function Gantt({
10417
10668
  }
10418
10669
  grid.onKeyDown(e);
10419
10670
  };
10671
+ const onTreeBlur = (e) => {
10672
+ if (e.currentTarget.contains(e.relatedTarget)) return;
10673
+ setFocusWithin(false);
10674
+ if (e.relatedTarget) focusedRow.current = null;
10675
+ };
10420
10676
  const onRowClick = (row) => {
10421
10677
  if (row.item) select(row.item.id === selected ? null : row.item);
10422
10678
  else if (row.group) toggle(row.group.id);
@@ -10428,25 +10684,33 @@ function Gantt({
10428
10684
  "aria-level": row.level,
10429
10685
  "aria-setsize": row.setSize,
10430
10686
  "aria-posinset": row.posInSet,
10431
- "aria-label": names.get(row.key),
10687
+ "aria-label": nameOf(row),
10432
10688
  "data-grid-key": row.key,
10433
10689
  tabIndex: grid.tabIndexOf(row.key),
10434
- onFocus: () => setActive(row.key)
10690
+ onFocus: () => {
10691
+ setActive(row.key);
10692
+ focusedRow.current = { key: row.key, index: row.index };
10693
+ }
10435
10694
  });
10436
10695
  const renderItem = (row, item) => {
10437
10696
  const shape = shapes.get(item.id);
10438
- if (!drawable(shape)) return null;
10439
- const tone = TONES[item.status !== void 0 && statuses[item.status]?.tone || "neutral"] ?? TONES.neutral;
10697
+ const dates = datesOf(item);
10440
10698
  const isSelected = item.id === selected;
10441
- const textWidth = measure(item.title) + (tone.icon ? ICON + ICON_GAP : 0) + 2 * KNOCKOUT;
10442
- const place = labelPlace(shape, textWidth, scale.width);
10699
+ const { tone, cue } = toneOf(item);
10700
+ if (!dates || !drawable(shape)) {
10701
+ return /* @__PURE__ */ jsx(NoDates, { ...treeitem(row), "aria-selected": isSelected, selected: isSelected, cue: cue && /* @__PURE__ */ jsx(Cue, { name: cue, size: ICON }) });
10702
+ }
10703
+ const textWidth = measure(item.title) + (cue ? ICON + ICON_GAP : 0) + 2 * KNOCKOUT;
10704
+ const place = labelPlace(shape, textWidth + 2 * EDGE_WIDTH, scale.width);
10443
10705
  const beside = place === "after" ? shape.x1 + BESIDE : shape.x0 - BESIDE - textWidth;
10444
- const icon = tone.icon && /* @__PURE__ */ jsx(Icon, { name: tone.icon, size: ICON, className: "shrink-0" });
10445
10706
  const title = /* @__PURE__ */ jsxs(Fragment, { children: [
10446
- icon,
10707
+ cue && /* @__PURE__ */ jsx(Cue, { name: cue, size: ICON }),
10447
10708
  /* @__PURE__ */ jsx("span", { className: "truncate", children: item.title })
10448
10709
  ] });
10449
- const shapeBox = shape.kind === "milestone" ? /* @__PURE__ */ jsx("div", { ...treeitem(row), "aria-selected": isSelected, className: cn("group absolute grid cursor-pointer place-items-center", "outline-none"), style: { insetInlineStart: (shape.x0 + shape.x1) / 2 - TARGET / 2, top: (ROW_HEIGHT2 - TARGET) / 2, width: TARGET, height: TARGET }, children: /* @__PURE__ */ jsx("span", { "data-gantt-shape": "milestone", "data-item": item.id, className: cn("block rotate-45 rounded-[2px] group-focus-visible:shadow-[var(--ds-shadow-focus)]", tone.fill, isSelected && SELECTED), style: { width: DIAMOND, height: DIAMOND } }) }) : (
10710
+ const shapeBox = shape.kind === "milestone" ? (
10711
+ // the target's own outline is its focus indicator in forced colours, which drop the diamond's shadow ring
10712
+ /* @__PURE__ */ jsx("div", { ...treeitem(row), "aria-selected": isSelected, className: "group absolute grid cursor-pointer place-items-center outline-none focus-visible:outline-hidden", style: { insetInlineStart: (shape.x0 + shape.x1) / 2 - TARGET / 2, top: (ROW_HEIGHT2 - TARGET) / 2, width: TARGET, height: TARGET }, children: /* @__PURE__ */ jsx("span", { "data-gantt-shape": "milestone", "data-item": item.id, className: cn("block rotate-45 rounded-[2px] group-focus-visible:shadow-[var(--ds-shadow-focus)]", EDGE, tone.fill, isSelected && SELECTED), style: { width: DIAMOND, height: DIAMOND } }) })
10713
+ ) : (
10450
10714
  // TimeBar keeps the title in view beside the pinned column while the bar's start is scrolled away
10451
10715
  /* @__PURE__ */ jsx(
10452
10716
  TimeBar,
@@ -10455,12 +10719,12 @@ function Gantt({
10455
10719
  "aria-selected": isSelected,
10456
10720
  "data-gantt-shape": "bar",
10457
10721
  "data-item": item.id,
10458
- start: item.start,
10459
- end: item.end ?? item.start,
10722
+ start: dates.start,
10723
+ end: dates.end,
10460
10724
  minWidth: MIN_BAR,
10461
- className: cn("cursor-pointer text-xs font-medium", tone.fill, tone.on, isSelected && SELECTED),
10725
+ className: cn("cursor-pointer text-xs font-medium", EDGE, tone.fill, tone.on, isSelected && SELECTED),
10462
10726
  style: { top: (ROW_HEIGHT2 - BAR_HEIGHT) / 2, bottom: "auto", height: BAR_HEIGHT },
10463
- children: place === "inside" && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex min-w-0 items-center gap-ds-1 overflow-hidden", children: title })
10727
+ children: place === "inside" && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-gs-clip": "", className: "flex min-w-0 items-center gap-ds-1 overflow-hidden", children: title })
10464
10728
  }
10465
10729
  )
10466
10730
  );
@@ -10479,23 +10743,29 @@ function Gantt({
10479
10743
  )
10480
10744
  ] });
10481
10745
  };
10482
- const renderGroup = (row, extent2) => /* @__PURE__ */ jsxs(Fragment, { children: [
10483
- /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "absolute inset-0 bg-[var(--ds-bg-muted)] opacity-60" }),
10484
- extent2 && Number.isFinite(extent2.start) && // a 24 px target around the thin summary bar it draws
10485
- /* @__PURE__ */ jsx(
10486
- TimeBar,
10487
- {
10488
- ...treeitem(row),
10489
- "aria-expanded": !collapsed.has(row.group.id),
10490
- "data-gantt-shape": "summary",
10491
- start: extent2.start,
10492
- end: extent2.end,
10493
- minWidth: MIN_BAR,
10494
- className: "cursor-pointer before:absolute before:inset-x-0 before:top-1/2 before:h-ds-2 before:-translate-y-1/2 before:rounded-[var(--ds-radius-full)] before:bg-[var(--ds-text-secondary)]",
10495
- style: { top: (ROW_HEIGHT2 - TARGET) / 2, bottom: "auto", height: TARGET }
10496
- }
10497
- )
10498
- ] });
10746
+ const renderGroup = (row, group) => {
10747
+ const extent2 = extents.get(group.id);
10748
+ const expanded = !collapsed.has(group.id);
10749
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
10750
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "absolute inset-0 bg-[var(--ds-bg-muted)] opacity-60" }),
10751
+ extent2 && Number.isFinite(extent2.start) ? (
10752
+ // a 24 px target around the thin summary bar it draws
10753
+ /* @__PURE__ */ jsx(
10754
+ TimeBar,
10755
+ {
10756
+ ...treeitem(row),
10757
+ "aria-expanded": expanded,
10758
+ "data-gantt-shape": "summary",
10759
+ start: extent2.start,
10760
+ end: extent2.end,
10761
+ minWidth: MIN_BAR,
10762
+ className: "cursor-pointer before:absolute before:inset-x-0 before:top-1/2 before:h-ds-2 before:-translate-y-1/2 before:rounded-[var(--ds-radius-full)] before:border-[length:1px] before:border-transparent before:bg-[var(--ds-text-secondary)]",
10763
+ style: { top: (ROW_HEIGHT2 - TARGET) / 2, bottom: "auto", height: TARGET }
10764
+ }
10765
+ )
10766
+ ) : /* @__PURE__ */ jsx(NoDates, { ...treeitem(row), "aria-expanded": expanded })
10767
+ ] });
10768
+ };
10499
10769
  const label = (row) => {
10500
10770
  if (row.group) {
10501
10771
  return /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: "flex min-w-0 items-center gap-ds-1.5 font-medium", children: [
@@ -10513,27 +10783,30 @@ function Gantt({
10513
10783
  ] })
10514
10784
  );
10515
10785
  };
10516
- return /* @__PURE__ */ jsx(
10786
+ const legend = /* @__PURE__ */ jsx(Legend, { statuses });
10787
+ const legendApart = toolbar === false && hasStatuses;
10788
+ const chart = /* @__PURE__ */ jsx(
10517
10789
  TimeViewport,
10518
10790
  {
10519
10791
  ref: viewportRef,
10520
10792
  scale,
10521
- onZoomChange: changeZoom,
10793
+ onZoomChange: zoomable ? changeZoom : void 0,
10522
10794
  labelHeader,
10523
10795
  toolbar: toolbar === false ? false : /* @__PURE__ */ jsxs(Fragment, { children: [
10524
10796
  toolbar,
10525
- /* @__PURE__ */ jsx(Legend, { statuses })
10797
+ legend
10526
10798
  ] }),
10527
10799
  tabStop: false,
10528
- "aria-label": ariaLabel,
10529
- className,
10530
- children: /* @__PURE__ */ jsxs("div", { ref: treeRef, role: "tree", "aria-label": ariaLabel, className: "relative", onKeyDown, children: [
10800
+ "aria-label": regionLabel,
10801
+ className: legendApart ? "h-auto flex-1" : className,
10802
+ children: /* @__PURE__ */ jsxs("div", { ref: treeRef, role: "tree", "aria-label": ariaLabel, className: "relative", onKeyDown, onFocus: () => setFocusWithin(true), onBlur: onTreeBlur, children: [
10531
10803
  /* @__PURE__ */ jsx("span", { ref: probeRef, "aria-hidden": "true", className: "pointer-events-none invisible absolute whitespace-nowrap text-xs font-medium", children: "0" }),
10532
10804
  /* @__PURE__ */ jsx(GanttArrows, { arrows, width: scale.width, height: rows.length * ROW_HEIGHT2 }),
10533
10805
  rows.map((row) => /* @__PURE__ */ jsx(
10534
10806
  TimeRow,
10535
10807
  {
10536
10808
  "data-row-key": row.key,
10809
+ height: ROW_HEIGHT2,
10537
10810
  label: label(row),
10538
10811
  className: "cursor-pointer",
10539
10812
  labelClassName: cn(
@@ -10541,13 +10814,18 @@ function Gantt({
10541
10814
  row.item && row.item.id === selected && "bg-[image:linear-gradient(var(--ds-accent-subtle),var(--ds-accent-subtle))] text-[var(--ds-accent-subtle-text)]"
10542
10815
  ),
10543
10816
  onClick: () => onRowClick(row),
10544
- children: row.group ? renderGroup(row, extents.get(row.group.id)) : renderItem(row, row.item)
10817
+ children: row.group ? renderGroup(row, row.group) : renderItem(row, row.item)
10545
10818
  },
10546
10819
  row.key
10547
10820
  ))
10548
10821
  ] })
10549
10822
  }
10550
10823
  );
10824
+ if (!legendApart) return chart;
10825
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 w-full min-w-0 flex-col", className), children: [
10826
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 px-ds-2 py-ds-1.5", children: legend }),
10827
+ chart
10828
+ ] });
10551
10829
  }
10552
10830
  var DEFAULT_OFFSET2 = "var(--ds-space-4)";
10553
10831
  var LAUNCHER_SIZES = {
@@ -11096,6 +11374,7 @@ function TabsList({ children, className }) {
11096
11374
  {
11097
11375
  role: "tablist",
11098
11376
  ...overflow === "scroll" ? track : {},
11377
+ "data-gs-clip": overflow === "scroll" ? void 0 : "",
11099
11378
  className: cn(
11100
11379
  trackClasses("horizontal"),
11101
11380
  "max-w-full",
@@ -11200,6 +11479,7 @@ function TabsRoot(props) {
11200
11479
  {
11201
11480
  role: "tablist",
11202
11481
  ...effectiveOverflow === "scroll" ? track : {},
11482
+ "data-gs-clip": effectiveOverflow === "scroll" ? void 0 : "",
11203
11483
  className: cn(
11204
11484
  trackClasses("horizontal"),
11205
11485
  effectiveOverflow === "scroll" ? SCROLL_TRACK : "overflow-hidden",
@@ -11217,14 +11497,35 @@ var Tabs = Object.assign(TabsRoot, {
11217
11497
  Trigger: TabsTrigger,
11218
11498
  Content: TabsContent
11219
11499
  });
11220
- var MIN_PANEL_WIDTH = 480;
11500
+ var MIN_PANEL_WIDTH = 560;
11501
+ var PANEL_STYLE = {
11502
+ "--ds-form-max": "calc(var(--ds-bp-md) - 2 * var(--ds-space-6))"
11503
+ };
11504
+ var PANEL_CLASSES = "min-w-0 @container";
11505
+ function useColumnGap(ref) {
11506
+ const [gap, setGap] = useState(0);
11507
+ useLayoutEffect(() => {
11508
+ const el = ref.current;
11509
+ if (!el) return;
11510
+ const read = () => {
11511
+ const px = parseFloat(getComputedStyle(el).columnGap);
11512
+ setGap(Number.isFinite(px) ? px : 0);
11513
+ };
11514
+ read();
11515
+ if (typeof ResizeObserver === "undefined") return;
11516
+ const observer = new ResizeObserver(read);
11517
+ observer.observe(el);
11518
+ return () => observer.disconnect();
11519
+ }, [ref]);
11520
+ return gap;
11521
+ }
11221
11522
  function SettingsLayout({
11222
11523
  sections,
11223
11524
  value,
11224
11525
  onChange,
11225
11526
  layout: layoutProp,
11226
11527
  railWidth = 220,
11227
- collapseBelow = railWidth + MIN_PANEL_WIDTH,
11528
+ collapseBelow: collapseBelowProp,
11228
11529
  "aria-label": ariaLabel = "Settings sections",
11229
11530
  children,
11230
11531
  className
@@ -11232,32 +11533,37 @@ function SettingsLayout({
11232
11533
  const shell = useAppShell();
11233
11534
  const layout = layoutProp ?? shell?.theme?.settingsLayout ?? "sidebar";
11234
11535
  const rootRef = useRef(null);
11536
+ const gap = useColumnGap(rootRef);
11537
+ const collapseBelow = collapseBelowProp ?? railWidth + gap + MIN_PANEL_WIDTH;
11235
11538
  const compact = useCompact(rootRef, collapseBelow);
11236
11539
  const tabs = sections.map((s) => ({ id: s.id, label: s.label, icon: s.icon, group: s.group, disabled: s.disabled }));
11237
11540
  const stickyTop = shell ? "var(--ds-sticky-offset, 0px)" : "calc(var(--ds-navbar-height) + var(--ds-sticky-offset, 0px))";
11238
11541
  if (layout === "tabs") {
11239
11542
  return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "tabs", className: cn("flex flex-col gap-ds-6", className), children: [
11240
11543
  /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, children: /* @__PURE__ */ jsx(Tabs, { orientation: "horizontal", mobile: "dropdown", tabs, activeTab: value, onTabChange: onChange }) }),
11241
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children })
11544
+ /* @__PURE__ */ jsx("div", { "data-settings-panel": "", className: PANEL_CLASSES, style: PANEL_STYLE, children })
11242
11545
  ] });
11243
11546
  }
11244
11547
  if (compact) {
11245
- return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "sidebar", "data-collapsed": "true", className: cn("flex flex-col gap-ds-6", className), children: [
11246
- /* @__PURE__ */ jsx(
11247
- Dropdown,
11248
- {
11249
- "aria-label": ariaLabel,
11250
- options: sections.filter((s) => !s.disabled).map((s) => ({ id: s.id, label: s.label, group: s.group })),
11251
- value,
11252
- onChange: (v) => onChange(v)
11253
- }
11254
- ),
11255
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children })
11256
- ] });
11548
+ return (
11549
+ // gap-x-ds-8 is the rail's gap, unused in one column: the threshold reads the same gap as the row (useColumnGap)
11550
+ /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "sidebar", "data-collapsed": "true", className: cn("flex flex-col gap-y-ds-6 gap-x-ds-8", className), children: [
11551
+ /* @__PURE__ */ jsx(
11552
+ Dropdown,
11553
+ {
11554
+ "aria-label": ariaLabel,
11555
+ options: sections.filter((s) => !s.disabled).map((s) => ({ id: s.id, label: s.label, group: s.group })),
11556
+ value,
11557
+ onChange: (v) => onChange(v)
11558
+ }
11559
+ ),
11560
+ /* @__PURE__ */ jsx("div", { "data-settings-panel": "", className: PANEL_CLASSES, style: PANEL_STYLE, children })
11561
+ ] })
11562
+ );
11257
11563
  }
11258
11564
  return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "sidebar", className: cn("flex flex-row items-start gap-ds-8", className), children: [
11259
11565
  /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, className: "sticky self-start shrink-0", style: { width: `${railWidth}px`, top: stickyTop }, children: /* @__PURE__ */ jsx(Tabs, { orientation: "vertical", tabs, activeTab: value, onTabChange: onChange, className: "w-full" }) }),
11260
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children })
11566
+ /* @__PURE__ */ jsx("div", { "data-settings-panel": "", className: cn("flex-1", PANEL_CLASSES), style: PANEL_STYLE, children })
11261
11567
  ] });
11262
11568
  }
11263
11569
  function SeparatorIcon({ type, size }) {
@@ -12969,6 +13275,16 @@ var widthToMaxWidth = {
12969
13275
  full: "full",
12970
13276
  narrow: "md"
12971
13277
  };
13278
+ var dataWidthOf = {
13279
+ sm: "sm",
13280
+ md: "narrow",
13281
+ lg: "lg",
13282
+ xl: "constrained",
13283
+ full: "full"
13284
+ };
13285
+ function pageMaxWidth(resolved) {
13286
+ return resolved === "md" ? `var(--ds-page-max, ${maxWidthValues.md})` : maxWidthValues[resolved];
13287
+ }
12972
13288
  var paddingClasses = {
12973
13289
  none: "",
12974
13290
  default: "px-ds-6",
@@ -13053,6 +13369,7 @@ var PageContainer = forwardRef(function PageContainer2({
13053
13369
  className,
13054
13370
  renderHeader
13055
13371
  }, ref) {
13372
+ if (REACT_VERSION_PROBLEM) throw new Error(REACT_VERSION_PROBLEM);
13056
13373
  const shell = useAppShell();
13057
13374
  const rootRef = useRef(null);
13058
13375
  const setRoot = useCallback((node) => {
@@ -13065,7 +13382,8 @@ var PageContainer = forwardRef(function PageContainer2({
13065
13382
  else rootRef.current?.style.removeProperty("--ds-sticky-offset");
13066
13383
  }, []);
13067
13384
  const resolvedMaxWidth = width ? widthToMaxWidth[width] : maxWidth;
13068
- const maxWidthValue = maxWidthValues[resolvedMaxWidth];
13385
+ const maxWidthValue = pageMaxWidth(resolvedMaxWidth);
13386
+ const dataWidth = dataWidthOf[resolvedMaxWidth];
13069
13387
  const [requestedWidth, setRequestedWidth] = useState(null);
13070
13388
  const requestContentWidth = useCallback((w) => setRequestedWidth(w), []);
13071
13389
  const effectiveContentWidth = contentWidth ?? requestedWidth ?? void 0;
@@ -13129,6 +13447,7 @@ var PageContainer = forwardRef(function PageContainer2({
13129
13447
  {
13130
13448
  ref: setRoot,
13131
13449
  "data-page-container": wrappedHeader ? "" : void 0,
13450
+ "data-width": dataWidth,
13132
13451
  className: cn(
13133
13452
  "w-full",
13134
13453
  paddingClasses[padding],
@@ -13160,7 +13479,7 @@ var PageContainer = forwardRef(function PageContainer2({
13160
13479
  ...overlapActive ? { marginTop: "calc(-1 * var(--ds-overlap, 0px))", position: "relative", zIndex: 1 } : {}
13161
13480
  } : void 0;
13162
13481
  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) }) });
13163
- return /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-page-container": "", className: "w-full", children: [
13482
+ return /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-page-container": "", "data-width": dataWidth, className: "w-full", children: [
13164
13483
  placement === "block" ? /* @__PURE__ */ jsx("div", { style: floatingBlockPadding(r.navbarFloating, r.sidebarFloating, overlapActive), children: /* @__PURE__ */ jsxs("div", { className: FLOATING_BLOCK, children: [
13165
13484
  strip,
13166
13485
  colourful ? header : /* @__PURE__ */ jsx("div", { className: columnClass(padding), style: columnStyle, children: header })
@@ -13421,7 +13740,7 @@ var PageHeader = forwardRef(function PageHeader2({
13421
13740
  }
13422
13741
  let header;
13423
13742
  if (variant === "minimal") {
13424
- header = /* @__PURE__ */ jsx(MinimalHeader, { title, caption, focusBack, badge, breadcrumbs: inlineBreadcrumbs, actions, tabs, breadcrumbPill: bcPill, breadcrumbSeparator: bcSep, as: Heading, className, headerRef: floating ? void 0 : ref, isFloating: floating, contentMaxWidth: gutterWidth });
13743
+ header = /* @__PURE__ */ jsx(MinimalHeader, { title, description, caption, focusBack, badge, breadcrumbs: inlineBreadcrumbs, actions, tabs, breadcrumbPill: bcPill, breadcrumbSeparator: bcSep, as: Heading, className, headerRef: floating ? void 0 : ref, isFloating: floating, contentMaxWidth: gutterWidth });
13425
13744
  } else if (variant === "tabs-integrated") {
13426
13745
  header = /* @__PURE__ */ jsx(
13427
13746
  TabsIntegratedHeader,
@@ -13596,6 +13915,7 @@ function DefaultHeader({
13596
13915
  }
13597
13916
  function MinimalHeader({
13598
13917
  title,
13918
+ description,
13599
13919
  caption,
13600
13920
  focusBack,
13601
13921
  badge,
@@ -13623,6 +13943,7 @@ function MinimalHeader({
13623
13943
  ] }),
13624
13944
  actions && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }) })
13625
13945
  ] }),
13946
+ description && /* @__PURE__ */ jsx(ClampedText, { className: "mt-ds-1 text-sm text-[var(--ds-text-secondary)]", children: description }),
13626
13947
  tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-3 -mx-ds-1", children: tabs })
13627
13948
  ] }) });
13628
13949
  }
@@ -13911,6 +14232,7 @@ function SnappingHeader({
13911
14232
  ] }),
13912
14233
  actions && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }) })
13913
14234
  ] }),
14235
+ description && /* @__PURE__ */ jsx(ClampedText, { className: "mt-ds-1 text-sm text-[var(--ds-text-secondary)]", children: description }),
13914
14236
  filters && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center flex-wrap gap-ds-3 mt-ds-3", children: filters }) }),
13915
14237
  expandedContent && /* @__PURE__ */ jsx("div", { className: "mt-ds-3", children: expandedContent }),
13916
14238
  tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-3 -mx-ds-1", children: tabs })
@@ -16028,7 +16350,9 @@ function Toast({ variant = "default", message, visible, onDismiss, duration = 5e
16028
16350
  var ToastContext = createContext(null);
16029
16351
  function useToast() {
16030
16352
  const ctx = useContext(ToastContext);
16031
- if (!ctx) throw new Error("useToast must be used within <ToastProvider>");
16353
+ if (!ctx) {
16354
+ throw new Error("useToast must be used within <ToastProvider>: the app shell's (get_app_shell writes it around AppShell). A page outside the shell (sign-in, a public page) shows its messages inline instead: an Alert above the form, and each field's error below the field.");
16355
+ }
16032
16356
  return ctx;
16033
16357
  }
16034
16358
  function useToastOptional() {
@@ -16271,6 +16595,7 @@ var SectionHeading = forwardRef(function SectionHeading2({
16271
16595
  "div",
16272
16596
  {
16273
16597
  ref,
16598
+ "data-gs": "section-heading",
16274
16599
  "data-variant": variant,
16275
16600
  className: cn(
16276
16601
  pane ? "shrink-0 px-ds-4 pt-ds-4 pb-ds-3 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]" : "mb-[var(--ds-heading-gap)]",
@@ -16286,7 +16611,7 @@ var SectionHeading = forwardRef(function SectionHeading2({
16286
16611
  ), children: [
16287
16612
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
16288
16613
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
16289
- /* @__PURE__ */ jsx("h2", { className: "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] truncate", children: title }),
16614
+ /* @__PURE__ */ jsx("h2", { className: cn("font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", pane ? "truncate" : "min-w-0 break-words"), children: title }),
16290
16615
  badge,
16291
16616
  count !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "neutral", size: "sm", children: count })
16292
16617
  ] }),