@neasg/design-system 0.4.22 → 0.4.23

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/README.md CHANGED
@@ -146,6 +146,15 @@ const darkThemeTokenMapping = {
146
146
  "--tooltip-foreground": "var(--nea-dark-neutral-50)",
147
147
  "--primary": "148 30% 38%",
148
148
  "--primary-foreground": "var(--nea-dark-neutral-50)",
149
+ "--interactive-foreground": "143 44% 67%",
150
+ "--highlight-background": "148 20% 18%",
151
+ "--highlight-foreground": "143 44% 67%",
152
+ "--button-primary-background": "148 30% 38%",
153
+ "--button-primary-hover-background": "148 30% 39%",
154
+ "--button-primary-foreground": "0 0% 100%",
155
+ "--count-badge-background": "144 66% 40%",
156
+ "--count-badge-foreground": "var(--nea-dark-neutral-800)",
157
+ "--instructional-foreground": "var(--nea-dark-neutral-200)",
149
158
  "--secondary": "var(--nea-dark-neutral-500)",
150
159
  "--secondary-foreground": "var(--nea-dark-neutral-100)",
151
160
  "--muted": "var(--nea-dark-neutral-500)",
package/dist/badge.js CHANGED
@@ -7,7 +7,7 @@ const badgeVariants = cva("inline-flex items-center justify-center text-center r
7
7
  variants: {
8
8
  variant: {
9
9
  secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
10
- muted: "border-transparent bg-muted text-muted-foreground",
10
+ muted: "border-transparent bg-muted text-instructional",
11
11
  destructive: "border-transparent bg-[hsl(var(--status-destructive))] text-[hsl(var(--status-destructive-foreground))]",
12
12
  outline: "text-foreground",
13
13
  success: "border-transparent bg-[hsl(var(--status-success))] text-[hsl(var(--status-success-foreground))]",
package/dist/button.js CHANGED
@@ -6,12 +6,12 @@ import { cn } from "./lib/utils";
6
6
  const buttonVariants = cva("inline-flex cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-control text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 disabled:pointer-events-none [&_svg]:h-4 [&_svg]:min-h-4 [&_svg]:w-4 [&_svg]:min-w-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
7
7
  variants: {
8
8
  variant: {
9
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
9
+ default: "bg-button-primary text-button-primary-foreground hover:bg-button-primary-hover",
10
10
  destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
11
11
  outline: "border border-[hsl(var(--button-outline-border))] bg-[hsl(var(--button-outline-background))] text-foreground hover:bg-[hsl(var(--button-outline-hover-background))] hover:text-accent-foreground",
12
12
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
13
13
  ghost: "hover:bg-accent hover:text-accent-foreground",
14
- link: "text-primary underline-offset-4 hover:text-primary/85 hover:underline",
14
+ link: "text-interactive underline-offset-4 hover:text-interactive hover:underline",
15
15
  },
16
16
  size: {
17
17
  sm: "h-8 px-2.5 text-sm",
package/dist/calendar.js CHANGED
@@ -16,7 +16,7 @@ function CalendarDayButton({ className, day, modifiers, children, disabled, onCl
16
16
  return (_jsx("button", { ref: ref, type: type, disabled: disabled, onClick: onClick, onFocus: onFocus, onBlur: onBlur, onKeyDown: onKeyDown, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, tabIndex: tabIndex, "aria-label": ariaLabel, "aria-pressed": ariaPressed, "aria-selected": ariaSelected, "data-day": day.date.toLocaleDateString(), "data-selected-single": modifiers.selected &&
17
17
  !modifiers.range_start &&
18
18
  !modifiers.range_end &&
19
- !modifiers.range_middle, "data-range-start": modifiers.range_start, "data-range-end": modifiers.range_end, "data-range-middle": modifiers.range_middle, className: cn("inline-flex cursor-pointer items-center justify-center rounded-control outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-primary/10 data-[range-middle=true]:text-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 aspect-square w-full min-w-[--cell-size] data-[range-end=true]:rounded-control data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-control group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px]", defaultClassNames.day, className), children: _jsx(Typography, { as: "span", variant: "bodySm", color: "inherit", children: children }) }));
19
+ !modifiers.range_middle, "data-range-start": modifiers.range_start, "data-range-end": modifiers.range_end, "data-range-middle": modifiers.range_middle, className: cn("inline-flex cursor-pointer items-center justify-center rounded-control outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", "data-[selected-single=true]:bg-button-primary data-[selected-single=true]:text-button-primary-foreground data-[range-middle=true]:bg-highlight data-[range-middle=true]:text-highlight-foreground data-[range-start=true]:bg-button-primary data-[range-start=true]:text-button-primary-foreground data-[range-end=true]:bg-button-primary data-[range-end=true]:text-button-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 aspect-square w-full min-w-[--cell-size] data-[range-end=true]:rounded-control data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-control group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px]", defaultClassNames.day, className), children: _jsx(Typography, { as: "span", variant: "bodySm", color: "inherit", children: children }) }));
20
20
  }
21
21
  function Calendar({ className, classNames, showOutsideDays = true, fixedWeeks = true, captionLayout = "label", buttonVariant = "ghost", formatters, components, ...props }) {
22
22
  const defaultClassNames = getDefaultClassNames();
@@ -44,9 +44,9 @@ function Calendar({ className, classNames, showOutsideDays = true, fixedWeeks =
44
44
  week_number_header: cn("w-[--cell-size] select-none", defaultClassNames.week_number_header),
45
45
  week_number: cn("text-muted-foreground select-none text-[0.8rem]", defaultClassNames.week_number),
46
46
  day: cn("group/day relative aspect-square h-full w-full select-none p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-[var(--radius-control)] [&:last-child[data-selected=true]_button]:rounded-r-[var(--radius-control)]", defaultClassNames.day),
47
- range_start: cn("bg-primary/10 rounded-l-[var(--radius-control)]", defaultClassNames.range_start),
48
- range_middle: cn("bg-primary/10 rounded-none", defaultClassNames.range_middle),
49
- range_end: cn("bg-primary/10 rounded-r-[var(--radius-control)]", defaultClassNames.range_end),
47
+ range_start: cn("bg-highlight rounded-l-[var(--radius-control)]", defaultClassNames.range_start),
48
+ range_middle: cn("bg-highlight rounded-none", defaultClassNames.range_middle),
49
+ range_end: cn("bg-highlight rounded-r-[var(--radius-control)]", defaultClassNames.range_end),
50
50
  today: cn("bg-accent text-accent-foreground rounded-control data-[selected=true]:rounded-none", defaultClassNames.today),
51
51
  outside: cn("text-muted-foreground aria-selected:text-muted-foreground", defaultClassNames.outside),
52
52
  disabled: cn("text-muted-foreground opacity-50", defaultClassNames.disabled),
package/dist/card.js CHANGED
@@ -109,7 +109,8 @@ const Card = React.forwardRef(({ title, description, variant = "default", size =
109
109
  return (_jsxs("div", { ref: ref, onClick: onClick, onKeyDown: handleKeyDown, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleFocus, onBlur: handleBlur, role: role !== null && role !== void 0 ? role : (interactive ? "button" : undefined), tabIndex: tabIndex !== null && tabIndex !== void 0 ? tabIndex : (interactive ? 0 : undefined), "aria-pressed": ariaPressed !== null && ariaPressed !== void 0 ? ariaPressed : (interactive && active !== undefined ? Boolean(active) : undefined), className: cn("group/card rounded-surface border border-[hsl(var(--card-border))] text-card-foreground", variant === "default" && "bg-card", variant === "elevated" && "bg-card shadow-sm", variant === "gradient" &&
110
110
  "border-[hsl(var(--card-gradient-border))]/30 bg-gradient-to-br from-[hsl(var(--card-gradient-from))] to-[hsl(var(--card-gradient-to))] text-[hsl(var(--card-gradient-foreground))]", active && "border-primary bg-primary/[0.04] ring-1 ring-primary/20", accent === "primary" &&
111
111
  "border-[hsl(var(--card-border))] shadow-[inset_4px_0_0_0_hsl(var(--primary))]", interactive &&
112
- "cursor-pointer transition-[border-color,background-color,box-shadow,transform] duration-150 ease-out hover:border-primary/50 hover:bg-primary/5 hover:shadow-md motion-safe:hover:-translate-y-0.5 motion-safe:active:translate-y-0 motion-safe:active:scale-[0.99] focus-visible:border-primary/50 focus-visible:bg-primary/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background", className), ...props, children: [_jsxs("div", { className: cn("relative flex flex-col gap-2", size === "default" && "p-4", size === "compact" && "p-3", showIcon && size === "default" && "pr-12", showIcon && size === "compact" && "pr-10"), children: [showIcon ? (_jsx("span", { "aria-hidden": "true", "data-slot": "card-icon", className: cn("pointer-events-none absolute right-3 top-3 inline-flex size-7 items-center justify-center rounded-control border border-[hsl(var(--card-border))] bg-background/80 text-muted-foreground shadow-sm transition-[background-color,color,transform] duration-150 ease-out group-hover/card:scale-105 group-hover/card:bg-primary/10 group-hover/card:text-primary group-focus-visible/card:bg-primary/10 group-focus-visible/card:text-primary motion-reduce:transition-colors [&_svg]:size-3.5", size === "compact" && "right-2.5 top-2.5 size-6 [&_svg]:size-3", active && "border-primary/30 bg-primary/10 text-primary", variant === "gradient" &&
112
+ "cursor-pointer transition-[border-color,background-color,box-shadow,transform] duration-150 ease-out hover:border-primary/50 hover:bg-primary/5 hover:shadow-md motion-safe:hover:-translate-y-0.5 motion-safe:active:translate-y-0 motion-safe:active:scale-[0.99] focus-visible:border-primary/50 focus-visible:bg-primary/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background", className), ...props, children: [_jsxs("div", { className: cn("relative flex flex-col gap-2", size === "default" && "p-4", size === "compact" && "p-3", showIcon && size === "default" && "pr-12", showIcon && size === "compact" && "pr-10"), children: [showIcon ? (_jsx("span", { "aria-hidden": "true", "data-slot": "card-icon", className: cn("pointer-events-none absolute right-3 top-3 inline-flex size-7 items-center justify-center rounded-control border border-[hsl(var(--card-border))] bg-background/80 text-muted-foreground shadow-sm transition-[background-color,color,transform] duration-150 ease-out group-hover/card:scale-105 group-hover/card:bg-highlight group-hover/card:text-highlight-foreground group-focus-visible/card:bg-highlight group-focus-visible/card:text-highlight-foreground motion-reduce:transition-colors [&_svg]:size-3.5", size === "compact" && "right-2.5 top-2.5 size-6 [&_svg]:size-3", active &&
113
+ "border-primary/30 bg-highlight text-highlight-foreground", variant === "gradient" &&
113
114
  "border-white/25 bg-white/10 text-current group-hover/card:bg-white/20 group-hover/card:text-current group-focus-visible/card:bg-white/20 group-focus-visible/card:text-current"), children: renderedIcon })) : null, loading ? (_jsx(Skeleton, { className: "h-3.5 w-24" })) : (renderedTitle), loading
114
115
  ? description != null && _jsx(Skeleton, { className: "h-5 w-40" })
115
116
  : renderedDescription, loading ? (stat !== undefined && _jsx(Skeleton, { className: "h-8 w-20" })) : stat ? (_jsx(Typography, { as: "p", variant: "title", color: variant === "gradient" ? "inherit" : undefined, children: stat })) : null, loading ? (subMetric !== undefined && _jsx(Skeleton, { className: "h-5 w-28" })) : subMetric ? (_jsx(Typography, { as: "p", variant: "caption", color: variant === "gradient" ? "inherit" : undefined, className: variant === "gradient" ? "opacity-80" : undefined, children: subMetric })) : null] }), children ? (_jsx("div", { className: cn(size === "default" && "p-4 pt-0", size === "compact" && "p-3 pt-0"), children: children })) : null, footer ? (_jsx("div", { className: cn("flex items-center", size === "default" && "p-4 pt-0", size === "compact" && "p-3 pt-0"), children: footer })) : null] }));
@@ -241,7 +241,7 @@ function CommandSearch({ value, onValueChange, sections, children, className, is
241
241
  if (((_c = section.action) === null || _c === void 0 ? void 0 : _c.closeOnSelect) !== false) {
242
242
  setIsOpen(false);
243
243
  }
244
- }), children: _jsx(Button, { asChild: true, variant: "link", size: "compact", className: "w-full justify-start text-left font-medium group-data-[selected=true]:underline", children: _jsxs("span", { children: [_jsx("span", { className: "min-w-0 truncate", children: section.action.label }), _jsxs("span", { className: "inline-flex shrink-0 items-center justify-center text-primary", children: [(_a = section.action.affordance) !== null && _a !== void 0 ? _a : (_jsx(ArrowRight, { className: "size-[var(--space-4)]", "aria-hidden": "true" })), _jsx("span", { className: "sr-only", children: (_b = section.action.affordanceLabel) !== null && _b !== void 0 ? _b : "Show all results in group" })] })] }) }) }, `${section.key}-action`)) : null] })] }, section.key));
244
+ }), children: _jsx(Button, { asChild: true, variant: "link", size: "compact", className: "w-full justify-start text-left font-medium group-data-[selected=true]:underline", children: _jsxs("span", { children: [_jsx("span", { className: "min-w-0 truncate", children: section.action.label }), _jsxs("span", { className: "inline-flex shrink-0 items-center justify-center text-interactive", children: [(_a = section.action.affordance) !== null && _a !== void 0 ? _a : (_jsx(ArrowRight, { className: "size-[var(--space-4)]", "aria-hidden": "true" })), _jsx("span", { className: "sr-only", children: (_b = section.action.affordanceLabel) !== null && _b !== void 0 ? _b : "Show all results in group" })] })] }) }) }, `${section.key}-action`)) : null] })] }, section.key));
245
245
  })) : (_jsx(CommandSearchEmptyState, { query: value, resultsMinHeight: resultsMinHeight }))) : (_jsx(CommandSearchEmptyState, { query: value, resultsMinHeight: resultsMinHeight })) }), shouldShowAllResults ? (_jsx(PopoverMenuFooter, { inset: "flush", children: _jsxs(PopoverMenuFooterAction, { className: "group", onClick: () => {
246
246
  onShowAllResults === null || onShowAllResults === void 0 ? void 0 : onShowAllResults(normalizedValue);
247
247
  setIsOpen(false);
@@ -17,7 +17,7 @@ function CountBadge({ count, variant = "default", size = "md", max, pulse = fals
17
17
  const variantClasses = {
18
18
  default: {
19
19
  badgeVariant: "secondary",
20
- className: "border-transparent bg-primary text-primary-foreground hover:bg-primary/90",
20
+ className: "border-transparent bg-count-badge text-count-badge-foreground",
21
21
  },
22
22
  danger: {
23
23
  badgeVariant: "destructive",
@@ -29,7 +29,7 @@ function CountBadge({ count, variant = "default", size = "md", max, pulse = fals
29
29
  },
30
30
  };
31
31
  const pulseColor = {
32
- default: "bg-primary/75",
32
+ default: "bg-count-badge/75",
33
33
  danger: "bg-[hsl(var(--status-destructive))]/70",
34
34
  muted: "bg-muted-foreground/50",
35
35
  };
@@ -166,7 +166,7 @@ const DateInput = React.forwardRef((props, ref) => {
166
166
  const disabledDays = (date) => isDateOutsideBounds(date, minDate, maxDate);
167
167
  const calendarContent = (_jsx(_Fragment, { children: props.mode === "range" ? (_jsx(Calendar, { mode: "range", selected: selectedRange, onSelect: handleRangeSelect, disabled: disabledDays, defaultMonth: (_b = selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.from) !== null && _b !== void 0 ? _b : selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.to, numberOfMonths: (_c = props.numberOfMonths) !== null && _c !== void 0 ? _c : 2, showOutsideDays: false, autoFocus: true })) : (_jsx(Calendar, { mode: "single", selected: selectedDate, onSelect: handleSelect, disabled: disabledDays, defaultMonth: selectedDate, autoFocus: true })) }));
168
168
  if (appearance === "inline") {
169
- return (_jsxs("span", { className: cn("inline-flex", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(Popover, { open: open, onOpenChange: handleOpenChange, align: "start", title: popoverTitle, surface: popoverTitle ? "menu" : "panel", contentClassName: "w-auto p-0", trigger: _jsxs("button", { ref: ref, type: "button", id: inputId, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, className: cn("inline-flex cursor-pointer items-baseline rounded-sm border-0 bg-transparent p-0 font-medium text-primary underline decoration-primary/40 underline-offset-4 transition-colors hover:decoration-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", !displayValue && "text-muted-foreground decoration-border", isInvalid && "text-destructive decoration-destructive", triggerClassName), children: [_jsxs("span", { className: "sr-only", children: [label, ": "] }), _jsx("span", { children: displayValue !== null && displayValue !== void 0 ? displayValue : placeholder })] }), children: calendarContent }), showDescription ? (_jsx("span", { id: descriptionId, className: "sr-only", children: description })) : null, error ? (_jsx("span", { id: errorId, role: "alert", className: "sr-only", children: error })) : null] }));
169
+ return (_jsxs("span", { className: cn("inline-flex", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(Popover, { open: open, onOpenChange: handleOpenChange, align: "start", title: popoverTitle, surface: popoverTitle ? "menu" : "panel", contentClassName: "w-auto p-0", trigger: _jsxs("button", { ref: ref, type: "button", id: inputId, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, className: cn("inline-flex cursor-pointer items-baseline rounded-sm border-0 bg-transparent p-0 font-medium text-interactive underline decoration-interactive/40 underline-offset-4 transition-colors hover:decoration-interactive focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", !displayValue && "text-muted-foreground decoration-border", isInvalid && "text-destructive decoration-destructive", triggerClassName), children: [_jsxs("span", { className: "sr-only", children: [label, ": "] }), _jsx("span", { children: displayValue !== null && displayValue !== void 0 ? displayValue : placeholder })] }), children: calendarContent }), showDescription ? (_jsx("span", { id: descriptionId, className: "sr-only", children: description })) : null, error ? (_jsx("span", { id: errorId, role: "alert", className: "sr-only", children: error })) : null] }));
170
170
  }
171
171
  return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: inputId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsxs("div", { className: "relative", children: [_jsx(Popover, { open: open, onOpenChange: handleOpenChange, align: "start", title: popoverTitle, surface: popoverTitle ? "menu" : "panel", contentClassName: "w-auto p-0", trigger: _jsx("button", { ref: ref, type: "button", id: inputId, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, className: cn(inputControlClassName, "relative cursor-pointer pr-9 text-left", showClearButton && "pr-16", !displayValue && "text-muted-foreground", isInvalid && "border-destructive focus-visible:ring-destructive", triggerClassName), children: _jsx("span", { className: "block truncate", children: displayValue !== null && displayValue !== void 0 ? displayValue : placeholder }) }), children: calendarContent }), _jsxs("span", { "aria-hidden": showClearButton ? undefined : true, className: "pointer-events-none absolute inset-y-0 right-3 flex items-center gap-2 text-muted-foreground", children: [showClearButton ? (_jsx("button", { type: "button", "aria-label": resolvedClearLabel, onClick: handleClear, className: "pointer-events-auto inline-flex h-4 w-4 cursor-pointer items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:cursor-not-allowed", children: _jsx(XIcon, { size: 14, className: "flex items-center justify-center" }) })) : null, _jsx(CalendarIcon, { "aria-hidden": "true", className: "h-4 w-4 shrink-0", strokeWidth: 2 })] })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
172
172
  });
@@ -6,12 +6,12 @@ import { Popover } from "./popover";
6
6
  import { PopoverAnchor, PopoverContent, PopoverRoot, PopoverTrigger, } from "./popover-primitive";
7
7
  import { cn } from "./lib/utils";
8
8
  const splitButtonDividerClassNames = {
9
- default: "before:bg-primary-foreground/30",
9
+ default: "before:bg-button-primary-foreground/30",
10
10
  destructive: "before:bg-destructive-foreground/30",
11
11
  outline: "before:bg-[hsl(var(--button-outline-border))]",
12
12
  secondary: "before:bg-secondary-foreground/20",
13
13
  ghost: "before:bg-border",
14
- link: "before:bg-primary/25",
14
+ link: "before:bg-interactive/25",
15
15
  };
16
16
  function DropdownButton({ label, leadingIcon, children, className, contentClassName, dropdownAriaLabel, menuWidth = "trigger", align = "end", alignOffset, avoidCollisions, collisionPadding, defaultOpen, disabled, hideWhenDetached, loading, loadingIndicator, loadingText, modal, onClick, onOpenChange, open, side, sideOffset = 4, split: splitProp, sticky, type = "button", variant, size, ...buttonProps }) {
17
17
  const menuContentClassName = cn("shadow-overlay-strong border-border/80 bg-popover ring-1 ring-border/50", menuWidth === "trigger" &&
@@ -92,7 +92,7 @@ export function FileUpload({ accept, maxSize = DEFAULT_MAX_FILE_SIZE, maxFiles =
92
92
  const fileInput = (_jsx("input", { ref: inputRef, type: "file", accept: accept, multiple: maxFiles > 1, onChange: handleInputChange, disabled: disabled, className: "hidden" }));
93
93
  return (_jsxs("div", { className: cn("space-y-2", className), children: [fileInput, _jsxs("div", { onDrop: handleDrop, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onClick: openFilePicker, className: cn("relative flex cursor-pointer flex-col items-center justify-center gap-1.5 rounded-surface border border-dashed p-4 transition-colors", isDragOver && !disabled
94
94
  ? "border-primary bg-primary/5"
95
- : "border-muted-foreground/25 hover:border-muted-foreground/50", disabled && "cursor-not-allowed opacity-50"), children: [_jsx(UploadIcon, { className: cn(isDragOver ? "text-primary" : "text-muted-foreground"), size: 28 }), _jsxs("div", { className: "text-center", children: [_jsx(Typography, { as: "p", variant: "label", children: isDragOver ? "Drop files here" : "Drag & drop files here" }), _jsxs(Typography, { as: "p", variant: "caption", className: "mt-1", children: ["or click to browse (max ", formatFileSize(maxSize), " each)"] })] })] }), error ? (_jsx(Typography, { as: "p", variant: "bodySm", className: "text-destructive", children: error })) : null, value.length > 0 ? (_jsx("div", { className: "space-y-2", children: value.map((file, index) => {
95
+ : "border-muted-foreground/25 hover:border-muted-foreground/50", disabled && "cursor-not-allowed opacity-50"), children: [_jsx(UploadIcon, { className: cn(isDragOver ? "text-interactive" : "text-muted-foreground"), size: 28 }), _jsxs("div", { className: "text-center", children: [_jsx(Typography, { as: "p", variant: "label", children: isDragOver ? "Drop files here" : "Drag & drop files here" }), _jsxs(Typography, { as: "p", variant: "caption", className: "mt-1", children: ["or click to browse (max ", formatFileSize(maxSize), " each)"] })] })] }), error ? (_jsx(Typography, { as: "p", variant: "bodySm", className: "text-destructive", children: error })) : null, value.length > 0 ? (_jsx("div", { className: "space-y-2", children: value.map((file, index) => {
96
96
  var _a;
97
97
  const progress = uploadProgress === null || uploadProgress === void 0 ? void 0 : uploadProgress.get(file.name);
98
98
  const isUploading = (progress === null || progress === void 0 ? void 0 : progress.status) === "uploading";
@@ -9,7 +9,7 @@ const filterToggleVariants = cva("inline-flex shrink-0 cursor-pointer select-non
9
9
  muted: "",
10
10
  },
11
11
  selected: {
12
- true: "border-transparent bg-primary/10 text-primary hover:bg-primary/20 focus-visible:bg-primary/20",
12
+ true: "border-transparent bg-highlight text-highlight-foreground hover:bg-highlight/80 focus-visible:bg-highlight/80",
13
13
  false: "border-transparent bg-muted text-muted-foreground hover:bg-muted/80",
14
14
  },
15
15
  },
package/dist/index.d.ts CHANGED
@@ -131,7 +131,7 @@ export { TableColumnVisibilityMenu } from "./table-column-visibility";
131
131
  export type { TableColumnVisibilityMenuProps } from "./table-column-visibility";
132
132
  export { useTableSort } from "./use-table-sort";
133
133
  export type { UseTableSortOptions, UseTableSortReturn, } from "./use-table-sort";
134
- export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
134
+ export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableRowHeader, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
135
135
  export type { TableColumn, TableColumnWidths, TableProps, TableResizeMode, TableRowExpansionProps, TableRowTone, TableSortDirection, TableSortingProps, TableSkeletonConfig, TableRowSkeletonProps, } from "./table";
136
136
  export type { TableColumnFilter, TableColumnFilterOption, TableDateColumnFilter, TableOptionsColumnFilter, TableTextColumnFilter, } from "./table-column-filter";
137
137
  export { Tabs, SubTabs, TabsRoot, TabWithDropdown, type TabsItem, type TabsItemLabel, type TabsItemLabelProps, type TabsProps, type SubTabsItem, type SubTabsProps, TabsList, TabsTrigger, TabsContent, type TabWithDropdownProps, useTabsContext, type TabDropdownSection, } from "./tabs";
package/dist/index.js CHANGED
@@ -68,7 +68,7 @@ export { Pagination } from "./pagination";
68
68
  export { TableToolbar } from "./table-toolbar";
69
69
  export { TableColumnVisibilityMenu } from "./table-column-visibility";
70
70
  export { useTableSort } from "./use-table-sort";
71
- export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
71
+ export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableRowHeader, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
72
72
  export { Tabs, SubTabs, TabsRoot, TabWithDropdown, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
73
73
  export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, } from "./tooltip";
74
74
  export { Toaster, toast } from "./toaster";
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
2
  declare const inputControlPaddingClassName = "px-3 py-2";
3
- declare const inputControlClassName = "h-control w-full min-w-0 rounded-control border border-input bg-transparent px-3 py-2 text-sm transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20";
3
+ declare const inputControlClassName = "h-control w-full min-w-0 rounded-control border border-input bg-transparent px-3 py-2 text-sm transition-[color,box-shadow] outline-none selection:bg-highlight selection:text-highlight-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20";
4
4
  declare const InputControl: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
5
5
  export { InputControl, inputControlClassName, inputControlPaddingClassName };
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cn } from "./lib/utils";
4
4
  const inputControlPaddingClassName = "px-3 py-2";
5
- const inputControlClassName = `h-control w-full min-w-0 rounded-control border border-input bg-transparent ${inputControlPaddingClassName} text-sm transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20`;
5
+ const inputControlClassName = `h-control w-full min-w-0 rounded-control border border-input bg-transparent ${inputControlPaddingClassName} text-sm transition-[color,box-shadow] outline-none selection:bg-highlight selection:text-highlight-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20`;
6
6
  const InputControl = React.forwardRef(({ className, type = "text", ...props }, ref) => (_jsx("input", { ref: ref, type: type, "data-slot": "input", className: cn(inputControlClassName, className), ...props })));
7
7
  InputControl.displayName = "InputControl";
8
8
  export { InputControl, inputControlClassName, inputControlPaddingClassName };
package/dist/link.js CHANGED
@@ -5,8 +5,8 @@ import { cn } from "./lib/utils";
5
5
  const linkVariants = cva("rounded-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", {
6
6
  variants: {
7
7
  variant: {
8
- default: "text-primary underline-offset-4 hover:text-primary/85 hover:underline",
9
- subtle: "text-foreground underline-offset-4 hover:text-primary hover:underline",
8
+ default: "text-interactive underline-offset-4 hover:text-interactive hover:underline",
9
+ subtle: "text-foreground underline-offset-4 hover:text-interactive hover:underline",
10
10
  inherit: "text-inherit no-underline hover:no-underline",
11
11
  },
12
12
  },
@@ -43,7 +43,7 @@ export function MessageNewDivider({ children = "New Message", className, }) {
43
43
  }
44
44
  export function MessageAttachmentRow({ attachment, downloadUrl, downloadLabel = `Download ${attachment.name}`, className, ...props }) {
45
45
  const renderContent = (isInteractive) => (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground", children: _jsx(FileText, { className: "h-4 w-4" }) }), _jsxs("div", { className: "min-w-0 pr-1", children: [_jsx(Typography, { as: "p", variant: "bodySm", className: "line-clamp-2 font-medium", children: attachment.name }), attachment.size ? (_jsx(Typography, { as: "p", variant: "caption", children: attachment.size })) : null] }), _jsx("span", { "aria-hidden": "true", className: cn("flex h-8 w-8 shrink-0 items-center justify-center rounded-control border border-border bg-background text-muted-foreground transition-colors", isInteractive
46
- ? "group-hover:border-primary/40 group-hover:bg-primary/10 group-hover:text-primary group-focus-visible:border-primary/40 group-focus-visible:bg-primary/10 group-focus-visible:text-primary"
46
+ ? "group-hover:border-primary/40 group-hover:bg-highlight group-hover:text-highlight-foreground group-focus-visible:border-primary/40 group-focus-visible:bg-highlight group-focus-visible:text-highlight-foreground"
47
47
  : "opacity-60"), children: _jsx(DownloadIcon, { size: 14 }) })] }));
48
48
  if (downloadUrl) {
49
49
  return (_jsx("a", { className: cn("group grid min-w-0 cursor-pointer grid-cols-[auto_minmax(0,1fr)_2.75rem] items-center gap-3 rounded-lg border border-border bg-card px-3 py-2 text-foreground no-underline shadow-sm transition hover:border-primary/50 hover:bg-primary/5 hover:no-underline hover:shadow-md focus-visible:border-primary/50 focus-visible:bg-primary/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background", className), href: downloadUrl, target: "_blank", rel: "noopener noreferrer", download: attachment.name, "aria-label": downloadLabel, title: downloadLabel, ...props, children: renderContent(true) }));
@@ -143,7 +143,7 @@ export function MessageItem({ message, caseId, showNewBadge = false, newMessageL
143
143
  const messageDate = (_a = message.sentAt) !== null && _a !== void 0 ? _a : message.createdAt;
144
144
  const downloadCaseId = caseId !== null && caseId !== void 0 ? caseId : message.caseId;
145
145
  const contentHtml = toHtmlContent(message.content);
146
- const richContentClassName = cn("break-words leading-6", "[&_p]:my-0 [&_p+p]:mt-2", "[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-5", "[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-5", "[&_li]:my-1", "[&_a]:text-primary [&_a]:underline [&_a:hover]:text-primary/80", "[&_h1]:mb-2 [&_h1]:mt-3 [&_h1]:text-xl [&_h1]:font-semibold", "[&_h2]:mb-2 [&_h2]:mt-3 [&_h2]:text-lg [&_h2]:font-semibold", "[&_h3]:mb-1.5 [&_h3]:mt-3 [&_h3]:text-base [&_h3]:font-semibold", "[&_h4]:mb-1.5 [&_h4]:mt-3 [&_h4]:font-semibold", "[&_h5]:mb-1.5 [&_h5]:mt-3 [&_h5]:font-semibold", "[&_h6]:mb-1.5 [&_h6]:mt-3 [&_h6]:font-semibold", "[&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground", "[&_table]:my-2 [&_table]:w-full [&_table]:border-collapse", "[&_td]:border [&_td]:border-border [&_td]:p-2", "[&_th]:border [&_th]:border-border [&_th]:bg-[hsl(var(--table-header-background))] [&_th]:p-2 [&_th]:font-semibold", "[&_pre]:my-2 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:font-mono [&_pre]:text-[0.85em]", "[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.85em]", "[&_pre_code]:bg-transparent [&_pre_code]:p-0", "[&_mark]:rounded-sm [&_mark]:bg-[hsl(var(--status-warning))] [&_mark]:px-0.5 [&_mark]:text-[hsl(var(--status-warning-foreground))]", "[&_img]:h-auto [&_img]:max-w-full", contentClassName);
146
+ const richContentClassName = cn("break-words leading-6", "[&_p]:my-0 [&_p+p]:mt-2", "[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-5", "[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-5", "[&_li]:my-1", "[&_a]:text-interactive [&_a]:underline [&_a:hover]:text-interactive", "[&_h1]:mb-2 [&_h1]:mt-3 [&_h1]:text-xl [&_h1]:font-semibold", "[&_h2]:mb-2 [&_h2]:mt-3 [&_h2]:text-lg [&_h2]:font-semibold", "[&_h3]:mb-1.5 [&_h3]:mt-3 [&_h3]:text-base [&_h3]:font-semibold", "[&_h4]:mb-1.5 [&_h4]:mt-3 [&_h4]:font-semibold", "[&_h5]:mb-1.5 [&_h5]:mt-3 [&_h5]:font-semibold", "[&_h6]:mb-1.5 [&_h6]:mt-3 [&_h6]:font-semibold", "[&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground", "[&_table]:my-2 [&_table]:w-full [&_table]:border-collapse", "[&_td]:border [&_td]:border-border [&_td]:p-2", "[&_th]:border [&_th]:border-border [&_th]:bg-[hsl(var(--table-header-background))] [&_th]:p-2 [&_th]:font-semibold", "[&_pre]:my-2 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:font-mono [&_pre]:text-[0.85em]", "[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.85em]", "[&_pre_code]:bg-transparent [&_pre_code]:p-0", "[&_mark]:rounded-sm [&_mark]:bg-[hsl(var(--status-warning))] [&_mark]:px-0.5 [&_mark]:text-[hsl(var(--status-warning-foreground))]", "[&_img]:h-auto [&_img]:max-w-full", contentClassName);
147
147
  return (_jsxs(_Fragment, { children: [showUnreadDivider && (_jsx(MessageNewDivider, { children: newMessageLabel })), _jsxs("article", { ref: messageRef, className: cn("min-w-0", className), children: [_jsxs("div", { className: "flex min-w-0 items-start justify-between gap-x-3 gap-y-1", children: [_jsx(AvatarProfile, { fallback: getInitials(initialsName), name: senderName, nameAs: "h3", nameTrailing: isApplicant ? (_jsx(Badge, { variant: "warning", bordered: true, children: "Applicant" })) : undefined, role: hasSenderProfileRole ? (_jsx("span", { "data-slot": "message-author-role", children: senderProfileRole })) : undefined, rolePlacement: "below", className: "min-w-0 flex-1 [column-gap:var(--space-3)]", avatarClassName: "h-9 w-9 shrink-0", fallbackClassName: "text-xs font-medium", nameClassName: "max-w-full whitespace-normal break-words [overflow-wrap:anywhere]" }), _jsx("time", { className: "shrink-0 whitespace-nowrap pt-0.5", dateTime: messageDate.toISOString(), children: _jsx(Typography, { as: "span", variant: "caption", className: "text-muted-foreground/60", children: formatTimestamp
148
148
  ? formatTimestamp(messageDate, message)
149
149
  : formatNotificationTimestamp(messageDate, {
@@ -162,7 +162,7 @@ function MultiSelect({ label, options, value, onValueChange, placeholder = "Sele
162
162
  var _a;
163
163
  event.preventDefault();
164
164
  (_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
165
- }, children: label }), _jsxs(Popover, { open: disabled ? false : open, onOpenChange: handleOpenChange, align: "start", sideOffset: 4, collisionPadding: 16, surface: "menu", tabBehavior: "close", contentClassName: "flex max-h-[var(--radix-popover-content-available-height)] w-[var(--radix-popover-trigger-width)] min-w-[var(--radix-popover-trigger-width)] flex-col overflow-hidden", onOpenAutoFocus: (event) => event.preventDefault(), trigger: _jsxs("div", { ref: triggerRef, role: "combobox", id: selectId, tabIndex: disabled ? -1 : 0, onKeyDown: handleTriggerKeyDown, "aria-expanded": open, "aria-controls": listboxId, "aria-haspopup": "listbox", "aria-invalid": isInvalid ? "true" : undefined, "aria-disabled": disabled ? "true" : undefined, "aria-labelledby": labelId, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, className: cn("relative flex h-control w-full cursor-pointer items-center gap-1.5 overflow-hidden rounded-control border border-input bg-background px-2.5 py-1.5 pr-8 text-sm ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", isInvalid ? "border-destructive" : "border-input", disabled && "cursor-not-allowed opacity-50", triggerClassName), children: [selectedLabels.length > 0 ? (_jsxs(_Fragment, { children: [selectedLabels.slice(0, maxVisibleTags).map((opt) => (_jsxs(Badge, { variant: "secondary", className: "max-w-[10rem] shrink-0 justify-start gap-1 overflow-visible text-sm font-medium", children: [_jsx("span", { className: "min-w-0 truncate", children: opt.label }), _jsx("button", { type: "button", className: "inline-flex cursor-pointer items-center justify-center rounded-sm text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: (event) => {
165
+ }, children: label }), _jsxs(Popover, { open: disabled ? false : open, onOpenChange: handleOpenChange, align: "start", sideOffset: 4, collisionPadding: 16, surface: "menu", tabBehavior: "close", contentClassName: "flex max-h-[var(--radix-popover-content-available-height)] w-[var(--radix-popover-trigger-width)] min-w-[var(--radix-popover-trigger-width)] flex-col overflow-hidden", onOpenAutoFocus: (event) => event.preventDefault(), trigger: _jsxs("div", { ref: triggerRef, role: "combobox", id: selectId, tabIndex: disabled ? -1 : 0, onKeyDown: handleTriggerKeyDown, "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-haspopup": "listbox", "aria-invalid": isInvalid ? "true" : undefined, "aria-disabled": disabled ? "true" : undefined, "aria-labelledby": labelId, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, className: cn("relative flex h-control w-full cursor-pointer items-center gap-1.5 overflow-hidden rounded-control border border-input bg-background px-2.5 py-1.5 pr-8 text-sm ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", isInvalid ? "border-destructive" : "border-input", disabled && "cursor-not-allowed opacity-50", triggerClassName), children: [selectedLabels.length > 0 ? (_jsxs(_Fragment, { children: [selectedLabels.slice(0, maxVisibleTags).map((opt) => (_jsxs(Badge, { variant: "secondary", className: "max-w-[10rem] shrink-0 justify-start gap-1 overflow-visible text-sm font-medium", children: [_jsx("span", { className: "min-w-0 truncate", children: opt.label }), _jsx("button", { type: "button", className: "inline-flex cursor-pointer items-center justify-center rounded-sm text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: (event) => {
166
166
  event.stopPropagation();
167
167
  removeOption(opt.value);
168
168
  }, "aria-label": `Remove ${opt.label}`, children: _jsx(XIcon, { size: 12 }) })] }, opt.value))), selectedLabels.length > maxVisibleTags ? (_jsxs(Badge, { variant: "secondary", className: "shrink-0 text-sm font-medium", title: selectedLabels
@@ -171,7 +171,7 @@ function MultiSelect({ label, options, value, onValueChange, placeholder = "Sele
171
171
  .join(", "), children: ["+", selectedLabels.length - maxVisibleTags] })) : null] })) : (_jsx("span", { className: "text-muted-foreground", children: placeholder })), _jsx(ChevronDownIcon, { "aria-hidden": "true", size: 16, className: cn("pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 shrink-0 text-muted-foreground transition-transform duration-150", open && "rotate-180") })] }), children: [searchable ? (_jsx(PopoverMenuSearch, { ref: searchInputRef, value: searchQuery, onChange: (event) => setSearchQuery(event.target.value), onKeyDown: handleSearchKeyDown, placeholder: searchPlaceholder, "aria-label": searchPlaceholder, className: "h-control border-0 bg-transparent px-2 py-0 text-sm shadow-none" })) : null, _jsx("div", { ref: listboxRef, id: listboxId, role: "listbox", "aria-label": typeof label === "string" ? label : "Options", "aria-multiselectable": "true", className: "max-h-64 overflow-y-auto", onKeyDown: handleListboxKeyDown, children: filteredOptions.length > 0 ? (filteredOptions.map((option) => {
172
172
  const isSelected = value.includes(option.value);
173
173
  return (_jsxs("button", { type: "button", role: "option", "aria-selected": isSelected, disabled: option.disabled, tabIndex: -1, onClick: () => toggleOption(option.value), className: cn("flex min-h-8 w-full cursor-pointer items-center gap-1.5 rounded-sm px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50", isSelected &&
174
- "bg-primary/10 text-primary hover:bg-primary/20 hover:text-primary focus:bg-primary/20 focus:text-primary"), children: [_jsx(Checkbox, { checked: isSelected, disabled: option.disabled, tabIndex: -1, readOnly: true, "aria-hidden": "true", className: "mt-0.5" }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx(SearchHighlight, { text: option.label, query: searchQuery }), option.description ? (_jsx(Typography, { as: "span", variant: "caption", children: _jsx(SearchHighlight, { text: option.description, query: searchQuery }) })) : null] })] }, option.value));
174
+ "bg-highlight text-highlight-foreground hover:bg-highlight/80 hover:text-highlight-foreground focus:bg-highlight/80 focus:text-highlight-foreground"), children: [_jsx(Checkbox, { checked: isSelected, disabled: option.disabled, tabIndex: -1, readOnly: true, "aria-hidden": "true", className: "mt-0.5" }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx(SearchHighlight, { text: option.label, query: searchQuery }), option.description ? (_jsx(Typography, { as: "span", variant: "caption", children: _jsx(SearchHighlight, { text: option.description, query: searchQuery }) })) : null] })] }, option.value));
175
175
  })) : (_jsx("div", { className: "px-2.5 py-1.5 text-center text-sm text-muted-foreground", children: "No options found." })) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
176
176
  }
177
177
  export { MultiSelect };
@@ -1,9 +1,11 @@
1
1
  import * as React from "react";
2
2
  export interface PageHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
3
3
  title: React.ReactNode;
4
+ /** Semantic heading level for the page title. Use 1 for top-level routed pages. */
5
+ headingLevel?: 1 | 2;
4
6
  subtitle?: React.ReactNode;
5
7
  breadcrumbs?: React.ReactNode;
6
8
  children?: React.ReactNode;
7
9
  }
8
- declare function PageHeader({ title, subtitle, breadcrumbs, children, className, ...props }: PageHeaderProps): import("react/jsx-runtime").JSX.Element;
10
+ declare function PageHeader({ title, headingLevel, subtitle, breadcrumbs, children, className, ...props }: PageHeaderProps): import("react/jsx-runtime").JSX.Element;
9
11
  export { PageHeader };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Typography } from "./typography";
3
- function PageHeader({ title, subtitle, breadcrumbs, children, className, ...props }) {
4
- return (_jsx("div", { className: className, ...props, children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [breadcrumbs ? _jsx("div", { children: breadcrumbs }) : null, _jsx("div", { className: "flex items-center gap-2", children: _jsx(Typography, { as: "h2", variant: "title", children: title }) }), subtitle ? (_jsx(Typography, { as: "p", variant: "caption", children: subtitle })) : null] }), children] }) }));
3
+ function PageHeader({ title, headingLevel = 2, subtitle, breadcrumbs, children, className, ...props }) {
4
+ return (_jsx("div", { className: className, ...props, children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [breadcrumbs ? _jsx("div", { children: breadcrumbs }) : null, _jsx("div", { className: "flex items-center gap-2", children: _jsx(Typography, { as: headingLevel === 1 ? "h1" : "h2", variant: "title", children: title }) }), subtitle ? (_jsx(Typography, { as: "p", variant: "caption", children: subtitle })) : null] }), children] }) }));
5
5
  }
6
6
  export { PageHeader };
@@ -7,6 +7,12 @@ export interface PaginationProps extends React.HTMLAttributes<HTMLDivElement> {
7
7
  onPageChange: (page: number) => void;
8
8
  onItemsPerPageChange?: (itemsPerPage: number) => void;
9
9
  itemsPerPageOptions?: number[];
10
+ /** Accessible name for the page-navigation landmark. */
11
+ navigationLabel?: string;
12
+ /** Visible label for the page-size control. */
13
+ itemsPerPageLabel?: string;
14
+ /** Accessible name for the page-size control. Defaults to `itemsPerPageLabel`. */
15
+ itemsPerPageAriaLabel?: string;
10
16
  }
11
- declare function Pagination({ currentPage, totalPages, totalItems, itemsPerPage, onPageChange, onItemsPerPageChange, itemsPerPageOptions, className, ...props }: PaginationProps): import("react/jsx-runtime").JSX.Element;
17
+ declare function Pagination({ currentPage, totalPages, totalItems, itemsPerPage, onPageChange, onItemsPerPageChange, itemsPerPageOptions, navigationLabel, itemsPerPageLabel, itemsPerPageAriaLabel, className, ...props }: PaginationProps): import("react/jsx-runtime").JSX.Element;
12
18
  export { Pagination };
@@ -13,13 +13,14 @@ const PaginationContent = React.forwardRef(({ className, ...props }, ref) => (_j
13
13
  PaginationContent.displayName = "PaginationContent";
14
14
  const PaginationItem = React.forwardRef(({ className, ...props }, ref) => (_jsx("li", { ref: ref, className: cn("", className), ...props })));
15
15
  PaginationItem.displayName = "PaginationItem";
16
- function Pagination({ currentPage, totalPages, totalItems, itemsPerPage = 5, onPageChange, onItemsPerPageChange, itemsPerPageOptions = [5, 10, 20, 50, 100], className, ...props }) {
16
+ function Pagination({ currentPage, totalPages, totalItems, itemsPerPage = 5, onPageChange, onItemsPerPageChange, itemsPerPageOptions = [5, 10, 20, 50, 100], navigationLabel = "Pagination", itemsPerPageLabel = "Rows per page", itemsPerPageAriaLabel, className, ...props }) {
17
+ const itemsPerPageLabelId = React.useId();
17
18
  const normalizedTotalPages = totalPages || 1;
18
19
  const startItem = totalItems === 0 ? 0 : (currentPage - 1) * itemsPerPage + 1;
19
20
  const endItem = Math.min(currentPage * itemsPerPage, totalItems);
20
- return (_jsxs("div", { className: cn("flex items-center justify-between gap-4", className), ...props, children: [_jsxs("div", { className: "text-sm text-muted-foreground", children: ["Showing ", startItem, "-", endItem, " out of ", totalItems] }), _jsxs("div", { className: "flex items-center gap-4", children: [onItemsPerPageChange ? (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Typography, { as: "span", variant: "caption", children: "Rows per page" }), _jsxs(SelectRoot, { value: itemsPerPage.toString(), onValueChange: (value) => {
21
+ return (_jsxs("div", { "data-slot": "pagination", className: cn("flex flex-col items-start gap-3 sm:flex-row sm:items-center sm:justify-between sm:gap-4", className), ...props, children: [_jsxs("div", { className: "text-sm text-instructional", children: ["Showing ", startItem, "-", endItem, " out of ", totalItems] }), _jsxs("div", { "data-slot": "pagination-controls", className: "flex w-full flex-wrap items-center justify-between gap-x-4 gap-y-2 sm:w-auto sm:flex-nowrap sm:justify-start", children: [onItemsPerPageChange ? (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Typography, { id: itemsPerPageLabelId, as: "span", variant: "caption", children: itemsPerPageLabel }), _jsxs(SelectRoot, { value: itemsPerPage.toString(), onValueChange: (value) => {
21
22
  onItemsPerPageChange(Number(value));
22
23
  onPageChange(1);
23
- }, children: [_jsx(SelectTrigger, { className: "w-[64px] text-sm", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemsPerPageOptions.map((option) => (_jsx(SelectItem, { value: option.toString(), children: option }, option))) })] })] })) : null, _jsxs("div", { className: "text-sm text-muted-foreground", children: ["Page ", currentPage, " of ", normalizedTotalPages] }), _jsx(PaginationNav, { className: "mx-0 w-auto justify-start", children: _jsxs(PaginationContent, { children: [_jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(1), disabled: currentPage === 1, "aria-label": "Go to first page", children: _jsx(ChevronsLeft, { className: "h-4 w-4" }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(Math.max(1, currentPage - 1)), disabled: currentPage === 1, "aria-label": "Go to previous page", children: _jsx(ChevronLeftIcon, { size: 16 }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(Math.min(normalizedTotalPages, currentPage + 1)), disabled: currentPage === normalizedTotalPages || totalPages === 0, "aria-label": "Go to next page", children: _jsx(ChevronRightIcon, { size: 16 }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(normalizedTotalPages), disabled: currentPage === normalizedTotalPages || totalPages === 0, "aria-label": "Go to last page", children: _jsx(ChevronsRight, { className: "h-4 w-4" }) }) })] }) })] })] }));
24
+ }, children: [_jsx(SelectTrigger, { "aria-label": itemsPerPageAriaLabel, "aria-labelledby": itemsPerPageAriaLabel ? undefined : itemsPerPageLabelId, className: "w-[64px] text-sm", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemsPerPageOptions.map((option) => (_jsx(SelectItem, { value: option.toString(), children: option }, option))) })] })] })) : null, _jsxs("div", { className: "text-sm text-instructional", children: ["Page ", currentPage, " of ", normalizedTotalPages] }), _jsx(PaginationNav, { "aria-label": navigationLabel, className: "mx-0 w-auto justify-start", children: _jsxs(PaginationContent, { children: [_jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(1), disabled: currentPage === 1, "aria-label": "Go to first page", children: _jsx(ChevronsLeft, { className: "h-4 w-4" }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(Math.max(1, currentPage - 1)), disabled: currentPage === 1, "aria-label": "Go to previous page", children: _jsx(ChevronLeftIcon, { size: 16 }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(Math.min(normalizedTotalPages, currentPage + 1)), disabled: currentPage === normalizedTotalPages || totalPages === 0, "aria-label": "Go to next page", children: _jsx(ChevronRightIcon, { size: 16 }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => onPageChange(normalizedTotalPages), disabled: currentPage === normalizedTotalPages || totalPages === 0, "aria-label": "Go to last page", children: _jsx(ChevronsRight, { className: "h-4 w-4" }) }) })] }) })] })] }));
24
25
  }
25
26
  export { Pagination };
@@ -21,12 +21,12 @@ function PopoverMenuSection({ className, children, ...props }) {
21
21
  return (_jsx("div", { className: cn("space-y-0", className), ...props, children: children }));
22
22
  }
23
23
  function PopoverMenuItem({ className, children, active, type = "button", ...props }) {
24
- return (_jsxs(Button, { type: type, variant: "ghost", "data-slot": "popover-menu-item", className: cn("h-auto min-h-8 w-full justify-start gap-2 px-2 py-2 text-left font-normal whitespace-normal", popoverMenuItemFocusClassName, active &&
25
- "bg-primary/10 text-primary hover:bg-primary/20 hover:text-primary focus-visible:bg-primary/20 focus-visible:text-primary", className), ...props, children: [children, active ? _jsx(Check, { "aria-hidden": "true", className: "ml-auto h-4 w-4 shrink-0" }) : null] }));
24
+ return (_jsxs(Button, { type: type, variant: "ghost", "data-slot": "popover-menu-item", className: cn("h-auto min-h-8 w-full justify-start gap-2 px-2 py-2 text-left font-normal whitespace-normal aria-[selected=true]:bg-highlight aria-[selected=true]:text-highlight-foreground aria-[selected=true]:[--muted-foreground:var(--instructional-foreground)] aria-[selected=true]:hover:bg-highlight/80 aria-[selected=true]:hover:text-highlight-foreground aria-[selected=true]:focus-visible:bg-highlight/80 aria-[selected=true]:focus-visible:text-highlight-foreground", popoverMenuItemFocusClassName, active &&
25
+ "bg-highlight text-highlight-foreground [--muted-foreground:var(--instructional-foreground)] hover:bg-highlight/80 hover:text-highlight-foreground focus-visible:bg-highlight/80 focus-visible:text-highlight-foreground", className), ...props, children: [children, active ? _jsx(Check, { "aria-hidden": "true", className: "ml-auto h-4 w-4 shrink-0" }) : null] }));
26
26
  }
27
27
  function PopoverMenuCheckboxItem({ className, children, checked = false, description, disabled, onChange, readOnly, ...props }) {
28
28
  return (_jsxs("label", { className: cn("flex h-auto min-h-8 w-full cursor-pointer items-start gap-1.5 rounded-sm px-2 py-2 text-left text-popover-foreground outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-within:bg-accent focus-within:text-accent-foreground", popoverMenuItemFocusWithinClassName, checked &&
29
- "bg-primary/10 text-primary hover:bg-primary/20 hover:text-primary focus-within:bg-primary/20 focus-within:text-primary", disabled &&
29
+ "bg-highlight text-highlight-foreground hover:bg-highlight/80 hover:text-highlight-foreground focus-within:bg-highlight/80 focus-within:text-highlight-foreground", disabled &&
30
30
  "cursor-not-allowed opacity-50 hover:bg-transparent hover:text-inherit", className), children: [_jsx(Checkbox, { ...props, "data-slot": "popover-menu-item", checked: checked, disabled: disabled, readOnly: readOnly !== null && readOnly !== void 0 ? readOnly : !onChange, onChange: onChange, className: "mt-0.5" }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx(Typography, { as: "span", variant: "bodySm", className: "block", children: children }), description ? (_jsx(Typography, { as: "span", variant: "caption", className: "mt-0.5 block", children: description })) : null] })] }));
31
31
  }
32
32
  function PopoverMenuDivider({ className, ...props }) {
@@ -390,5 +390,5 @@ export function RichTextEditor({ placeholder = "Type message here...", editorAri
390
390
  }, children: color.value === "" && (_jsx("span", { className: "text-xs text-muted-foreground", children: "A" })) }, `bg-${color.name}`))) }) }), _jsx(ToolbarIconButton, { label: "Bullet list", active: editor === null || editor === void 0 ? void 0 : editor.isActive("bulletList"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleBulletList().run(), children: _jsx(ListIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Ordered list", active: editor === null || editor === void 0 ? void 0 : editor.isActive("orderedList"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleOrderedList().run(), children: _jsx(ListOrderedIcon, { size: 14 }) }), _jsx(Tooltip, { content: `Attach files (max ${maxFileSizeLabel}) - ${acceptedFileTypesLabel}`, side: "top", contentClassName: "max-w-[240px] text-center", children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-8 w-8", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, "aria-label": "Attach files", children: _jsx(PaperclipIcon, { size: 14 }) }) }), _jsx(ToolbarIconButton, { label: "Add link", active: editor === null || editor === void 0 ? void 0 : editor.isActive("link"), onClick: openLinkMenu, children: _jsx(LinkIcon, { size: 14 }) }), showTemplates && templates.length > 0 && (_jsx(_Fragment, { children: _jsxs(Popover, { open: templatePopoverOpen, onOpenChange: setTemplatePopoverOpen, align: "end", contentClassName: "w-96 p-0", trigger: _jsxs(Button, { type: "button", variant: "ghost", className: "h-8 gap-1 text-muted-foreground", children: ["Template", _jsx(ChevronDownIcon, { size: 14 })] }), children: [_jsx("div", { className: "max-h-80 overflow-y-auto", children: templatesByCategory.map(({ category, templates }) => {
391
391
  var _a;
392
392
  return (_jsxs("div", { children: [_jsx("div", { className: "sticky top-0 bg-muted px-3 py-2 text-sm font-semibold text-foreground", children: (_a = templateCategoryLabels[category]) !== null && _a !== void 0 ? _a : category }), templates.map((template) => (_jsxs("button", { type: "button", className: "w-full border-b border-border/50 px-3 py-2 text-left text-sm transition-colors last:border-0 hover:bg-muted", onClick: () => applyTemplate(template), children: [_jsx("div", { className: "truncate font-medium", children: template.name }), template.description && (_jsx("div", { className: "mt-0.5 truncate text-xs text-muted-foreground", children: template.description }))] }, template.id)))] }, category));
393
- }) }), templateCreateHref && (_jsx("div", { className: "border-t p-2", children: _jsxs("a", { href: templateCreateHref, target: "_blank", rel: "noopener noreferrer", className: "flex w-full items-center justify-center gap-1.5 rounded-md px-3 py-2 text-sm text-primary transition-colors hover:bg-muted", children: [_jsx(PlusIcon, { size: 16 }), templateCreateLabel] }) }))] }) }))] }) }), _jsx("div", { className: "bg-muted px-3 pb-2 pt-1 dark:bg-background", children: sendAction })] }));
393
+ }) }), templateCreateHref && (_jsx("div", { className: "border-t p-2", children: _jsxs("a", { href: templateCreateHref, target: "_blank", rel: "noopener noreferrer", className: "flex w-full items-center justify-center gap-1.5 rounded-md px-3 py-2 text-sm text-interactive transition-colors hover:bg-muted", children: [_jsx(PlusIcon, { size: 16 }), templateCreateLabel] }) }))] }) }))] }) }), _jsx("div", { className: "bg-muted px-3 pb-2 pt-1 dark:bg-background", children: sendAction })] }));
394
394
  }
