gridsmith-ui 0.17.2 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
19
19
  import { useLatest } from './chunk-WH2FUFAC.js';
20
20
  import { Icon } from './chunk-4LDYR7QP.js';
21
21
  import { cn, GLASS_SURFACE, ACTIVE_RING, GLASS_BACKDROP, GLASS_DROPDOWN, lockBodyScroll, unlockBodyScroll, GLASS_OVERLAY, ERROR_ACTIVE_RING, ERROR_FOCUS_RING, FOCUS_RING, ERROR_HAS_FOCUS_RING, HAS_FOCUS_RING, ERROR_FOCUS_WITHIN_RING, FOCUS_WITHIN_RING } from './chunk-4KVFH5KC.js';
22
- import { createContext, forwardRef, useState, useRef, useCallback, useLayoutEffect, useMemo, useContext, useId, memo, useEffect, useImperativeHandle, Children, isValidElement, cloneElement, Fragment as Fragment$1, useSyncExternalStore } from 'react';
22
+ import { createContext, forwardRef, useState, useRef, useCallback, useLayoutEffect, useMemo, useContext, Children, isValidElement, useId, memo, useEffect, useImperativeHandle, Fragment as Fragment$1, cloneElement, useSyncExternalStore } from 'react';
23
23
  import { createPortal, flushSync } from 'react-dom';
24
24
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
25
25
 
@@ -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
@@ -4899,6 +4907,17 @@ function MentionInput({
4899
4907
  )
4900
4908
  ] });
4901
4909
  }
4910
+ var HEADER_LAYOUT = {
4911
+ row: "flex flex-wrap items-start justify-between gap-ds-4",
4912
+ block: "flex flex-col space-y-ds-1.5 grow basis-1/2 [min-width:min-content] break-words",
4913
+ titleLeads: "grow basis-1/2 max-w-max [min-width:min-content] break-words",
4914
+ actions: "flex items-center gap-ds-2 shrink-0 ml-auto",
4915
+ below: "mt-ds-1.5"
4916
+ };
4917
+ function actionsAreBadges(actions) {
4918
+ const items = Children.toArray(actions);
4919
+ return items.length > 0 && items.every((item) => isValidElement(item) && (item.type === Fragment$1 ? actionsAreBadges(item.props.children) : item.type === Badge));
4920
+ }
4902
4921
  var CardCtx = createContext(false);
4903
4922
  var CardActionCtx = createContext(null);
4904
4923
  var CARD_ACTION_ATTR = "data-card-action";
@@ -5141,7 +5160,7 @@ var CardHeader = forwardRef(function CardHeader2({ className, children, actions,
5141
5160
  const isCheckable = useContext(CardCtx);
5142
5161
  const hasActions = actions || menuItems;
5143
5162
  const pl = isCheckable ? "pl-ds-10" : "pl-ds-5";
5144
- const actionsBlock = hasActions ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 shrink-0 ml-auto", children: [
5163
+ const actionsBlock = hasActions ? /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.actions, children: [
5145
5164
  actions,
5146
5165
  menuItems && /* @__PURE__ */ jsx(
5147
5166
  DropdownMenu,
@@ -5166,8 +5185,20 @@ var CardHeader = forwardRef(function CardHeader2({ className, children, actions,
5166
5185
  ] }) : null;
5167
5186
  const iconBlock = icon ? /* @__PURE__ */ jsx("span", { className: "shrink-0 h-10 w-10 rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-muted)] flex items-center justify-center mb-ds-1", children: /* @__PURE__ */ jsx(Icon, { name: icon, size: 20, className: "text-[var(--ds-text-secondary)]" }) }) : null;
5168
5187
  if (hasActions) {
5169
- return /* @__PURE__ */ jsx("div", { ref, className: cn(pl, "pr-ds-5 pt-ds-4 last:pb-ds-4", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-ds-4", children: [
5170
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-ds-1.5 grow basis-1/2 [min-width:min-content] break-words", children: [
5188
+ const parts = Children.toArray(children);
5189
+ const titleLeads = !iconBlock && !menuItems && actionsAreBadges(actions) && parts.length > 0 && isValidElement(parts[0]) && parts[0].type === CardTitle;
5190
+ if (titleLeads) {
5191
+ const below = parts.slice(1);
5192
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn(pl, "pr-ds-5 pt-ds-4 last:pb-ds-4", className), ...props, children: [
5193
+ /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
5194
+ /* @__PURE__ */ jsx("div", { "data-card-title-row": "", className: HEADER_LAYOUT.titleLeads, children: parts[0] }),
5195
+ actionsBlock
5196
+ ] }),
5197
+ below.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-ds-1.5 break-words", HEADER_LAYOUT.below), children: below })
5198
+ ] });
5199
+ }
5200
+ return /* @__PURE__ */ jsx("div", { ref, className: cn(pl, "pr-ds-5 pt-ds-4 last:pb-ds-4", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
5201
+ /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.block, children: [
5171
5202
  iconBlock,
5172
5203
  children
5173
5204
  ] }),
@@ -5381,11 +5412,27 @@ var columnClasses = {
5381
5412
  3: "grid-cols-3",
5382
5413
  4: "grid-cols-4"
5383
5414
  };
5415
+ var autoColumnClasses = {
5416
+ horizontal: {
5417
+ 2: "grid-cols-[repeat(auto-fill,minmax(max(min(20rem,100%),calc((100%_-_var(--ds-space-8))/2)),1fr))]",
5418
+ 3: "grid-cols-[repeat(auto-fill,minmax(max(min(20rem,100%),calc((100%_-_2*var(--ds-space-8))/3)),1fr))]",
5419
+ 4: "grid-cols-[repeat(auto-fill,minmax(max(min(20rem,100%),calc((100%_-_3*var(--ds-space-8))/4)),1fr))]"
5420
+ },
5421
+ vertical: {
5422
+ 2: "grid-cols-[repeat(auto-fill,minmax(max(min(8rem,100%),calc((100%_-_var(--ds-space-6))/2)),1fr))]",
5423
+ 3: "grid-cols-[repeat(auto-fill,minmax(max(min(8rem,100%),calc((100%_-_2*var(--ds-space-6))/3)),1fr))]",
5424
+ 4: "grid-cols-[repeat(auto-fill,minmax(max(min(8rem,100%),calc((100%_-_3*var(--ds-space-6))/4)),1fr))]"
5425
+ }
5426
+ };
5384
5427
  var spanClasses = {
5385
5428
  2: "col-span-2",
5386
5429
  3: "col-span-3",
5387
5430
  4: "col-span-4"
5388
5431
  };
5432
+ function spanClass(span, columns) {
5433
+ if (!span || span <= 1) return void 0;
5434
+ return span >= columns ? "col-span-full" : spanClasses[span];
5435
+ }
5389
5436
  var stackBreakpoints = {
5390
5437
  sm: "max-sm:grid-cols-1",
5391
5438
  md: "max-md:grid-cols-1",
@@ -5402,6 +5449,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5402
5449
  }, ref) {
5403
5450
  const s = sizeStyles3[size];
5404
5451
  const stackBelow = stackBelowProp ?? (columns > 1 ? "sm" : void 0);
5452
+ const templateClass = (layout) => columns > 1 && stackBelow !== "none" ? autoColumnClasses[layout][columns] : columnClasses[columns];
5405
5453
  if (orientation === "vertical") {
5406
5454
  return /* @__PURE__ */ jsx(
5407
5455
  "dl",
@@ -5409,7 +5457,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5409
5457
  ref,
5410
5458
  className: cn(
5411
5459
  "grid",
5412
- columnClasses[columns],
5460
+ templateClass("vertical"),
5413
5461
  "gap-x-ds-6 gap-y-ds-1",
5414
5462
  stackBelow && stackBelow !== "none" && stackBreakpoints[stackBelow],
5415
5463
  dividers && "divide-y-[length:var(--ds-border-width)] divide-[var(--ds-border-subtle)]",
@@ -5420,7 +5468,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5420
5468
  {
5421
5469
  className: cn(
5422
5470
  s.py,
5423
- item.span && item.span > 1 && spanClasses[item.span]
5471
+ spanClass(item.span, columns)
5424
5472
  ),
5425
5473
  children: [
5426
5474
  /* @__PURE__ */ jsxs(
@@ -5451,7 +5499,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5451
5499
  ref,
5452
5500
  className: cn(
5453
5501
  "grid",
5454
- columnClasses[columns],
5502
+ templateClass("horizontal"),
5455
5503
  "gap-x-ds-8",
5456
5504
  stackBelow && stackBelow !== "none" && stackBreakpoints[stackBelow],
5457
5505
  dividers && "divide-y-[length:var(--ds-border-width)] divide-[var(--ds-border-subtle)]",
@@ -5464,7 +5512,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5464
5512
  "flex items-start justify-between",
5465
5513
  s.py,
5466
5514
  s.gap,
5467
- item.span && item.span > 1 && spanClasses[item.span]
5515
+ spanClass(item.span, columns)
5468
5516
  ),
5469
5517
  children: [
5470
5518
  /* @__PURE__ */ jsxs(
@@ -5574,6 +5622,69 @@ function Accordion({ items, type = "single", defaultOpen = [], open: controlledO
5574
5622
  ] }, i);
5575
5623
  }) });
5576
5624
  }
5625
+ var EDGE_FADE = 24;
5626
+ var NONE = { left: false, right: false, vertical: false, gutterRight: 0, gutterBottom: 0 };
5627
+ var same = (a, b) => a.left === b.left && a.right === b.right && a.vertical === b.vertical && a.gutterRight === b.gutterRight && a.gutterBottom === b.gutterBottom;
5628
+ function useScrollEdges(options = {}) {
5629
+ const { enabled = true, fade = EDGE_FADE } = options;
5630
+ const elRef = useRef(null);
5631
+ const observer = useRef(null);
5632
+ const borders = useRef({ x: 0, y: 0 });
5633
+ const [state, setState] = useState(NONE);
5634
+ const measure = useCallback(() => {
5635
+ const el = elRef.current;
5636
+ if (!el) return;
5637
+ const next = {
5638
+ left: el.scrollLeft > 1,
5639
+ right: el.scrollLeft + el.clientWidth < el.scrollWidth - 1,
5640
+ vertical: el.scrollHeight > el.clientHeight + 1,
5641
+ gutterRight: Math.max(0, el.offsetWidth - el.clientWidth - borders.current.x),
5642
+ gutterBottom: Math.max(0, el.offsetHeight - el.clientHeight - borders.current.y)
5643
+ };
5644
+ setState((prev) => same(prev, next) ? prev : next);
5645
+ }, []);
5646
+ const ref = useCallback((el) => {
5647
+ observer.current?.disconnect();
5648
+ observer.current = null;
5649
+ elRef.current = el;
5650
+ if (!el || !enabled) return;
5651
+ const style = getComputedStyle(el);
5652
+ const px = (v) => parseFloat(v) || 0;
5653
+ borders.current = { x: px(style.borderLeftWidth) + px(style.borderRightWidth), y: px(style.borderTopWidth) + px(style.borderBottomWidth) };
5654
+ measure();
5655
+ if (typeof ResizeObserver === "undefined") return;
5656
+ const o = new ResizeObserver(measure);
5657
+ o.observe(el);
5658
+ for (const child of Array.from(el.children)) o.observe(child);
5659
+ observer.current = o;
5660
+ }, [enabled, measure]);
5661
+ const shown = enabled && (state.left || state.right);
5662
+ const { gutterRight: gr, gutterBottom: gb } = state;
5663
+ const fadeLayer = `linear-gradient(to right, ${state.left ? `transparent 0, black ${fade}px` : "black 0"}, ${state.right ? `black calc(100% - ${gr + fade}px), transparent calc(100% - ${gr}px)` : "black 100%"})`;
5664
+ const gutters = [gb > 0 && { size: `100% ${gb}px`, position: "0 100%" }, gr > 0 && { size: `${gr}px 100%`, position: "100% 0" }].filter(Boolean);
5665
+ const mask = shown ? {
5666
+ image: [fadeLayer, ...gutters.map(() => "linear-gradient(black, black)")].join(", "),
5667
+ size: ["100% 100%", ...gutters.map((g) => g.size)].join(", "),
5668
+ position: ["0 0", ...gutters.map((g) => g.position)].join(", ")
5669
+ } : void 0;
5670
+ return {
5671
+ /** The scroller's ref (a callback). */
5672
+ ref,
5673
+ /** The scroller itself, for code that moves it. */
5674
+ elRef,
5675
+ measure,
5676
+ /** Content hides past a side edge: the scroller moves sideways, and its edges fade. */
5677
+ overflowing: shown,
5678
+ /** It scrolls either way, sideways or up and down: a keyboard needs to reach it. */
5679
+ scrollable: enabled && (state.left || state.right || state.vertical),
5680
+ edges: { left: state.left, right: state.right },
5681
+ /** For the scroller: `data-fade` names the faded edges ("left", "right" or "left right"), the style masks them. */
5682
+ fadeProps: {
5683
+ "data-fade": shown ? [state.left && "left", state.right && "right"].filter(Boolean).join(" ") : void 0,
5684
+ style: mask ? { maskImage: mask.image, WebkitMaskImage: mask.image, maskSize: mask.size, WebkitMaskSize: mask.size, maskPosition: mask.position, WebkitMaskPosition: mask.position, maskRepeat: "no-repeat", WebkitMaskRepeat: "no-repeat" } : void 0
5685
+ }
5686
+ };
5687
+ }
5577
5688
  function TruncCell({ children, lines }) {
5578
5689
  const { ref, isTruncated } = useTextOverflow({ lines });
5579
5690
  const clampClass = lines === 1 ? "truncate" : `line-clamp-${lines}`;
@@ -5581,6 +5692,31 @@ function TruncCell({ children, lines }) {
5581
5692
  if (!isTruncated) return cell;
5582
5693
  return /* @__PURE__ */ jsx(Tooltip, { content: children, children: cell });
5583
5694
  }
5695
+ var TITLE_CLASSES = "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] break-words";
5696
+ var FRAME_CLASSES = cn(
5697
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
5698
+ "rounded-[var(--ds-radius-md)]",
5699
+ GLASS_SURFACE,
5700
+ "bg-[var(--ds-bg-surface)]"
5701
+ );
5702
+ function TableHeading({ title, description, actions, titleId, descriptionId, inset }) {
5703
+ const heading = /* @__PURE__ */ jsx("h3", { id: titleId, className: TITLE_CLASSES, children: title });
5704
+ const actionsBlock = actions ? /* @__PURE__ */ jsx("div", { className: HEADER_LAYOUT.actions, children: actions }) : null;
5705
+ const descriptionText = (className) => description ? /* @__PURE__ */ jsx("p", { id: descriptionId, className: cn(className, "text-sm text-[var(--ds-text-muted)] break-words"), children: description }) : null;
5706
+ return /* @__PURE__ */ jsx("div", { "data-table-heading": "", className: cn(inset ? "px-ds-5 py-ds-4" : "pb-ds-4"), children: actions && !actionsAreBadges(actions) ? /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
5707
+ /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.block, children: [
5708
+ heading,
5709
+ descriptionText()
5710
+ ] }),
5711
+ actionsBlock
5712
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5713
+ /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
5714
+ /* @__PURE__ */ jsx("div", { className: HEADER_LAYOUT.titleLeads, children: heading }),
5715
+ actionsBlock
5716
+ ] }),
5717
+ descriptionText(HEADER_LAYOUT.below)
5718
+ ] }) });
5719
+ }
5584
5720
  function Table({
5585
5721
  columns,
5586
5722
  data,
@@ -5601,8 +5737,15 @@ function Table({
5601
5737
  emptyState,
5602
5738
  onRowClick,
5603
5739
  mobileBreakpoint: mobileBreakpointProp,
5740
+ title,
5741
+ description,
5742
+ actions,
5743
+ "aria-label": ariaLabel,
5604
5744
  ref
5605
5745
  }) {
5746
+ const headingId = useId();
5747
+ const titleId = `${headingId}-title`;
5748
+ const descriptionId = `${headingId}-description`;
5606
5749
  const [internalSortKey, setInternalSortKey] = useState(null);
5607
5750
  const [internalSortDir, setInternalSortDir] = useState(null);
5608
5751
  const [expandedRows, setExpandedRows] = useState(/* @__PURE__ */ new Set());
@@ -5669,14 +5812,23 @@ function Table({
5669
5812
  const preferences = useShellPreferences();
5670
5813
  const mobileBreakpoint = mobileBreakpointProp ?? preferences.tableToCardsBreakpoint ?? "md";
5671
5814
  const isMobile = useBreakpoint(mobileBreakpoint);
5815
+ const { ref: scrollRef, measure: measureScroll, scrollable, fadeProps } = useScrollEdges({ enabled: !isMobile });
5816
+ const [regionFocused, setRegionFocused] = useState(false);
5817
+ const cardHeading = title ? /* @__PURE__ */ jsx(TableHeading, { title, description, actions, titleId, descriptionId, inset: false }) : null;
5818
+ const titledCards = (body) => /* @__PURE__ */ jsxs("div", { ref, "data-ds-list": "", "data-gs": "table", className: cn("flex flex-col", className), children: [
5819
+ cardHeading,
5820
+ body
5821
+ ] });
5672
5822
  if (isMobile) {
5673
5823
  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" }) });
5824
+ if (title) return titledCards(/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-ds-10", children: /* @__PURE__ */ jsx(Spinner, { label: "Loading table data" }) }));
5825
+ 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
5826
  }
5676
5827
  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 });
5828
+ if (title) return titledCards(/* @__PURE__ */ jsx("div", { className: "py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", children: emptyState }));
5829
+ 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
5830
  }
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) => {
5831
+ 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
5832
  const isSelected = selectedSet.has(originalIdx);
5681
5833
  return /* @__PURE__ */ jsxs(
5682
5834
  "div",
@@ -5710,171 +5862,188 @@ function Table({
5710
5862
  i
5711
5863
  );
5712
5864
  }) }) });