@@ -27,7 +27,7 @@ const SelectContent = React.forwardRef(({ className, children, header, position
27
27
  SelectContent.displayName = SelectPrimitive.Content.displayName;
28
28
  const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Label, { ref: ref, "data-slot": "select-label", className: cn("px-2 py-1.5 text-xs text-muted-foreground", className), ...props })));
29
29
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
30
- const SelectItem = React.forwardRef(({ className, children, description, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, "data-slot": "select-item", className: cn("relative flex min-h-8 w-full cursor-pointer select-none items-center gap-1.5 rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[state=checked]:bg-primary/10 data-[state=checked]:text-primary data-[state=checked]:hover:bg-primary/20 data-[state=checked]:hover:text-primary data-[state=checked]:focus:bg-primary/20 data-[state=checked]:focus:text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { "data-slot": "select-item-indicator", className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx(SelectPrimitive.ItemText, { className: "break-words font-normal", children: children }), description ? (_jsx(Typography, { as: "span", variant: "caption", children: description })) : null] })] })));
30
+ const SelectItem = React.forwardRef(({ className, children, description, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, "data-slot": "select-item", className: cn("relative flex min-h-8 w-full cursor-pointer select-none items-center gap-1.5 rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[state=checked]:bg-highlight data-[state=checked]:text-highlight-foreground data-[state=checked]:hover:bg-highlight/80 data-[state=checked]:hover:text-highlight-foreground data-[state=checked]:focus:bg-highlight/80 data-[state=checked]:focus:text-highlight-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { "data-slot": "select-item-indicator", className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx(SelectPrimitive.ItemText, { className: "break-words font-normal", children: children }), description ? (_jsx(Typography, { as: "span", variant: "caption", children: description })) : null] })] })));
31
31
  SelectItem.displayName = SelectPrimitive.Item.displayName;
32
32
  const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Separator, { ref: ref, "data-slot": "select-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props })));
33
33
  SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
package/dist/spinner.js CHANGED
@@ -8,7 +8,7 @@ const SIZE_CLASS = {
8
8
  lg: "h-10 w-10 border-[3px]",
9
9
  };
10
10
  function Spinner({ size = "md", label = "Loading", className, ...props }) {
11
- return (_jsxs("div", { role: "status", "aria-live": "polite", className: cn("inline-flex items-center justify-center", className), ...props, children: [_jsx("span", { "aria-hidden": "true", className: cn("shrink-0 animate-spin rounded-full border-current border-t-transparent text-primary", SIZE_CLASS[size]) }), _jsx("span", { className: "sr-only", children: label })] }));
11
+ return (_jsxs("div", { role: "status", "aria-live": "polite", className: cn("inline-flex items-center justify-center", className), ...props, children: [_jsx("span", { "aria-hidden": "true", className: cn("shrink-0 animate-spin rounded-full border-current border-t-transparent text-interactive", SIZE_CLASS[size]) }), _jsx("span", { className: "sr-only", children: label })] }));
12
12
  }
13
13
  function LoaderStack({ size, label, message, description, }) {
14
14
  return (_jsxs(_Fragment, { children: [_jsx("div", { className: "mb-4", children: _jsx(Spinner, { size: size, label: label }) }), message || description ? (_jsxs("div", { className: "space-y-1.5", children: [message ? (_jsx(Typography, { as: "p", variant: "bodySm", className: "font-semibold", children: message })) : null, description ? (_jsx(Typography, { as: "p", variant: "caption", className: "max-w-md", children: description })) : null] })) : null] }));
package/dist/styles.css CHANGED
@@ -171,6 +171,15 @@
171
171
 
172
172
  --primary: var(--nea-green-700);
173
173
  --primary-foreground: var(--nea-neutral-0);
174
+ --interactive-foreground: var(--nea-green-800);
175
+ --highlight-background: var(--nea-green-100);
176
+ --highlight-foreground: var(--nea-green-900);
177
+ --button-primary-background: var(--nea-green-800);
178
+ --button-primary-hover-background: var(--nea-green-900);
179
+ --button-primary-foreground: var(--nea-neutral-0);
180
+ --count-badge-background: var(--nea-green-800);
181
+ --count-badge-foreground: var(--nea-neutral-0);
182
+ --instructional-foreground: 215 16% 40%;
174
183
 
175
184
  --secondary: var(--nea-neutral-100);
176
185
  --secondary-foreground: var(--nea-neutral-900);
@@ -296,6 +305,15 @@
296
305
 
297
306
  --primary: 148 30% 38%;
298
307
  --primary-foreground: var(--nea-dark-neutral-50);
308
+ --interactive-foreground: 143 44% 67%;
309
+ --highlight-background: 148 20% 18%;
310
+ --highlight-foreground: 143 44% 67%;
311
+ --button-primary-background: 148 30% 38%;
312
+ --button-primary-hover-background: 148 30% 39%;
313
+ --button-primary-foreground: 0 0% 100%;
314
+ --count-badge-background: 148 24% 24%;
315
+ --count-badge-foreground: 143 44% 67%;
316
+ --instructional-foreground: var(--nea-dark-neutral-200);
299
317
 
300
318
  --secondary: var(--nea-dark-neutral-500);
301
319
  --secondary-foreground: var(--nea-dark-neutral-100);
package/dist/table.d.ts CHANGED
@@ -30,6 +30,14 @@ declare const TableCell: React.ForwardRefExoticComponent<React.TdHTMLAttributes<
30
30
  treatWidthAsMinWidth?: boolean;
31
31
  containOverflow?: boolean;
32
32
  } & React.RefAttributes<HTMLTableCellElement>>;
33
+ declare const TableRowHeader: React.ForwardRefExoticComponent<React.ThHTMLAttributes<HTMLTableCellElement> & {
34
+ width?: string;
35
+ minWidth?: string;
36
+ maxWidth?: string;
37
+ wrap?: boolean;
38
+ treatWidthAsMinWidth?: boolean;
39
+ containOverflow?: boolean;
40
+ } & React.RefAttributes<HTMLTableCellElement>>;
33
41
  declare const TableCaption: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableCaptionElement> & React.RefAttributes<HTMLTableCaptionElement>>;
34
42
  type TableAlign = "left" | "center" | "right";
35
43
  export type TableRowTone = "default" | "muted" | "warning";
@@ -85,13 +93,15 @@ export interface TableColumn<Row> {
85
93
  skeleton?: TableSkeletonConfig;
86
94
  headerClassName?: string;
87
95
  cellClassName?: string;
96
+ /** Renders this column's body cells as semantic row headers. */
97
+ rowHeader?: boolean;
88
98
  /** Pins the column to the left or right edge while the table scrolls horizontally. */
89
99
  sticky?: "left" | "right";
90
100
  /** Enables a resize handle for this column. Use `resizableColumns` on Table to opt every column in by default. */
91
101
  resizable?: boolean;
92
102
  /** Minimum pixel width while resizing. Defaults to `minWidth`, then 64. */
93
103
  resizeMinWidth?: number;
94
- /** Maximum pixel width while resizing. Omit to allow the column to grow. */
104
+ /** Maximum pixel width while resizing. Defaults to 1200. */
95
105
  resizeMaxWidth?: number;
96
106
  }
97
107
  export interface TableSortingProps {
@@ -137,6 +147,8 @@ export interface TableColumnResizeHandleRenderProps<Row> {
137
147
  handle: React.ReactElement;
138
148
  }
139
149
  export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
150
+ /** Accessible name used as a visually hidden caption when `caption` is omitted. */
151
+ accessibleLabel: string;
140
152
  columns: TableColumn<Row>[];
141
153
  rows: Row[];
142
154
  getRowKey: (row: Row, index: number) => string;
@@ -155,6 +167,8 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
155
167
  sorting?: TableSortingProps;
156
168
  pagination?: Omit<PaginationProps, "className">;
157
169
  loading?: boolean;
170
+ /** Status announced while loading. */
171
+ loadingLabel?: string;
158
172
  loadingRows?: number;
159
173
  /** Optional controlled ordered list of visible column keys. Omit to render every column in config order. */
160
174
  visibleColumnKeys?: readonly string[];
@@ -180,8 +194,6 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
180
194
  striped?: boolean;
181
195
  /** Adds a disclosure column and full-width expandable content for nested tables or row details. */
182
196
  rowExpansion?: TableRowExpansionProps<Row>;
183
- /** When set, rows become interactive: cursor-pointer, hover highlight, and click handling. */
184
- onRowClick?: (row: Row, index: number) => void;
185
197
  }
186
- declare function Table<Row>({ columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, minHeight, sorting, pagination, loading, loadingRows, visibleColumnKeys, resizableColumns, columnResizeMode, columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState, emptyStateMinHeight, striped, rowExpansion, onRowClick, onScroll, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
187
- export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, SortableTableHead, TableRowSkeleton, };
198
+ declare function Table<Row>({ accessibleLabel, columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, minHeight, sorting, pagination, loading, loadingLabel, loadingRows, visibleColumnKeys, resizableColumns, columnResizeMode, columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState, emptyStateMinHeight, striped, rowExpansion, onScroll, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
199
+ export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableRowHeader, TableCaption, SortableTableHead, TableRowSkeleton, };
package/dist/table.js CHANGED
@@ -14,6 +14,7 @@ const TableContainer = React.forwardRef(({ className, ...props }, ref) => (_jsx(
14
14
  TableContainer.displayName = "TableContainer";
15
15
  const DEFAULT_COLUMN_RESIZE_MIN_WIDTH = 64;
16
16
  const DEFAULT_COLUMN_RESIZE_START_WIDTH = 160;
17
+ const DEFAULT_COLUMN_RESIZE_MAX_WIDTH = 1200;
17
18
  const COLUMN_RESIZE_KEYBOARD_STEP = 16;
18
19
  const TABLE_EXPANSION_COLUMN_WIDTH = 48;
19
20
  const TABLE_MIN_WIDTH_CSS_VAR = "--table-min-width";
@@ -62,7 +63,7 @@ function getColumnResizeMinWidth(column) {
62
63
  }
63
64
  function getColumnResizeMaxWidth(column) {
64
65
  var _a;
65
- return (_a = parsePixelSize(column.resizeMaxWidth)) !== null && _a !== void 0 ? _a : Number.POSITIVE_INFINITY;
66
+ return (_a = parsePixelSize(column.resizeMaxWidth)) !== null && _a !== void 0 ? _a : DEFAULT_COLUMN_RESIZE_MAX_WIDTH;
66
67
  }
67
68
  function getColumnResizeMaxWidthStyle(column) {
68
69
  const maxWidth = getColumnResizeMaxWidth(column);
@@ -138,6 +139,15 @@ function getColumnResizeLabel(column) {
138
139
  }
139
140
  return column.key;
140
141
  }
142
+ function getColumnHeaderAriaLabel(column) {
143
+ if (column.header === null ||
144
+ column.header === undefined ||
145
+ column.header === false ||
146
+ column.header === "") {
147
+ return getColumnResizeLabel(column);
148
+ }
149
+ return undefined;
150
+ }
141
151
  const TableHeader = React.forwardRef(({ className, ...props }, ref) => (_jsx("thead", { ref: ref, className: cn("sticky top-0 z-10 bg-[hsl(var(--table-header-background))] [&_tr]:border-b [&_tr]:border-[hsl(var(--table-border))] [&>tr]:bg-inherit", className), ...props })));
142
152
  TableHeader.displayName = "TableHeader";
143
153
  const TableBody = React.forwardRef(({ className, ...props }, ref) => (_jsx("tbody", { ref: ref, className: cn("[&_tr:last-child]:border-0", className), ...props })));
@@ -146,7 +156,7 @@ const TableFooter = React.forwardRef(({ className, ...props }, ref) => (_jsx("tf
146
156
  TableFooter.displayName = "TableFooter";
147
157
  const TableRow = React.forwardRef(({ className, ...props }, ref) => (_jsx("tr", { ref: ref, className: cn("border-b border-[hsl(var(--table-border))] bg-card text-card-foreground transition-colors data-[state=selected]:bg-muted", className), ...props })));
148
158
  TableRow.displayName = "TableRow";
149
- const TableHead = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, ...props }, ref) => (_jsx("th", { ref: ref, className: cn("h-11 px-4 py-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
159
+ const TableHead = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, ...props }, ref) => (_jsx("th", { ref: ref, className: cn("h-11 px-4 py-2 text-left align-middle font-medium text-instructional [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
150
160
  style: {
151
161
  ...style,
152
162
  fontWeight: 500,
@@ -165,6 +175,14 @@ const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidt
165
175
  treatWidthAsMinWidth,
166
176
  }), ...props })));
167
177
  TableCell.displayName = "TableCell";
178
+ const TableRowHeader = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, scope = "row", ...props }, ref) => (_jsx("th", { ref: ref, scope: scope, className: cn("h-12 px-4 py-2 text-left align-middle font-medium text-card-foreground [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
179
+ style,
180
+ width,
181
+ minWidth,
182
+ maxWidth,
183
+ treatWidthAsMinWidth,
184
+ }), ...props })));
185
+ TableRowHeader.displayName = "TableRowHeader";
168
186
  const TableCaption = React.forwardRef(({ className, ...props }, ref) => (_jsx("caption", { ref: ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })));
169
187
  TableCaption.displayName = "TableCaption";
170
188
  const STICKY_SCROLL_EDGE_THRESHOLD = 1;
@@ -246,13 +264,20 @@ export const TABLE_SKELETON_PRESETS = {
246
264
  };
247
265
  function SortableTableHead({ field, children, currentSortField, currentSortDirection, onSort, className, allowWrap = false, width, minWidth, maxWidth, align, sortLabel, resizeHandle, treatWidthAsMinWidth, containOverflow, }) {
248
266
  const isSorted = currentSortField === field;
249
- return (_jsxs(TableHead, { "data-slot": "table-column-head", className: cn(resizeHandle && "relative", alignClassName(align), className), wrap: allowWrap, width: width, minWidth: minWidth, maxWidth: maxWidth, treatWidthAsMinWidth: treatWidthAsMinWidth, containOverflow: containOverflow, children: [_jsxs("button", { type: "button", onClick: () => onSort(field), className: cn("-ml-1.5 flex h-7 cursor-pointer items-center gap-1 rounded px-1.5 py-0 font-medium transition-colors hover:text-foreground", containOverflow && "max-w-full overflow-hidden", allowWrap
267
+ const resolvedSortLabel = sortLabel !== null && sortLabel !== void 0 ? sortLabel : (typeof children === "string" || typeof children === "number"
268
+ ? String(children)
269
+ : field);
270
+ return (_jsxs(TableHead, { "data-slot": "table-column-head", scope: "col", "aria-sort": isSorted
271
+ ? currentSortDirection === "asc"
272
+ ? "ascending"
273
+ : "descending"
274
+ : undefined, className: cn(resizeHandle && "relative", alignClassName(align), className), wrap: allowWrap, width: width, minWidth: minWidth, maxWidth: maxWidth, treatWidthAsMinWidth: treatWidthAsMinWidth, containOverflow: containOverflow, children: [_jsxs("button", { type: "button", onClick: () => onSort(field), className: cn("-ml-1.5 flex h-7 cursor-pointer items-center gap-1 rounded px-1.5 py-0 font-medium transition-colors hover:text-foreground", containOverflow && "max-w-full overflow-hidden", allowWrap
250
275
  ? "max-w-full whitespace-normal text-left"
251
- : "whitespace-nowrap", align === "right" && "ml-auto justify-end", align === "center" && "mx-auto justify-center"), style: { font: "inherit", color: "inherit", fontWeight: 500 }, "aria-label": `Sort by ${sortLabel !== null && sortLabel !== void 0 ? sortLabel : field}`, children: [_jsx("span", { className: cn("min-w-0", containOverflow && !allowWrap && "truncate", containOverflow && allowWrap && "break-words [overflow-wrap:anywhere]"), children: children }), _jsx("span", { className: "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center", children: isSorted ? (currentSortDirection === "asc" ? (_jsx(ArrowUpIcon, { className: "shrink-0", size: 14 })) : (_jsx(ArrowDownIcon, { className: "shrink-0", size: 14 }))) : (_jsx(ArrowUpDown, { className: "h-3.5 w-3.5 shrink-0 opacity-50" })) })] }), resizeHandle] }));
276
+ : "whitespace-nowrap", align === "right" && "ml-auto justify-end", align === "center" && "mx-auto justify-center"), style: { font: "inherit", color: "inherit", fontWeight: 500 }, "aria-label": `Sort by ${resolvedSortLabel}`, children: [_jsx("span", { className: cn("min-w-0", containOverflow && !allowWrap && "truncate", containOverflow && allowWrap && "break-words [overflow-wrap:anywhere]"), children: children }), _jsx("span", { "aria-hidden": "true", className: "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center", children: isSorted ? (currentSortDirection === "asc" ? (_jsx(ArrowUpIcon, { className: "shrink-0", size: 14 })) : (_jsx(ArrowDownIcon, { className: "shrink-0", size: 14 }))) : (_jsx(ArrowUpDown, { className: "h-3.5 w-3.5 shrink-0 opacity-50" })) })] }), resizeHandle] }));
252
277
  }
253
278
  const TableColumnResizeHandle = React.forwardRef(function TableColumnResizeHandle({ column, nextColumn, currentWidth, minWidth, maxWidth, onResizeStart, onResizeByKeyboard, className, onClick, ...props }, ref) {
254
279
  const label = getColumnResizeLabel(column);
255
- return (_jsx("button", { ...props, ref: ref, type: "button", "aria-label": `Resize ${label} column`, title: `Resize ${label} column`, "aria-valuenow": currentWidth, "aria-valuemin": minWidth, "aria-valuemax": maxWidth, onPointerDown: (event) => onResizeStart(column, nextColumn, event), onClick: (event) => {
280
+ return (_jsx("div", { ...props, ref: ref, role: "separator", tabIndex: 0, "aria-orientation": "vertical", "aria-label": `Resize ${label} column`, title: `Resize ${label} column`, "aria-valuenow": currentWidth, "aria-valuemin": minWidth, "aria-valuemax": maxWidth, "aria-valuetext": currentWidth === undefined ? undefined : `${currentWidth} pixels`, onPointerDown: (event) => onResizeStart(column, nextColumn, event), onClick: (event) => {
256
281
  onClick === null || onClick === void 0 ? void 0 : onClick(event);
257
282
  if (event.defaultPrevented) {
258
283
  return;
@@ -268,11 +293,11 @@ const TableColumnResizeHandle = React.forwardRef(function TableColumnResizeHandl
268
293
  onResizeByKeyboard(column, nextColumn, event.key === "ArrowLeft"
269
294
  ? -COLUMN_RESIZE_KEYBOARD_STEP
270
295
  : COLUMN_RESIZE_KEYBOARD_STEP);
271
- }, className: cn("group absolute right-0 top-0 z-[1] flex h-full w-4 cursor-col-resize touch-none items-center justify-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", className), children: _jsx("span", { "aria-hidden": "true", className: "h-6 w-px rounded-full bg-[hsl(var(--table-border))] transition-colors group-hover:bg-primary group-focus-visible:bg-primary" }) }));
296
+ }, className: cn("group absolute right-0 top-0 z-[1] flex h-full w-4 cursor-col-resize touch-none items-center justify-center rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring", className), children: _jsx("span", { "aria-hidden": "true", className: "h-6 w-px rounded-full bg-[hsl(var(--table-border))] transition-colors group-hover:bg-primary group-focus-visible:bg-primary" }) }));
272
297
  });
273
298
  const TableColumnGrowResizeHandle = React.forwardRef(function TableColumnGrowResizeHandle({ column, currentWidth, minWidth, maxWidth, onResizeStart, onResizeByKeyboard, className, onClick, ...props }, ref) {
274
299
  const label = getColumnResizeLabel(column);
275
- return (_jsx("button", { ...props, ref: ref, type: "button", "aria-label": `Resize ${label} column`, title: `Resize ${label} column`, "aria-valuenow": currentWidth, "aria-valuemin": minWidth, "aria-valuemax": maxWidth, onPointerDown: (event) => onResizeStart(column, event), onClick: (event) => {
300
+ return (_jsx("div", { ...props, ref: ref, role: "separator", tabIndex: 0, "aria-orientation": "vertical", "aria-label": `Resize ${label} column`, title: `Resize ${label} column`, "aria-valuenow": currentWidth, "aria-valuemin": minWidth, "aria-valuemax": maxWidth, "aria-valuetext": currentWidth === undefined ? undefined : `${currentWidth} pixels`, onPointerDown: (event) => onResizeStart(column, event), onClick: (event) => {
276
301
  onClick === null || onClick === void 0 ? void 0 : onClick(event);
277
302
  if (event.defaultPrevented) {
278
303
  return;
@@ -288,7 +313,7 @@ const TableColumnGrowResizeHandle = React.forwardRef(function TableColumnGrowRes
288
313
  onResizeByKeyboard(column, event.key === "ArrowLeft"
289
314
  ? -COLUMN_RESIZE_KEYBOARD_STEP
290
315
  : COLUMN_RESIZE_KEYBOARD_STEP);
291
- }, className: cn("group absolute right-0 top-0 z-[1] flex h-full w-4 cursor-col-resize touch-none items-center justify-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", className), children: _jsx("span", { "aria-hidden": "true", className: "h-6 w-px rounded-full bg-[hsl(var(--table-border))] transition-colors group-hover:bg-primary group-focus-visible:bg-primary" }) }));
316
+ }, className: cn("group absolute right-0 top-0 z-[1] flex h-full w-4 cursor-col-resize touch-none items-center justify-center rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring", className), children: _jsx("span", { "aria-hidden": "true", className: "h-6 w-px rounded-full bg-[hsl(var(--table-border))] transition-colors group-hover:bg-primary group-focus-visible:bg-primary" }) }));
292
317
  });
293
318
  function getDefaultSkeleton(column) {
294
319
  var _a, _b;
@@ -329,7 +354,18 @@ function getVisibleColumns(columns, visibleColumnKeys) {
329
354
  function isStickyResizeBoundary(column, nextColumn) {
330
355
  return Boolean(column.sticky === "right" || nextColumn.sticky);
331
356
  }
332
- function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { message: "No data available", description: "There are no rows to display in this table yet.", className: "border-0 bg-transparent px-0 py-6" })), toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, minHeight, sorting, pagination, loading = false, loadingRows = 5, visibleColumnKeys, resizableColumns = false, columnResizeMode = "pair", columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState = false, emptyStateMinHeight = DEFAULT_CENTERED_EMPTY_STATE_MIN_HEIGHT, striped = false, rowExpansion, onRowClick, onScroll, style, ...props }) {
357
+ function getColumnPairResizeRange(column, nextColumn, columnWidths) {
358
+ const currentWidth = getResolvedResizableColumnWidth(column, columnWidths);
359
+ const nextWidth = getResolvedResizableColumnWidth(nextColumn, columnWidths);
360
+ const minDelta = Math.max(getColumnResizeMinWidth(column) - currentWidth, nextWidth - getColumnResizeMaxWidth(nextColumn));
361
+ const maxDelta = Math.min(getColumnResizeMaxWidth(column) - currentWidth, nextWidth - getColumnResizeMinWidth(nextColumn));
362
+ return {
363
+ currentWidth,
364
+ minWidth: Math.round(currentWidth + minDelta),
365
+ maxWidth: Math.round(currentWidth + maxDelta),
366
+ };
367
+ }
368
+ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { message: "No data available", description: "There are no rows to display in this table yet.", className: "border-0 bg-transparent px-0 py-6" })), toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, minHeight, sorting, pagination, loading = false, loadingLabel = "Loading table data", loadingRows = 5, visibleColumnKeys, resizableColumns = false, columnResizeMode = "pair", columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState = false, emptyStateMinHeight = DEFAULT_CENTERED_EMPTY_STATE_MIN_HEIGHT, striped = false, rowExpansion, onScroll, style, ...props }) {
333
369
  const visibleColumns = getVisibleColumns(columns, visibleColumnKeys);
334
370
  const hasExpansionColumn = Boolean(rowExpansion);
335
371
  const hasColumnFilters = visibleColumns.some((column) => column.filter);
@@ -353,6 +389,7 @@ function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { mess
353
389
  const [stickyScrollState, setStickyScrollState] = React.useState({
354
390
  left: false,
355
391
  right: false,
392
+ scrollable: false,
356
393
  });
357
394
  const [uncontrolledExpandedRowKeys, setUncontrolledExpandedRowKeys] = React.useState(() => { var _a; return new Set((_a = rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.defaultExpandedRowKeys) !== null && _a !== void 0 ? _a : []); });
358
395
  const controlledExpandedRowKeys = rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.expandedRowKeys;
@@ -364,12 +401,16 @@ function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { mess
364
401
  }
365
402
  const maxScrollLeft = Math.max(0, element.scrollWidth - element.clientWidth);
366
403
  const scrollLeft = Math.max(0, element.scrollLeft);
404
+ const maxScrollTop = Math.max(0, element.scrollHeight - element.clientHeight);
367
405
  const nextScrollState = {
368
406
  left: scrollLeft > STICKY_SCROLL_EDGE_THRESHOLD,
369
407
  right: scrollLeft < maxScrollLeft - STICKY_SCROLL_EDGE_THRESHOLD,
408
+ scrollable: maxScrollLeft > STICKY_SCROLL_EDGE_THRESHOLD ||
409
+ maxScrollTop > STICKY_SCROLL_EDGE_THRESHOLD,
370
410
  };
371
411
  setStickyScrollState((currentScrollState) => currentScrollState.left === nextScrollState.left &&
372
- currentScrollState.right === nextScrollState.right
412
+ currentScrollState.right === nextScrollState.right &&
413
+ currentScrollState.scrollable === nextScrollState.scrollable
373
414
  ? currentScrollState
374
415
  : nextScrollState);
375
416
  }, []);
@@ -499,13 +540,17 @@ function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { mess
499
540
  if (stickyResizeBoundary) {
500
541
  return null;
501
542
  }
543
+ const pairRange = getColumnPairResizeRange(column, nextColumn, resolvedColumnWidths);
502
544
  const currentWidth = isGrowResizeMode
503
545
  ? getColumnResizeBaseWidth(column, resolvedColumnWidths)
504
- : getResolvedResizableColumnWidth(column, resolvedColumnWidths);
505
- const maxWidth = Number.isFinite(getColumnResizeMaxWidth(column))
546
+ : pairRange.currentWidth;
547
+ const minWidth = isGrowResizeMode
548
+ ? getColumnResizeMinWidth(column)
549
+ : pairRange.minWidth;
550
+ const maxWidth = isGrowResizeMode
506
551
  ? getColumnResizeMaxWidth(column)
507
- : undefined;
508
- const handle = isGrowResizeMode ? (_jsx(TableColumnGrowResizeHandle, { column: column, currentWidth: currentWidth, minWidth: getColumnResizeMinWidth(column), maxWidth: maxWidth, onResizeStart: startGrowColumnResize, onResizeByKeyboard: resizeGrowColumnByKeyboard })) : (_jsx(TableColumnResizeHandle, { column: column, nextColumn: nextColumn, currentWidth: currentWidth, minWidth: getColumnResizeMinWidth(column), maxWidth: maxWidth, onResizeStart: startColumnResize, onResizeByKeyboard: resizeColumnByKeyboard }));
552
+ : pairRange.maxWidth;
553
+ const handle = isGrowResizeMode ? (_jsx(TableColumnGrowResizeHandle, { column: column, currentWidth: currentWidth, minWidth: minWidth, maxWidth: maxWidth, onResizeStart: startGrowColumnResize, onResizeByKeyboard: resizeGrowColumnByKeyboard })) : (_jsx(TableColumnResizeHandle, { column: column, nextColumn: nextColumn, currentWidth: currentWidth, minWidth: minWidth, maxWidth: maxWidth, onResizeStart: startColumnResize, onResizeByKeyboard: resizeColumnByKeyboard }));
509
554
  if (renderColumnResizeHandle) {
510
555
  return renderColumnResizeHandle({ column, nextColumn, handle });
511
556
  }
@@ -540,89 +585,94 @@ function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { mess
540
585
  (_a = rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.onExpandedRowKeysChange) === null || _a === void 0 ? void 0 : _a.call(rowExpansion, Array.from(nextExpandedRowKeys));
541
586
  }, [controlledExpandedRowKeys, rowExpansion, uncontrolledExpandedRowKeys]);
542
587
  const resolvedEmptyState = centerEmptyState ? (_jsx("div", { "data-slot": "table-empty-state", className: "flex w-full min-w-0 items-center justify-center whitespace-normal text-center [&>*]:mx-auto", style: { minHeight: emptyStateMinHeight }, children: emptyState })) : (emptyState);
543
- const container = (_jsx(TableContainer, { ref: tableContainerRef, onScroll: handleTableContainerScroll, className: cn("w-full", loading && "pointer-events-none select-none", className, containerClassName), style: {
588
+ const container = (_jsxs(TableContainer, { ref: tableContainerRef, role: stickyScrollState.scrollable ? "region" : undefined, "aria-label": stickyScrollState.scrollable
589
+ ? `${accessibleLabel} scroll area`
590
+ : undefined, tabIndex: stickyScrollState.scrollable ? 0 : undefined, onScroll: handleTableContainerScroll, className: cn("w-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", loading && "pointer-events-none select-none", className, containerClassName), style: {
544
591
  ...style,
545
592
  ...(growTableMinWidth !== undefined
546
593
  ? { [TABLE_MIN_WIDTH_CSS_VAR]: `${growTableMinWidth}px` }
547
594
  : {}),
548
595
  ...(maxHeight !== undefined ? { maxHeight } : {}),
549
596
  ...(resolvedMinHeight !== undefined ? { minHeight: resolvedMinHeight } : {}),
550
- }, ...props, children: _jsxs(TableRoot, { className: cn(shouldUseFixedLayout && "table-fixed", isGrowResizeMode &&
551
- (hasResolvedColumnWidths
552
- ? "w-[var(--table-min-width)]"
553
- : "min-w-[var(--table-min-width)]"), tableClassName), children: [caption ? _jsx(TableCaption, { children: caption }) : null, _jsxs("colgroup", { children: [hasExpansionColumn ? (_jsx("col", { style: {
554
- width: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
555
- minWidth: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
556
- maxWidth: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
557
- } })) : null, visibleColumns.map((column) => (_jsx("col", { style: getTableColumnSizingStyle({
558
- width: getRenderedColumnWidth({
559
- column,
560
- columnWidths: resolvedColumnWidths,
561
- resizing: isColumnResizingEnabled,
562
- }),
563
- minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled),
564
- maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled),
565
- treatWidthAsMinWidth: !isColumnResizingEnabled,
566
- }) }, column.key)))] }), _jsxs(TableHeader, { children: [_jsxs(TableRow, { children: [hasExpansionColumn ? (_jsx(TableHead, { "data-slot": "table-expansion-head", "aria-label": "Expand rows", className: "w-12 px-2" })) : null, visibleColumns.map((column, columnIndex) => {
567
- var _a;
568
- const sortField = (_a = column.sortKey) !== null && _a !== void 0 ? _a : column.key;
569
- const isSortable = column.sortable && sorting;
570
- const columnWidth = getRenderedColumnWidth({
597
+ }, ...props, children: [_jsxs(TableRoot, { "aria-busy": loading, className: cn(shouldUseFixedLayout && "table-fixed", isGrowResizeMode &&
598
+ (hasResolvedColumnWidths
599
+ ? "w-[var(--table-min-width)]"
600
+ : "min-w-[var(--table-min-width)]"), tableClassName), children: [_jsx(TableCaption, { className: caption ? undefined : "sr-only !m-0", children: caption !== null && caption !== void 0 ? caption : accessibleLabel }), _jsxs("colgroup", { children: [hasExpansionColumn ? (_jsx("col", { style: {
601
+ width: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
602
+ minWidth: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
603
+ maxWidth: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
604
+ } })) : null, visibleColumns.map((column) => (_jsx("col", { style: getTableColumnSizingStyle({
605
+ width: getRenderedColumnWidth({
571
606
  column,
572
607
  columnWidths: resolvedColumnWidths,
573
608
  resizing: isColumnResizingEnabled,
574
- });
575
- const resizeHandle = renderResizeHandle(column, columnIndex);
576
- return (isSortable ? (_jsx(SortableTableHead, { field: sortField, currentSortField: sorting.field, currentSortDirection: sorting.direction, onSort: sorting.onSort, className: cn(stickyClassName(column.sticky, "head", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.headerClassName), allowWrap: column.wrap, width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), align: column.align, sortLabel: column.sortLabel, resizeHandle: resizeHandle, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, children: column.header }, column.key)) : (_jsxs(TableHead, { "data-slot": "table-column-head", width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), wrap: column.wrap, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn(resizeHandle && "relative", alignClassName(column.align), stickyClassName(column.sticky, "head", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.headerClassName), children: [column.header, resizeHandle] }, column.key)));
577
- })] }), hasColumnFilters ? (_jsxs(TableRow, { "data-slot": "table-filter-row", className: "!bg-card", children: [hasExpansionColumn ? (_jsx(TableHead, { "data-slot": "table-expansion-filter-cell", "aria-hidden": "true", className: "w-12 px-2" })) : null, visibleColumns.map((column) => {
578
- const columnWidth = getRenderedColumnWidth({
579
- column,
580
- columnWidths: resolvedColumnWidths,
581
- resizing: isColumnResizingEnabled,
582
- });
583
- return (_jsx(TableHead, { "data-slot": "table-filter-cell", width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn("relative h-11 px-2 py-1 font-normal", stickyClassName(column.sticky, "filter", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.filterClassName), children: column.filter ? (_jsx("div", { "data-slot": "table-filter-control", className: "absolute inset-x-2 inset-y-1 min-w-0", children: _jsx(TableColumnFilterControl, { filter: column.filter, label: getColumnResizeLabel(column) }) })) : null }, column.key));
584
- })] })) : null] }), _jsx(TableBody, { children: loading ? (_jsx(TableRowSkeleton, { rows: loadingRows, columns: [
585
- ...(hasExpansionColumn
586
- ? [{ width: "1rem", height: "1rem" }]
587
- : []),
588
- ...visibleColumns.map((column) => {
589
- var _a;
590
- return (_a = column.skeleton) !== null && _a !== void 0 ? _a : getDefaultSkeleton(column);
591
- }),
592
- ] })) : rows.length ? (rows.map((row, rowIndex) => {
593
- var _a, _b, _c, _d;
594
- const rowKey = getRowKey(row, rowIndex);
595
- const resolvedTone = typeof rowTone === "function"
596
- ? rowTone(row, rowIndex)
597
- : rowTone;
598
- const isRowExpandable = (_b = (_a = rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.isRowExpandable) === null || _a === void 0 ? void 0 : _a.call(rowExpansion, row, rowIndex)) !== null && _b !== void 0 ? _b : hasExpansionColumn;
599
- const isExpanded = isRowExpandable && resolvedExpandedRowKeys.has(rowKey);
600
- const expandedContentId = `${expansionIdPrefix}-expanded-row-${rowIndex}`;
601
- return (_jsxs(React.Fragment, { children: [_jsxs(TableRow, { "data-expanded": isExpanded ? "true" : undefined, onClick: onRowClick ? () => onRowClick(row, rowIndex) : undefined, className: cn(onRowClick && "cursor-pointer hover:bg-muted", striped &&
602
- rowIndex % 2 === 1 &&
603
- !resolvedTone &&
604
- "bg-[hsl(var(--table-row-striped-background))]", tableRowToneClassName(resolvedTone), typeof rowClassName === "function"
605
- ? rowClassName(row, rowIndex)
606
- : rowClassName), children: [hasExpansionColumn ? (_jsx(TableCell, { className: "w-12 px-2 text-center", children: isRowExpandable ? (_jsx("button", { type: "button", "aria-expanded": isExpanded, "aria-controls": expandedContentId, "aria-label": (_d = (_c = rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.getToggleLabel) === null || _c === void 0 ? void 0 : _c.call(rowExpansion, row, rowIndex, isExpanded)) !== null && _d !== void 0 ? _d : `${isExpanded ? "Collapse" : "Expand"} row ${rowIndex + 1}`, onClick: (event) => {
607
- event.stopPropagation();
608
- toggleExpandedRow(rowKey);
609
- }, className: "inline-flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx(ChevronRight, { "aria-hidden": "true", className: cn("h-4 w-4 transition-transform duration-150", isExpanded && "rotate-90") }) })) : null })) : null, visibleColumns.map((column) => {
610
- var _a, _b;
611
- const columnWidth = getRenderedColumnWidth({
612
- column,
613
- columnWidths: resolvedColumnWidths,
614
- resizing: isColumnResizingEnabled,
615
- });
616
- const value = (_b = (_a = column.cell) === null || _a === void 0 ? void 0 : _a.call(column, row, rowIndex)) !== null && _b !== void 0 ? _b : (column.accessorKey !== undefined
617
- ? row[column.accessorKey]
618
- : null);
619
- return (_jsx(TableCell, { width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), wrap: column.wrap, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn(alignClassName(column.align), stickyClassName(column.sticky, "cell", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.cellClassName), children: value }, column.key));
620
- })] }), isExpanded ? (_jsx(TableRow, { id: expandedContentId, "data-slot": "table-expanded-row", className: "bg-[hsl(var(--table-row-striped-background))] hover:bg-[hsl(var(--table-row-striped-background))]", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length +
621
- (hasExpansionColumn ? 1 : 0), 1), className: "h-auto whitespace-normal p-4", children: rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.renderExpandedContent(row, rowIndex) }) })) : null] }, rowKey));
622
- })) : (_jsx(TableRow, { className: "cursor-default hover:bg-card", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length + (hasExpansionColumn ? 1 : 0), 1), className: "p-6", children: resolvedEmptyState }) })) })] }) }));
609
+ }),
610
+ minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled),
611
+ maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled),
612
+ treatWidthAsMinWidth: !isColumnResizingEnabled,
613
+ }) }, column.key)))] }), _jsxs(TableHeader, { children: [_jsxs(TableRow, { children: [hasExpansionColumn ? (_jsx(TableHead, { "data-slot": "table-expansion-head", "aria-label": "Expand rows", scope: "col", className: "w-12 px-2" })) : null, visibleColumns.map((column, columnIndex) => {
614
+ var _a;
615
+ const sortField = (_a = column.sortKey) !== null && _a !== void 0 ? _a : column.key;
616
+ const isSortable = column.sortable && sorting;
617
+ const columnWidth = getRenderedColumnWidth({
618
+ column,
619
+ columnWidths: resolvedColumnWidths,
620
+ resizing: isColumnResizingEnabled,
621
+ });
622
+ const resizeHandle = renderResizeHandle(column, columnIndex);
623
+ return (isSortable ? (_jsx(SortableTableHead, { field: sortField, currentSortField: sorting.field, currentSortDirection: sorting.direction, onSort: sorting.onSort, className: cn(stickyClassName(column.sticky, "head", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.headerClassName), allowWrap: column.wrap, width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), align: column.align, sortLabel: column.sortLabel, resizeHandle: resizeHandle, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, children: column.header }, column.key)) : (_jsxs(TableHead, { "data-slot": "table-column-head", "aria-label": getColumnHeaderAriaLabel(column), scope: "col", width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), wrap: column.wrap, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn(resizeHandle && "relative", alignClassName(column.align), stickyClassName(column.sticky, "head", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.headerClassName), children: [column.header, resizeHandle] }, column.key)));
624
+ })] }), hasColumnFilters ? (_jsxs(TableRow, { "data-slot": "table-filter-row", className: "!bg-card", children: [hasExpansionColumn ? (_jsx(TableCell, { "data-slot": "table-expansion-filter-cell", "aria-hidden": "true", className: "w-12 px-2" })) : null, visibleColumns.map((column) => {
625
+ const columnWidth = getRenderedColumnWidth({
626
+ column,
627
+ columnWidths: resolvedColumnWidths,
628
+ resizing: isColumnResizingEnabled,
629
+ });
630
+ return (_jsx(TableCell, { "data-slot": "table-filter-cell", width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn("relative h-11 px-2 py-1 font-normal", stickyClassName(column.sticky, "filter", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.filterClassName), children: column.filter ? (_jsx("div", { "data-slot": "table-filter-control", className: "absolute inset-x-2 inset-y-1 min-w-0", children: _jsx(TableColumnFilterControl, { filter: column.filter, label: getColumnResizeLabel(column) }) })) : null }, column.key));
631
+ })] })) : null] }), _jsx(TableBody, { "aria-hidden": loading ? "true" : undefined, children: loading ? (_jsx(TableRowSkeleton, { rows: loadingRows, columns: [
632
+ ...(hasExpansionColumn
633
+ ? [{ width: "1rem", height: "1rem" }]
634
+ : []),
635
+ ...visibleColumns.map((column) => {
636
+ var _a;
637
+ return (_a = column.skeleton) !== null && _a !== void 0 ? _a : getDefaultSkeleton(column);
638
+ }),
639
+ ] })) : rows.length ? (rows.map((row, rowIndex) => {
640
+ var _a, _b, _c, _d;
641
+ const rowKey = getRowKey(row, rowIndex);
642
+ const resolvedTone = typeof rowTone === "function"
643
+ ? rowTone(row, rowIndex)
644
+ : rowTone;
645
+ const isRowExpandable = (_b = (_a = rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.isRowExpandable) === null || _a === void 0 ? void 0 : _a.call(rowExpansion, row, rowIndex)) !== null && _b !== void 0 ? _b : hasExpansionColumn;
646
+ const isExpanded = isRowExpandable && resolvedExpandedRowKeys.has(rowKey);
647
+ const expandedContentId = `${expansionIdPrefix}-expanded-row-${rowIndex}`;
648
+ return (_jsxs(React.Fragment, { children: [_jsxs(TableRow, { "data-expanded": isExpanded ? "true" : undefined, className: cn(striped &&
649
+ rowIndex % 2 === 1 &&
650
+ !resolvedTone &&
651
+ "bg-[hsl(var(--table-row-striped-background))]", tableRowToneClassName(resolvedTone), typeof rowClassName === "function"
652
+ ? rowClassName(row, rowIndex)
653
+ : rowClassName), children: [hasExpansionColumn ? (_jsx(TableCell, { className: "w-12 px-2 text-center", children: isRowExpandable ? (_jsx("button", { type: "button", "aria-expanded": isExpanded, "aria-controls": isExpanded ? expandedContentId : undefined, "aria-label": (_d = (_c = rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.getToggleLabel) === null || _c === void 0 ? void 0 : _c.call(rowExpansion, row, rowIndex, isExpanded)) !== null && _d !== void 0 ? _d : `${isExpanded ? "Collapse" : "Expand"} row ${rowIndex + 1}`, onClick: (event) => {
654
+ event.stopPropagation();
655
+ toggleExpandedRow(rowKey);
656
+ }, className: "inline-flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx(ChevronRight, { "aria-hidden": "true", className: cn("h-4 w-4 transition-transform duration-150", isExpanded && "rotate-90") }) })) : null })) : null, visibleColumns.map((column) => {
657
+ var _a, _b;
658
+ const columnWidth = getRenderedColumnWidth({
659
+ column,
660
+ columnWidths: resolvedColumnWidths,
661
+ resizing: isColumnResizingEnabled,
662
+ });
663
+ const value = (_b = (_a = column.cell) === null || _a === void 0 ? void 0 : _a.call(column, row, rowIndex)) !== null && _b !== void 0 ? _b : (column.accessorKey !== undefined
664
+ ? row[column.accessorKey]
665
+ : null);
666
+ const Cell = column.rowHeader
667
+ ? TableRowHeader
668
+ : TableCell;
669
+ return (_jsx(Cell, { width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), wrap: column.wrap, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn(alignClassName(column.align), stickyClassName(column.sticky, "cell", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.cellClassName), children: value }, column.key));
670
+ })] }), isExpanded ? (_jsx(TableRow, { id: expandedContentId, "data-slot": "table-expanded-row", className: "bg-[hsl(var(--table-row-striped-background))] hover:bg-[hsl(var(--table-row-striped-background))]", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length +
671
+ (hasExpansionColumn ? 1 : 0), 1), className: "h-auto whitespace-normal p-4", children: rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.renderExpandedContent(row, rowIndex) }) })) : null] }, rowKey));
672
+ })) : (_jsx(TableRow, { className: "cursor-default hover:bg-card", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length + (hasExpansionColumn ? 1 : 0), 1), className: "p-6", children: resolvedEmptyState }) })) })] }), _jsx("div", { role: "status", className: "sr-only", children: loading ? loadingLabel : null })] }));
623
673
  if (!pagination) {
624
674
  return (_jsxs("div", { className: "space-y-4", children: [toolbar, container] }));
625
675
  }
626
676
  return (_jsxs("div", { className: "space-y-4", children: [toolbar, container, _jsx(Pagination, { ...pagination })] }));
627
677
  }
628
- export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, SortableTableHead, TableRowSkeleton, };
678
+ export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableRowHeader, TableCaption, SortableTableHead, TableRowSkeleton, };
@@ -38,6 +38,21 @@ export const neaTailwindPreset = {
38
38
  DEFAULT: "hsl(var(--primary))",
39
39
  foreground: "hsl(var(--primary-foreground))",
40
40
  },
41
+ interactive: "hsl(var(--interactive-foreground))",
42
+ highlight: {
43
+ DEFAULT: "hsl(var(--highlight-background))",
44
+ foreground: "hsl(var(--highlight-foreground))",
45
+ },
46
+ "button-primary": {
47
+ DEFAULT: "hsl(var(--button-primary-background))",
48
+ foreground: "hsl(var(--button-primary-foreground))",
49
+ },
50
+ "button-primary-hover": "hsl(var(--button-primary-hover-background))",
51
+ "count-badge": {
52
+ DEFAULT: "hsl(var(--count-badge-background))",
53
+ foreground: "hsl(var(--count-badge-foreground))",
54
+ },
55
+ instructional: "hsl(var(--instructional-foreground))",
41
56
  secondary: {
42
57
  DEFAULT: "hsl(var(--secondary))",
43
58
  foreground: "hsl(var(--secondary-foreground))",
package/dist/theme.d.ts CHANGED
@@ -357,6 +357,15 @@ export declare const darkThemeTokenMapping: {
357
357
  readonly "--tooltip-foreground": "var(--nea-dark-neutral-50)";
358
358
  readonly "--primary": "148 30% 38%";
359
359
  readonly "--primary-foreground": "var(--nea-dark-neutral-50)";
360
+ readonly "--interactive-foreground": "143 44% 67%";
361
+ readonly "--highlight-background": "148 20% 18%";
362
+ readonly "--highlight-foreground": "143 44% 67%";
363
+ readonly "--button-primary-background": "148 30% 38%";
364
+ readonly "--button-primary-hover-background": "148 30% 39%";
365
+ readonly "--button-primary-foreground": "0 0% 100%";
366
+ readonly "--count-badge-background": "148 24% 24%";
367
+ readonly "--count-badge-foreground": "143 44% 67%";
368
+ readonly "--instructional-foreground": "var(--nea-dark-neutral-200)";
360
369
  readonly "--secondary": "var(--nea-dark-neutral-500)";
361
370
  readonly "--secondary-foreground": "var(--nea-dark-neutral-100)";
362
371
  readonly "--muted": "var(--nea-dark-neutral-500)";
@@ -448,6 +457,42 @@ export declare const semanticColorTokens: readonly [{
448
457
  readonly foregroundToken: "--primary-foreground";
449
458
  readonly darkValue: "148 30% 38%";
450
459
  readonly darkForegroundValue: "var(--nea-dark-neutral-50)";
460
+ }, {
461
+ readonly name: "Interactive Foreground";
462
+ readonly token: "--interactive-foreground";
463
+ readonly foregroundToken?: undefined;
464
+ readonly darkValue: "143 44% 67%";
465
+ readonly darkForegroundValue?: undefined;
466
+ }, {
467
+ readonly name: "Highlight";
468
+ readonly token: "--highlight-background";
469
+ readonly foregroundToken: "--highlight-foreground";
470
+ readonly darkValue: "148 20% 18%";
471
+ readonly darkForegroundValue: "143 44% 67%";
472
+ }, {
473
+ readonly name: "Primary Button";
474
+ readonly token: "--button-primary-background";
475
+ readonly foregroundToken: "--button-primary-foreground";
476
+ readonly darkValue: "148 30% 38%";
477
+ readonly darkForegroundValue: "0 0% 100%";
478
+ }, {
479
+ readonly name: "Primary Button Hover";
480
+ readonly token: "--button-primary-hover-background";
481
+ readonly foregroundToken?: undefined;
482
+ readonly darkValue: "148 30% 39%";
483
+ readonly darkForegroundValue?: undefined;
484
+ }, {
485
+ readonly name: "Count Badge";
486
+ readonly token: "--count-badge-background";
487
+ readonly foregroundToken: "--count-badge-foreground";
488
+ readonly darkValue: "148 24% 24%";
489
+ readonly darkForegroundValue: "143 44% 67%";
490
+ }, {
491
+ readonly name: "Instructional Foreground";
492
+ readonly token: "--instructional-foreground";
493
+ readonly foregroundToken?: undefined;
494
+ readonly darkValue: "var(--nea-dark-neutral-200)";
495
+ readonly darkForegroundValue?: undefined;
451
496
  }, {
452
497
  readonly name: "Secondary";
453
498
  readonly token: "--secondary";
package/dist/theme.js CHANGED
@@ -186,6 +186,15 @@ export const darkThemeTokenMapping = {
186
186
  "--tooltip-foreground": "var(--nea-dark-neutral-50)",
187
187
  "--primary": "148 30% 38%",
188
188
  "--primary-foreground": "var(--nea-dark-neutral-50)",
189
+ "--interactive-foreground": "143 44% 67%",
190
+ "--highlight-background": "148 20% 18%",
191
+ "--highlight-foreground": "143 44% 67%",
192
+ "--button-primary-background": "148 30% 38%",
193
+ "--button-primary-hover-background": "148 30% 39%",
194
+ "--button-primary-foreground": "0 0% 100%",
195
+ "--count-badge-background": "148 24% 24%",
196
+ "--count-badge-foreground": "143 44% 67%",
197
+ "--instructional-foreground": "var(--nea-dark-neutral-200)",
189
198
  "--secondary": "var(--nea-dark-neutral-500)",
190
199
  "--secondary-foreground": "var(--nea-dark-neutral-100)",
191
200
  "--muted": "var(--nea-dark-neutral-500)",
@@ -273,6 +282,33 @@ export const semanticColorTokens = [
273
282
  token: "--primary",
274
283
  foregroundToken: "--primary-foreground",
275
284
  }),
285
+ withDarkThemeMapping({
286
+ name: "Interactive Foreground",
287
+ token: "--interactive-foreground",
288
+ }),
289
+ withDarkThemeMapping({
290
+ name: "Highlight",
291
+ token: "--highlight-background",
292
+ foregroundToken: "--highlight-foreground",
293
+ }),
294
+ withDarkThemeMapping({
295
+ name: "Primary Button",
296
+ token: "--button-primary-background",
297
+ foregroundToken: "--button-primary-foreground",
298
+ }),
299
+ withDarkThemeMapping({
300
+ name: "Primary Button Hover",
301
+ token: "--button-primary-hover-background",
302
+ }),
303
+ withDarkThemeMapping({
304
+ name: "Count Badge",
305
+ token: "--count-badge-background",
306
+ foregroundToken: "--count-badge-foreground",
307
+ }),
308
+ withDarkThemeMapping({
309
+ name: "Instructional Foreground",
310
+ token: "--instructional-foreground",
311
+ }),
276
312
  withDarkThemeMapping({
277
313
  name: "Secondary",
278
314
  token: "--secondary",
package/dist/toaster.js CHANGED
@@ -36,7 +36,7 @@ function Toaster({ position = "top-right", visibleToasts = 3, closeButton = true
36
36
  title: cn(typographyVariants({ variant: "bodySm" }), "!font-medium !text-popover-foreground"),
37
37
  description: cn(typographyVariants({ variant: "bodySm" }), "!text-muted-foreground"),
38
38
  icon: "!mx-0 flex !h-8 !w-8 shrink-0 !items-center !justify-center self-start rounded-control !border !border-border !bg-muted !text-muted-foreground [&>svg]:!m-0 [&>svg]:!h-4 [&>svg]:!w-4",
39
- actionButton: cn(buttonVariants({ variant: "default", size: "compact" }), toastActionClassName, "!bg-primary !text-primary-foreground hover:!bg-primary/90"),
39
+ actionButton: cn(buttonVariants({ variant: "default", size: "compact" }), toastActionClassName, "!bg-button-primary !text-button-primary-foreground hover:!bg-button-primary-hover"),
40
40
  cancelButton: cn(buttonVariants({ variant: "outline", size: "compact" }), toastActionClassName, "!border-[hsl(var(--button-outline-border))] !bg-[hsl(var(--button-outline-background))] !text-foreground hover:!bg-[hsl(var(--button-outline-hover-background))]"),
41
41
  closeButton: "!absolute !left-auto !right-2.5 !top-2.5 !flex !h-6 !w-6 !translate-x-0 !translate-y-0 !items-center !justify-center !rounded-control !border-0 !bg-transparent !text-muted-foreground hover:!bg-accent hover:!text-foreground",
42
42
  success: "border-[hsl(var(--status-success-foreground))]/20 [&_[data-icon]]:!border-status-success-foreground/20 [&_[data-icon]]:!bg-status-success [&_[data-icon]]:!text-status-success-foreground",
@@ -12,8 +12,8 @@ const typographyVariants = cva("", {
12
12
  body: "text-base text-foreground",
13
13
  bodySm: "text-sm text-foreground",
14
14
  label: "text-sm font-medium leading-5 text-foreground",
15
- caption: "text-sm text-muted-foreground",
16
- eyebrow: "text-[11px] font-semibold uppercase leading-4 tracking-normal text-muted-foreground",
15
+ caption: "text-sm text-instructional",
16
+ eyebrow: "text-[11px] font-semibold uppercase leading-4 tracking-normal text-instructional",
17
17
  },
18
18
  },
19
19
  defaultVariants: {
@@ -40,7 +40,7 @@ const Typography = React.forwardRef(({ as, asChild = false, className, variant,
40
40
  : color === "foreground"
41
41
  ? "text-foreground"
42
42
  : color === "muted"
43
- ? "text-muted-foreground"
43
+ ? "text-instructional"
44
44
  : undefined;
45
45
  return (_jsx(Comp, { ref: ref, className: cn(typographyVariants({ variant: resolvedVariant }), colorClass, className), ...props }));
46
46
  });
@@ -41,6 +41,6 @@ function useSearchHighlight(text, query) {
41
41
  }
42
42
  function SearchHighlight({ text, query, className, markClassName, ...props }) {
43
43
  const segments = useSearchHighlight(text, query);
44
- return (_jsx("span", { className: className, ...props, children: segments.map((segment, index) => segment.matched ? (_jsx("mark", { className: cn("rounded-[2px] bg-primary/10 px-0.5 font-medium text-primary dark:bg-primary/50 dark:text-foreground dark:ring-1 dark:ring-inset dark:ring-[hsl(var(--status-success-foreground)/0.55)]", markClassName), children: segment.text }, `${segment.text}-${index}`)) : (_jsx(React.Fragment, { children: segment.text }, `${segment.text}-${index}`))) }));
44
+ return (_jsx("span", { className: className, ...props, children: segments.map((segment, index) => segment.matched ? (_jsx("mark", { className: cn("rounded-[2px] bg-highlight px-0.5 font-medium text-highlight-foreground", markClassName), children: segment.text }, `${segment.text}-${index}`)) : (_jsx(React.Fragment, { children: segment.text }, `${segment.text}-${index}`))) }));
45
45
  }
46
46
  export { SearchHighlight, useSearchHighlight };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neasg/design-system",
3
- "version": "0.4.22",
3
+ "version": "0.4.23",
4
4
  "description": "NEA shared design system primitives, theme tokens, and Storybook docs.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",