5865
+ return title ? titledCards(cards) : cards;
5713
5866
  }
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(
5867
+ const table = /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", ...title ? { "aria-labelledby": titleId, "aria-describedby": description ? descriptionId : void 0 } : ariaLabel ? { "aria-label": ariaLabel } : {}, children: [
5868
+ /* @__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: [
5869
+ 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(
5870
+ Checkbox,
5871
+ {
5872
+ checked: allSelected,
5873
+ indeterminate: someSelected,
5874
+ onChange: toggleAll
5875
+ }
5876
+ ) }),
5877
+ expandable && /* @__PURE__ */ jsx("th", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3") }),
5878
+ columns.map((col) => {
5879
+ const isSortable = sortable || col.sortable;
5880
+ const isActiveSort = sortKey === col.key;
5881
+ const headerKind = kindOf(col);
5882
+ const headerAlign = col.align ?? (headerKind === "number" ? "right" : "left");
5883
+ const shrink = (headerKind === "number" || headerKind === "date") && !col.maxWidth;
5884
+ return /* @__PURE__ */ jsx(
5885
+ "th",
5886
+ {
5887
+ className: cn(
5888
+ density === "compact" ? "px-ds-3 py-ds-2" : "px-ds-4 py-ds-3",
5889
+ shrink && "w-px whitespace-nowrap",
5890
+ "font-[var(--ds-weight-heading)] text-[var(--ds-text-secondary)]",
5891
+ "text-xs uppercase tracking-wider",
5892
+ headerAlign === "right" ? "text-right" : headerAlign === "center" ? "text-center" : "text-left",
5893
+ isSortable && "cursor-pointer select-none hover:text-[var(--ds-text-primary)]"
5894
+ ),
5895
+ style: col.maxWidth || col.width || col.minWidth ? { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth } : void 0,
5896
+ onClick: isSortable ? () => handleSort(col.key) : void 0,
5897
+ children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-ds-1", children: [
5898
+ col.header,
5899
+ isSortable && isActiveSort && /* @__PURE__ */ jsx(
5900
+ Icon,
5901
+ {
5902
+ name: "chevron-right",
5903
+ size: 12,
5904
+ className: cn(
5905
+ "transition-transform",
5906
+ sortDir === "asc" ? "-rotate-90" : "rotate-90"
5907
+ )
5908
+ }
5909
+ )
5910
+ ] })
5911
+ },
5912
+ col.key
5913
+ );
5914
+ })
5915
+ ] }) }),
5916
+ /* @__PURE__ */ jsx("tbody", { children: loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5917
+ "td",
5918
+ {
5919
+ colSpan: columns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
5920
+ className: "px-ds-4 py-ds-10 text-center",
5921
+ children: /* @__PURE__ */ jsx(Spinner, { label: "Loading table data" })
5922
+ }
5923
+ ) }) : data.length === 0 && emptyState ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5924
+ "td",
5925
+ {
5926
+ colSpan: columns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
5927
+ className: "px-ds-4 py-ds-10 text-center text-sm text-[var(--ds-text-muted)]",
5928
+ children: emptyState
5929
+ }
5930
+ ) }) : sortedWithIndices.map(({ row, originalIdx }, i) => {
5931
+ const isSelected = selectedSet.has(originalIdx);
5932
+ const isExpanded = expandedRows.has(i);
5933
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
5934
+ /* @__PURE__ */ jsxs(
5935
+ "tr",
5936
+ {
5937
+ className: cn(
5938
+ "border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] last:border-b-0",
5939
+ "transition-colors",
5940
+ isSelected ? "bg-[var(--ds-accent-subtle)]" : striped && i % 2 === 1 ? "bg-[var(--ds-bg-muted)]/50" : "hover:bg-[var(--ds-bg-muted)]",
5941
+ onRowClick && "cursor-pointer focus-visible:outline-none focus-visible:shadow-[inset_var(--ds-shadow-focus)]"
5942
+ ),
5943
+ onClick: onRowClick ? () => onRowClick(row, originalIdx) : void 0,
5944
+ tabIndex: onRowClick ? 0 : void 0,
5945
+ onKeyDown: onRowClick ? (e) => {
5946
+ if (e.target !== e.currentTarget) return;
5947
+ if (e.key === "Enter" || e.key === " ") {
5948
+ e.preventDefault();
5949
+ onRowClick(row, originalIdx);
5950
+ }
5951
+ } : void 0,
5952
+ children: [
5953
+ 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) }) }),
5954
+ 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(
5955
+ "button",
5956
+ {
5957
+ onClick: () => toggleExpand(i),
5958
+ className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] cursor-pointer",
5959
+ "aria-expanded": isExpanded,
5960
+ "aria-label": "Expand row",
5961
+ children: /* @__PURE__ */ jsx(
5760
5962
  Icon,
5761
5963
  {
5762
5964
  name: "chevron-right",
5763
- size: 12,
5764
- className: cn(
5765
- "transition-transform",
5766
- sortDir === "asc" ? "-rotate-90" : "rotate-90"
5767
- )
5965
+ size: 14,
5966
+ className: cn("transition-transform", isExpanded && "rotate-90")
5768
5967
  }
5769
5968
  )
5770
- ] })
5771
- },
5772
- col.key
5773
- );
5774
- })
5775
- ] }) }),
5776
- /* @__PURE__ */ jsx("tbody", { children: loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5969
+ }
5970
+ ) }),
5971
+ columns.map((col) => {
5972
+ const truncLines = col.truncate === true ? 1 : col.truncate ? col.truncate.lines : 0;
5973
+ const kind = kindOf(col);
5974
+ const alignClass = col.align ? col.align === "right" ? "text-right" : col.align === "center" ? "text-center" : "text-left" : kind === "number" ? "text-right" : "text-left";
5975
+ const cellContent = row[col.key];
5976
+ const shrink = (kind === "number" || kind === "date") && !col.maxWidth;
5977
+ let inner;
5978
+ if (truncLines > 0) {
5979
+ inner = /* @__PURE__ */ jsx(TruncCell, { lines: truncLines, children: cellContent });
5980
+ } else if (kind === "email") {
5981
+ inner = /* @__PURE__ */ jsx("span", { className: "max-w-[40ch] truncate inline-block align-bottom", title: typeof cellContent === "string" ? cellContent : void 0, children: cellContent });
5982
+ } else {
5983
+ inner = cellContent;
5984
+ }
5985
+ const align = col.align ?? (kind === "number" ? "right" : "left");
5986
+ if (align !== "left" && cellContent !== null && cellContent !== void 0 && typeof cellContent !== "string" && typeof cellContent !== "number") {
5987
+ inner = /* @__PURE__ */ jsx("div", { className: cn("flex items-center", align === "center" ? "justify-center" : "justify-end"), children: inner });
5988
+ }
5989
+ return /* @__PURE__ */ jsx(
5990
+ "td",
5991
+ {
5992
+ className: cn(
5993
+ density === "compact" ? "px-ds-3 py-ds-2" : "px-ds-4 py-ds-3",
5994
+ "text-[var(--ds-text-primary)]",
5995
+ alignClass,
5996
+ shrink && "w-px whitespace-nowrap"
5997
+ ),
5998
+ style: col.maxWidth || col.width || col.minWidth ? { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth } : void 0,
5999
+ children: inner
6000
+ },
6001
+ col.key
6002
+ );
6003
+ })
6004
+ ]
6005
+ }
6006
+ ),
6007
+ expandable && isExpanded && renderExpanded && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5777
6008
  "td",
5778
6009
  {
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" })
6010
+ colSpan: columns.length + (selectable ? 1 : 0) + 1,
6011
+ className: "bg-[var(--ds-bg-muted)] px-ds-6 py-ds-4 text-sm text-[var(--ds-text-primary)]",
6012
+ children: renderExpanded(row, i)
5782
6013
  }
5783
- ) }) : data.length === 0 && emptyState ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
5784
- "td",
6014
+ ) })
6015
+ ] }, i);
6016
+ }) })
6017
+ ] });
6018
+ const region = scrollable || regionFocused ? { tabIndex: 0, role: "region", ...title ? { "aria-labelledby": titleId } : { "aria-label": ariaLabel ?? "Table" } } : {};
6019
+ return /* @__PURE__ */ jsxs(
6020
+ "div",
6021
+ {
6022
+ ref,
6023
+ "data-ds-list": "",
6024
+ "data-gs": "table",
6025
+ className: cn("w-full min-w-0 flex flex-col", FRAME_CLASSES, "has-[>[data-table-scroll]:focus-visible]:shadow-[var(--ds-shadow-focus)]", className),
6026
+ children: [
6027
+ title && /* @__PURE__ */ jsx(TableHeading, { title, description, actions, titleId, descriptionId, inset: true }),
6028
+ /* @__PURE__ */ jsx(
6029
+ "div",
5785
6030
  {
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
6031
+ ref: scrollRef,
6032
+ "data-table-scroll": "",
6033
+ onScroll: measureScroll,
6034
+ onFocus: (e) => {
6035
+ if (e.target === e.currentTarget) setRegionFocused(true);
6036
+ },
6037
+ onBlur: (e) => {
6038
+ if (e.target === e.currentTarget) setRegionFocused(false);
6039
+ },
6040
+ ...fadeProps,
6041
+ ...region,
6042
+ className: cn("min-h-0 overflow-x-auto focus-visible:outline-none", title ? "rounded-b-[inherit]" : "rounded-[inherit]"),
6043
+ children: table
5789
6044
  }
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
- ] })
6045
+ )
6046
+ ]
5878
6047
  }
5879
6048
  );
5880
6049
  }
@@ -8386,6 +8555,7 @@ function Carousel({
8386
8555
  "div",
8387
8556
  {
8388
8557
  ref,
8558
+ "data-gs-clip": "",
8389
8559
  className: cn(
8390
8560
  "relative overflow-hidden rounded-[var(--ds-radius-md)]",
8391
8561
  "bg-[var(--ds-bg-muted)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
@@ -10193,7 +10363,7 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
10193
10363
  const shows = (i) => i === 0 ? fits(0) : `calc(${[fits(i), ...labels.slice(0, i).map((_, j) => `(1 - ${fits(j)})`)].join(" * ")})`;
10194
10364
  const named = props["aria-label"] !== void 0 || props["aria-labelledby"] !== void 0;
10195
10365
  label = /* @__PURE__ */ jsxs(Fragment, { children: [
10196
- /* @__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)) }),
10366
+ /* @__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)) }),
10197
10367
  !named && /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels[0] })
10198
10368
  ] });
10199
10369
  } else if (children != null && children !== false) {
@@ -10201,6 +10371,7 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
10201
10371
  "span",
10202
10372
  {
10203
10373
  "data-time-bar-label": "",
10374
+ "data-gs-clip": "",
10204
10375
  className: "sticky flex min-w-0 items-center gap-ds-1 overflow-hidden whitespace-nowrap",
10205
10376
  style: { ...sticky, paddingInline: INSET, opacity: `clamp(0, calc((${inView} - ${MIN_TITLE}) * 1000), 1)` },
10206
10377
  children: typeof children === "string" ? /* @__PURE__ */ jsx("span", { className: "truncate", children }) : children
@@ -10212,6 +10383,7 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
10212
10383
  {
10213
10384
  ...props,
10214
10385
  ref: barRef,
10386
+ "data-gs-clip": "",
10215
10387
  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),
10216
10388
  style: { insetInlineStart: x, width: drawnWidth, ...style },
10217
10389
  children: label
@@ -10661,7 +10833,7 @@ function Gantt({
10661
10833
  minWidth: MIN_BAR,
10662
10834
  className: cn("cursor-pointer text-xs font-medium", EDGE, tone.fill, tone.on, isSelected && SELECTED),
10663
10835
  style: { top: (ROW_HEIGHT2 - BAR_HEIGHT) / 2, bottom: "auto", height: BAR_HEIGHT },
10664
- children: place === "inside" && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex min-w-0 items-center gap-ds-1 overflow-hidden", children: title })
10836
+ 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 })
10665
10837
  }
10666
10838
  )
10667
10839
  );
@@ -11058,40 +11230,22 @@ function trackClasses(orientation) {
11058
11230
  "border-b-[length:var(--ds-nav-h-track-border)] border-[var(--ds-border-default)]"
11059
11231
  );
11060
11232
  }
11061
- var FADE = 24;
11062
11233
  function useScrollTrack(activeTab, enabled) {
11063
- const ref = useRef(null);
11064
- const [edges, setEdges] = useState({ left: false, right: false });
11065
- const measure = useCallback(() => {
11066
- const el = ref.current;
11067
- if (!el) return;
11068
- const left = el.scrollLeft > 1;
11069
- const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
11070
- setEdges((prev) => prev.left === left && prev.right === right ? prev : { left, right });
11071
- }, []);
11234
+ const { ref, elRef, measure, fadeProps } = useScrollEdges({ enabled });
11072
11235
  useLayoutEffect(() => {
11073
11236
  if (!enabled) return;
11074
- measure();
11075
- const el = ref.current;
11076
- if (!el || typeof ResizeObserver === "undefined") return;
11077
- const observer = new ResizeObserver(measure);
11078
- observer.observe(el);
11079
- return () => observer.disconnect();
11080
- }, [enabled, measure]);
11081
- useLayoutEffect(() => {
11082
- if (!enabled) return;
11083
- const el = ref.current;
11237
+ const el = elRef.current;
11084
11238
  const tab = el?.querySelector('[role="tab"][aria-selected="true"]');
11085
11239
  if (!el || !tab) return;
11086
11240
  const start = tab.offsetLeft;
11087
11241
  const end = start + tab.offsetWidth;
11088
- if (start < el.scrollLeft + FADE) el.scrollLeft = Math.max(0, start - FADE);
11089
- else if (end > el.scrollLeft + el.clientWidth - FADE) el.scrollLeft = end - el.clientWidth + FADE;
11242
+ if (start < el.scrollLeft + EDGE_FADE) el.scrollLeft = Math.max(0, start - EDGE_FADE);
11243
+ else if (end > el.scrollLeft + el.clientWidth - EDGE_FADE) el.scrollLeft = end - el.clientWidth + EDGE_FADE;
11090
11244
  measure();
11091
- }, [activeTab, enabled, measure]);
11245
+ }, [activeTab, enabled, measure, elRef]);
11092
11246
  useEffect(() => {
11093
11247
  if (!enabled) return;
11094
- const el = ref.current;
11248
+ const el = elRef.current;
11095
11249
  if (!el) return;
11096
11250
  const onWheel = (e) => {
11097
11251
  if (el.scrollWidth <= el.clientWidth || Math.abs(e.deltaY) <= Math.abs(e.deltaX)) return;
@@ -11141,14 +11295,11 @@ function useScrollTrack(activeTab, enabled) {
11141
11295
  el.removeEventListener("pointercancel", onUp);
11142
11296
  el.removeEventListener("click", onClick, true);
11143
11297
  };
11144
- }, [enabled]);
11145
- const fade = enabled ? [edges.left && "left", edges.right && "right"].filter(Boolean).join(" ") : "";
11146
- const mask = fade ? `linear-gradient(to right, ${edges.left ? `transparent 0, black ${FADE}px` : "black 0"}, ${edges.right ? `black calc(100% - ${FADE}px), transparent 100%` : "black 100%"})` : void 0;
11298
+ }, [enabled, elRef]);
11147
11299
  return {
11148
11300
  ref,
11149
11301
  onScroll: measure,
11150
- "data-fade": fade || void 0,
11151
- style: mask ? { maskImage: mask, WebkitMaskImage: mask } : void 0
11302
+ ...fadeProps
11152
11303
  };
11153
11304
  }
11154
11305
  var SCROLL_TRACK = "overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden max-w-full";
@@ -11311,6 +11462,7 @@ function TabsList({ children, className }) {
11311
11462
  {
11312
11463
  role: "tablist",
11313
11464
  ...overflow === "scroll" ? track : {},
11465
+ "data-gs-clip": overflow === "scroll" ? void 0 : "",
11314
11466
  className: cn(
11315
11467
  trackClasses("horizontal"),
11316
11468
  "max-w-full",
@@ -11415,6 +11567,7 @@ function TabsRoot(props) {
11415
11567
  {
11416
11568
  role: "tablist",
11417
11569
  ...effectiveOverflow === "scroll" ? track : {},
11570
+ "data-gs-clip": effectiveOverflow === "scroll" ? void 0 : "",
11418
11571
  className: cn(
11419
11572
  trackClasses("horizontal"),
11420
11573
  effectiveOverflow === "scroll" ? SCROLL_TRACK : "overflow-hidden",
@@ -11432,14 +11585,35 @@ var Tabs = Object.assign(TabsRoot, {
11432
11585
  Trigger: TabsTrigger,
11433
11586
  Content: TabsContent
11434
11587
  });
11435
- var MIN_PANEL_WIDTH = 480;
11588
+ var MIN_PANEL_WIDTH = 560;
11589
+ var PANEL_STYLE = {
11590
+ "--ds-form-max": "calc(var(--ds-bp-md) - 2 * var(--ds-space-6))"
11591
+ };
11592
+ var PANEL_CLASSES = "min-w-0 @container";
11593
+ function useColumnGap(ref) {
11594
+ const [gap, setGap] = useState(0);
11595
+ useLayoutEffect(() => {
11596
+ const el = ref.current;
11597
+ if (!el) return;
11598
+ const read = () => {
11599
+ const px = parseFloat(getComputedStyle(el).columnGap);
11600
+ setGap(Number.isFinite(px) ? px : 0);
11601
+ };
11602
+ read();
11603
+ if (typeof ResizeObserver === "undefined") return;
11604
+ const observer = new ResizeObserver(read);
11605
+ observer.observe(el);
11606
+ return () => observer.disconnect();
11607
+ }, [ref]);
11608
+ return gap;
11609
+ }
11436
11610
  function SettingsLayout({
11437
11611
  sections,
11438
11612
  value,
11439
11613
  onChange,
11440
11614
  layout: layoutProp,
11441
11615
  railWidth = 220,
11442
- collapseBelow = railWidth + MIN_PANEL_WIDTH,
11616
+ collapseBelow: collapseBelowProp,
11443
11617
  "aria-label": ariaLabel = "Settings sections",
11444
11618
  children,
11445
11619
  className
@@ -11447,32 +11621,37 @@ function SettingsLayout({
11447
11621
  const shell = useAppShell();
11448
11622
  const layout = layoutProp ?? shell?.theme?.settingsLayout ?? "sidebar";
11449
11623
  const rootRef = useRef(null);
11624
+ const gap = useColumnGap(rootRef);
11625
+ const collapseBelow = collapseBelowProp ?? railWidth + gap + MIN_PANEL_WIDTH;
11450
11626
  const compact = useCompact(rootRef, collapseBelow);
11451
11627
  const tabs = sections.map((s) => ({ id: s.id, label: s.label, icon: s.icon, group: s.group, disabled: s.disabled }));
11452
11628
  const stickyTop = shell ? "var(--ds-sticky-offset, 0px)" : "calc(var(--ds-navbar-height) + var(--ds-sticky-offset, 0px))";
11453
11629
  if (layout === "tabs") {
11454
11630
  return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "tabs", className: cn("flex flex-col gap-ds-6", className), children: [
11455
11631
  /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, children: /* @__PURE__ */ jsx(Tabs, { orientation: "horizontal", mobile: "dropdown", tabs, activeTab: value, onTabChange: onChange }) }),
11456
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children })
11632
+ /* @__PURE__ */ jsx("div", { "data-settings-panel": "", className: PANEL_CLASSES, style: PANEL_STYLE, children })
11457
11633
  ] });
11458
11634
  }
11459
11635
  if (compact) {
11460
- return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "sidebar", "data-collapsed": "true", className: cn("flex flex-col gap-ds-6", className), children: [
11461
- /* @__PURE__ */ jsx(
11462
- Dropdown,
11463
- {
11464
- "aria-label": ariaLabel,
11465
- options: sections.filter((s) => !s.disabled).map((s) => ({ id: s.id, label: s.label, group: s.group })),
11466
- value,
11467
- onChange: (v) => onChange(v)
11468
- }
11469
- ),
11470
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children })
11471
- ] });
11636
+ return (
11637
+ // gap-x-ds-8 is the rail's gap, unused in one column: the threshold reads the same gap as the row (useColumnGap)
11638
+ /* @__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: [
11639
+ /* @__PURE__ */ jsx(
11640
+ Dropdown,
11641
+ {
11642
+ "aria-label": ariaLabel,
11643
+ options: sections.filter((s) => !s.disabled).map((s) => ({ id: s.id, label: s.label, group: s.group })),
11644
+ value,
11645
+ onChange: (v) => onChange(v)
11646
+ }
11647
+ ),
11648
+ /* @__PURE__ */ jsx("div", { "data-settings-panel": "", className: PANEL_CLASSES, style: PANEL_STYLE, children })
11649
+ ] })
11650
+ );
11472
11651
  }
11473
11652
  return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "sidebar", className: cn("flex flex-row items-start gap-ds-8", className), children: [
11474
11653
  /* @__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" }) }),
11475
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children })
11654
+ /* @__PURE__ */ jsx("div", { "data-settings-panel": "", className: cn("flex-1", PANEL_CLASSES), style: PANEL_STYLE, children })
11476
11655
  ] });
11477
11656
  }
11478
11657
  function SeparatorIcon({ type, size }) {
@@ -13184,6 +13363,16 @@ var widthToMaxWidth = {
13184
13363
  full: "full",
13185
13364
  narrow: "md"
13186
13365
  };
13366
+ var dataWidthOf = {
13367
+ sm: "sm",
13368
+ md: "narrow",
13369
+ lg: "lg",
13370
+ xl: "constrained",
13371
+ full: "full"
13372
+ };
13373
+ function pageMaxWidth(resolved) {
13374
+ return resolved === "md" ? `var(--ds-page-max, ${maxWidthValues.md})` : maxWidthValues[resolved];
13375
+ }
13187
13376
  var paddingClasses = {
13188
13377
  none: "",
13189
13378
  default: "px-ds-6",
@@ -13281,7 +13470,8 @@ var PageContainer = forwardRef(function PageContainer2({
13281
13470
  else rootRef.current?.style.removeProperty("--ds-sticky-offset");
13282
13471
  }, []);
13283
13472
  const resolvedMaxWidth = width ? widthToMaxWidth[width] : maxWidth;
13284
- const maxWidthValue = maxWidthValues[resolvedMaxWidth];
13473
+ const maxWidthValue = pageMaxWidth(resolvedMaxWidth);
13474
+ const dataWidth = dataWidthOf[resolvedMaxWidth];
13285
13475
  const [requestedWidth, setRequestedWidth] = useState(null);
13286
13476
  const requestContentWidth = useCallback((w) => setRequestedWidth(w), []);
13287
13477
  const effectiveContentWidth = contentWidth ?? requestedWidth ?? void 0;
@@ -13345,6 +13535,7 @@ var PageContainer = forwardRef(function PageContainer2({
13345
13535
  {
13346
13536
  ref: setRoot,
13347
13537
  "data-page-container": wrappedHeader ? "" : void 0,
13538
+ "data-width": dataWidth,
13348
13539
  className: cn(
13349
13540
  "w-full",
13350
13541
  paddingClasses[padding],
@@ -13376,7 +13567,7 @@ var PageContainer = forwardRef(function PageContainer2({
13376
13567
  ...overlapActive ? { marginTop: "calc(-1 * var(--ds-overlap, 0px))", position: "relative", zIndex: 1 } : {}
13377
13568
  } : void 0;
13378
13569
  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) }) });
13379
- return /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-page-container": "", className: "w-full", children: [
13570
+ return /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-page-container": "", "data-width": dataWidth, className: "w-full", children: [
13380
13571
  placement === "block" ? /* @__PURE__ */ jsx("div", { style: floatingBlockPadding(r.navbarFloating, r.sidebarFloating, overlapActive), children: /* @__PURE__ */ jsxs("div", { className: FLOATING_BLOCK, children: [
13381
13572
  strip,
13382
13573
  colourful ? header : /* @__PURE__ */ jsx("div", { className: columnClass(padding), style: columnStyle, children: header })
@@ -13637,7 +13828,7 @@ var PageHeader = forwardRef(function PageHeader2({
13637
13828
  }
13638
13829
  let header;
13639
13830
  if (variant === "minimal") {
13640
- 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 });
13831
+ 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 });
13641
13832
  } else if (variant === "tabs-integrated") {
13642
13833
  header = /* @__PURE__ */ jsx(
13643
13834
  TabsIntegratedHeader,
@@ -13812,6 +14003,7 @@ function DefaultHeader({
13812
14003
  }
13813
14004
  function MinimalHeader({
13814
14005
  title,
14006
+ description,
13815
14007
  caption,
13816
14008
  focusBack,
13817
14009
  badge,
@@ -13839,6 +14031,7 @@ function MinimalHeader({
13839
14031
  ] }),
13840
14032
  actions && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }) })
13841
14033
  ] }),
14034
+ description && /* @__PURE__ */ jsx(ClampedText, { className: "mt-ds-1 text-sm text-[var(--ds-text-secondary)]", children: description }),
13842
14035
  tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-3 -mx-ds-1", children: tabs })
13843
14036
  ] }) });
13844
14037
  }
@@ -14127,6 +14320,7 @@ function SnappingHeader({
14127
14320
  ] }),
14128
14321
  actions && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }) })
14129
14322
  ] }),
14323
+ description && /* @__PURE__ */ jsx(ClampedText, { className: "mt-ds-1 text-sm text-[var(--ds-text-secondary)]", children: description }),
14130
14324
  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 }) }),
14131
14325
  expandedContent && /* @__PURE__ */ jsx("div", { className: "mt-ds-3", children: expandedContent }),
14132
14326
  tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-3 -mx-ds-1", children: tabs })
@@ -16489,6 +16683,7 @@ var SectionHeading = forwardRef(function SectionHeading2({
16489
16683
  "div",
16490
16684
  {
16491
16685
  ref,
16686
+ "data-gs": "section-heading",
16492
16687
  "data-variant": variant,
16493
16688
  className: cn(
16494
16689
  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)]",
@@ -16504,7 +16699,7 @@ var SectionHeading = forwardRef(function SectionHeading2({
16504
16699
  ), children: [
16505
16700
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
16506
16701
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
16507
- /* @__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 }),
16702
+ /* @__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 }),
16508
16703
  badge,
16509
16704
  count !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "neutral", size: "sm", children: count })
16510
16705
  ] }),