@neasg/design-system 0.4.35 → 0.4.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/activity-card.d.ts +29 -0
  2. package/dist/activity-card.js +65 -0
  3. package/dist/autocomplete.d.ts +84 -0
  4. package/dist/autocomplete.js +267 -0
  5. package/dist/avatar.d.ts +3 -1
  6. package/dist/avatar.js +27 -10
  7. package/dist/badge.js +1 -1
  8. package/dist/button.d.ts +1 -1
  9. package/dist/button.js +1 -0
  10. package/dist/calendar.js +1 -1
  11. package/dist/card.d.ts +1 -1
  12. package/dist/card.js +1 -1
  13. package/dist/compound-search-input.d.ts +8 -0
  14. package/dist/compound-search-input.js +9 -0
  15. package/dist/date-input.js +3 -3
  16. package/dist/dialog.d.ts +3 -1
  17. package/dist/dialog.js +6 -2
  18. package/dist/draggable-tabs.js +9 -8
  19. package/dist/index.d.ts +12 -4
  20. package/dist/index.js +6 -2
  21. package/dist/label-value-grid.js +6 -4
  22. package/dist/layout.d.ts +39 -6
  23. package/dist/layout.js +118 -42
  24. package/dist/lib/utils.js +1 -0
  25. package/dist/message-item.d.ts +23 -3
  26. package/dist/message-item.js +115 -16
  27. package/dist/notification.d.ts +14 -3
  28. package/dist/notification.js +32 -7
  29. package/dist/popover-menu.d.ts +4 -2
  30. package/dist/popover-menu.js +65 -5
  31. package/dist/popover.d.ts +5 -2
  32. package/dist/popover.js +3 -3
  33. package/dist/rich-text-editor.d.ts +2 -1
  34. package/dist/rich-text-editor.js +28 -3
  35. package/dist/routing-timeline.d.ts +7 -2
  36. package/dist/routing-timeline.js +67 -29
  37. package/dist/search-input.d.ts +4 -0
  38. package/dist/search-input.js +2 -2
  39. package/dist/select-primitive.js +1 -1
  40. package/dist/select.d.ts +3 -1
  41. package/dist/select.js +9 -4
  42. package/dist/status-dot.d.ts +13 -0
  43. package/dist/status-dot.js +19 -0
  44. package/dist/styles.css +0 -19
  45. package/dist/table-column-filter.js +7 -5
  46. package/dist/table.d.ts +9 -1
  47. package/dist/table.js +25 -11
  48. package/dist/tabs.d.ts +1 -1
  49. package/dist/tabs.js +6 -4
  50. package/dist/textarea.d.ts +1 -0
  51. package/dist/textarea.js +3 -2
  52. package/dist/theme-switcher.d.ts +3 -1
  53. package/dist/theme-switcher.js +8 -2
  54. package/package.json +1 -1
@@ -1,58 +1,96 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { ArrowDown } from "lucide-react";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ArrowRight, Mail, Route } from "lucide-react";
3
+ import { ActivityCard, ActivityCardContent } from "./activity-card";
4
+ import { AvatarProfile } from "./avatar";
3
5
  import { Badge } from "./badge";
4
6
  import { EmptyState } from "./empty-state";
5
7
  import { cn } from "./lib/utils";
6
8
  import { Typography } from "./typography";
7
9
  const routingKindConfig = {
8
10
  assigned: {
9
- title: "Assigned Case",
10
- label: "Assigned",
11
- badgeVariant: "info",
12
- markerClassName: "border-[hsl(var(--status-info-foreground)/0.35)] bg-[hsl(var(--status-info))]",
11
+ title: "Assigned case",
12
+ label: "ASSIGNED",
13
+ icon: "route",
13
14
  },
14
15
  reassigned: {
15
- title: "Reassigned Case",
16
- label: "Reassigned",
17
- badgeVariant: "accent",
18
- markerClassName: "border-[hsl(var(--status-accent-foreground)/0.35)] bg-[hsl(var(--status-accent))]",
16
+ title: "Reassigned case",
17
+ label: "REASSIGNED",
18
+ icon: "route",
19
19
  },
20
20
  returned: {
21
- title: "Returned Case",
22
- label: "Returned",
23
- badgeVariant: "warning",
24
- markerClassName: "border-[hsl(var(--status-warning-foreground)/0.35)] bg-[hsl(var(--status-warning))]",
21
+ title: "Case returned",
22
+ label: "RETURNED",
23
+ icon: "route",
25
24
  },
26
25
  routed: {
27
- title: "Case Transferred",
28
- label: "Routed",
29
- badgeVariant: "secondary",
30
- markerClassName: "border-primary/30 bg-primary/15",
26
+ title: "Case routed",
27
+ label: "ROUTED",
28
+ icon: "route",
29
+ },
30
+ "message-received": {
31
+ title: "Received from applicant",
32
+ label: "RECEIVED",
33
+ detailLabel: "Response from applicant:",
34
+ icon: "mail",
35
+ },
36
+ "message-sent": {
37
+ title: "Sent to applicant",
38
+ label: "SENT",
39
+ detailLabel: "Officer sent a message:",
40
+ icon: "mail",
31
41
  },
32
42
  system: {
33
- title: "System Update",
34
- label: "System",
35
- badgeVariant: "muted",
36
- markerClassName: "border-muted-foreground/30 bg-muted",
43
+ title: "System update",
44
+ label: "SYSTEM",
45
+ icon: "route",
37
46
  },
38
47
  };
39
- function RoutingTimelineHandoffEndpoint({ value, }) {
48
+ function RoutingTimelineHandoffEndpoint({ value, label, }) {
40
49
  const isTextValue = typeof value === "string" || typeof value === "number";
41
- return isTextValue ? (_jsx("span", { className: "min-w-0 truncate font-medium text-card-foreground", children: value })) : (_jsx("div", { className: "min-w-0", children: value }));
50
+ if (!isTextValue) {
51
+ return (_jsx("div", { "data-slot": "routing-timeline-profile", "data-avatar-size": "xs", className: "min-w-0", children: value }));
52
+ }
53
+ const name = String(value);
54
+ const fallback = name
55
+ .split(/\s+/)
56
+ .filter(Boolean)
57
+ .slice(0, 2)
58
+ .map((part) => part.charAt(0))
59
+ .join("")
60
+ .toUpperCase();
61
+ return (_jsx("div", { "data-slot": "routing-timeline-profile", "data-avatar-size": "xs", className: "min-w-0", children: _jsx(AvatarProfile, { size: "xs", fallback: fallback || "?", name: name, role: label, rolePlacement: "below" }) }));
62
+ }
63
+ function RoutingEventIcon({ icon, kind, density, }) {
64
+ if (icon)
65
+ return _jsx(_Fragment, { children: icon });
66
+ return routingKindConfig[kind].icon === "mail" ? (_jsx(Mail, { "aria-hidden": "true", className: density === "compact" ? "size-4" : "size-6" })) : (_jsx(Route, { "aria-hidden": "true", className: density === "compact" ? "size-4" : "size-6" }));
42
67
  }
43
- function RoutingTimeline({ items, emptyMessage = "No routing activity yet.", className, ...props }) {
68
+ function RoutingTimeline({ items, emptyMessage = "No routing activity yet.", density = "default", className, ...props }) {
44
69
  if (items.length === 0) {
45
- return (_jsx(EmptyState, { "data-slot": "routing-timeline", message: emptyMessage, className: cn("border-0 bg-transparent px-0 py-[var(--space-6)]", className), ...props }));
70
+ return (_jsx(EmptyState, { "data-slot": "routing-timeline", message: emptyMessage, className: cn("border-0 bg-transparent px-0", density === "compact"
71
+ ? "py-[var(--space-4)]"
72
+ : "py-[var(--space-6)]", className), ...props }));
46
73
  }
47
- return (_jsx("div", { "data-slot": "routing-timeline", className: cn("min-w-0", className), ...props, children: _jsx("ol", { "data-slot": "routing-timeline-list", className: "relative space-y-0", children: items.map((item) => {
48
- var _a, _b, _c, _d;
74
+ return (_jsx("div", { "data-slot": "routing-timeline", "data-density": density, className: cn("min-w-0", className), ...props, children: _jsx("ol", { "data-slot": "routing-timeline-list", className: cn(density === "compact"
75
+ ? "space-y-[var(--space-4)]"
76
+ : "space-y-[32px]"), children: items.map((item) => {
77
+ var _a, _b, _c, _d, _e;
49
78
  const kind = (_a = item.kind) !== null && _a !== void 0 ? _a : "routed";
50
79
  const config = routingKindConfig[kind];
51
80
  const hasRouteEndpoints = Boolean(item.from || item.to);
52
81
  const hasCompleteRoute = Boolean(item.from && item.to);
53
82
  const title = (_b = item.title) !== null && _b !== void 0 ? _b : config.title;
54
83
  const remark = (_c = item.remark) !== null && _c !== void 0 ? _c : item.description;
55
- return (_jsxs("li", { className: "relative grid grid-cols-[var(--space-3)_minmax(0,1fr)] gap-[var(--space-3)] pb-[var(--space-5)] last:pb-0", children: [_jsx("div", { className: "relative flex justify-center", children: _jsx("span", { "aria-hidden": "true", "data-slot": "routing-timeline-marker", className: cn("relative z-10 mt-[var(--space-1)] size-[var(--space-2)] rounded-full border", config.markerClassName) }) }), _jsxs("div", { "data-slot": "routing-timeline-event-card", className: "min-w-0 rounded-surface border border-border/70 bg-card p-[var(--space-3)] text-card-foreground", children: [_jsxs("div", { className: "min-w-0 space-y-[var(--space-1)]", children: [_jsxs("div", { className: "flex min-w-0 items-start justify-between gap-[var(--space-2)]", children: [_jsx(Typography, { as: "p", variant: "label", className: "min-w-0 flex-1 font-semibold", children: title }), _jsx(Badge, { variant: config.badgeVariant, bordered: true, className: "max-w-full shrink-0 self-start", children: (_d = item.badgeLabel) !== null && _d !== void 0 ? _d : config.label })] }), item.timestamp || item.actor ? (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-[var(--space-1)] gap-y-[var(--space-1)]", children: [item.timestamp ? (_jsx(Typography, { as: "time", variant: "caption", className: "shrink-0 text-muted-foreground/60", children: item.timestamp })) : null, item.actor ? (_jsxs(Typography, { as: "p", variant: "caption", className: "inline-flex min-w-0 items-center gap-x-[var(--space-1)]", children: [item.timestamp ? (_jsx("span", { "aria-hidden": "true", children: "\u00B7" })) : null, "By ", item.actor] })) : null] })) : null] }), remark ? (_jsx(Typography, { as: "div", variant: "bodySm", "data-slot": "routing-timeline-remark", className: "mt-[var(--space-4)] font-normal", children: remark })) : null, hasRouteEndpoints ? (_jsxs("footer", { "data-slot": "routing-timeline-handoff", "data-complete-route": hasCompleteRoute ? "true" : undefined, className: "mt-[var(--space-4)] flex flex-col space-y-[var(--space-2)] text-xs text-muted-foreground", children: [item.from ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.from })) : null, item.from && item.to ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "sr-only", children: "to" }), _jsx(ArrowDown, { "aria-hidden": "true", "data-slot": "routing-timeline-handoff-arrow", className: "ml-1 size-4 shrink-0 text-muted-foreground" })] })) : null, item.to ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.to })) : null] })) : null] })] }, item.id));
84
+ const detailLabel = (_d = item.detailLabel) !== null && _d !== void 0 ? _d : config.detailLabel;
85
+ const showDefaultBadge = item.badgeLabel !== null;
86
+ return (_jsx("li", { children: _jsx(ActivityCard, { "data-slot": "routing-timeline-event-card", density: density, icon: _jsx(RoutingEventIcon, { icon: item.icon, kind: kind, density: density }), title: title, meta: item.timestamp, trailing: item.badge ? (item.badge) : showDefaultBadge ? (_jsx(Badge, { variant: "outline", bordered: true, className: "max-w-full shrink-0", children: (_e = item.badgeLabel) !== null && _e !== void 0 ? _e : config.label })) : null, children: hasRouteEndpoints || remark ? (_jsxs(ActivityCardContent, { density: density, children: [hasRouteEndpoints ? (_jsxs("div", { "data-slot": "routing-timeline-handoff", "data-complete-route": hasCompleteRoute ? "true" : undefined, className: cn("flex min-w-0 items-center", density === "compact"
87
+ ? "gap-[var(--space-3)]"
88
+ : "gap-[24px]"), children: [item.from ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.from, label: item.fromLabel })) : null, item.from && item.to ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "sr-only", children: "to" }), _jsx(ArrowRight, { "aria-hidden": "true", "data-slot": "routing-timeline-handoff-arrow", className: cn("shrink-0 text-foreground", density === "compact" ? "size-4" : "size-6") })] })) : null, item.to ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.to, label: item.toLabel })) : null] })) : null, remark ? (_jsxs("div", { "data-slot": "routing-timeline-remark", className: cn("bg-muted", density === "compact"
89
+ ? "rounded-control p-[var(--space-2)]"
90
+ : "rounded-md p-[12px]"), children: [detailLabel ? (_jsx(Typography, { as: "p", variant: "bodySm", className: "font-semibold text-instructional", children: detailLabel })) : null, _jsx(Typography, { as: "div", variant: "bodySm", className: cn(detailLabel &&
91
+ (density === "compact"
92
+ ? "mt-[var(--space-1)]"
93
+ : "mt-[4px]")), children: remark })] })) : null] })) : null }) }, item.id));
56
94
  }) }) }));
57
95
  }
58
96
  export { RoutingTimeline };
@@ -4,9 +4,13 @@ export interface SearchInputProps extends Omit<React.ComponentProps<typeof Input
4
4
  value: string;
5
5
  onValueChange: (value: string) => void;
6
6
  label?: React.ReactNode;
7
+ /** Visually hides the label while keeping it available to assistive technology. */
8
+ hideLabel?: boolean;
7
9
  description?: React.ReactNode;
8
10
  error?: React.ReactNode;
9
11
  invalid?: boolean;
12
+ /** Keeps the control visually marked after a search result is selected. */
13
+ selected?: boolean;
10
14
  containerClassName?: string;
11
15
  labelClassName?: string;
12
16
  descriptionClassName?: string;
@@ -6,7 +6,7 @@ import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
6
6
  import { InputControl } from "./input-control";
7
7
  import { cn } from "./lib/utils";
8
8
  import { SearchInputShell } from "./search-input-shell";
9
- const SearchInput = React.forwardRef(({ value, onValueChange, label = "Search", description, error, invalid = false, placeholder = "Search...", containerClassName, labelClassName, descriptionClassName, errorClassName, onSearch, searchDelay = 300, searchOnChange = true, searchOnEnter = true, className, id, disabled, onKeyDown, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, ...props }, ref) => {
9
+ const SearchInput = React.forwardRef(({ value, onValueChange, label = "Search", hideLabel = true, description, error, invalid = false, selected = false, placeholder = "Search...", containerClassName, labelClassName, descriptionClassName, errorClassName, onSearch, searchDelay = 300, searchOnChange = true, searchOnEnter = true, className, id, disabled, onKeyDown, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, ...props }, ref) => {
10
10
  const searchTimeoutRef = React.useRef();
11
11
  React.useEffect(() => {
12
12
  return () => {
@@ -64,7 +64,7 @@ const SearchInput = React.forwardRef(({ value, onValueChange, label = "Search",
64
64
  }
65
65
  onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
66
66
  }, [onKeyDown, runSearch, searchOnEnter]);
67
- return (_jsxs(Field, { className: containerClassName, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: inputId, className: cn("sr-only", labelClassName), children: label }), _jsxs(SearchInputShell, { children: [_jsx(InputControl, { ref: ref, id: inputId, type: "text", placeholder: placeholder, value: value, onChange: handleChange, onKeyDown: handleKeyDown, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: cn("bg-background pl-8 pr-7", className), ...props }), value ? (_jsx("button", { type: "button", onClick: () => {
67
+ return (_jsxs(Field, { className: containerClassName, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: inputId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsxs(SearchInputShell, { children: [_jsx(InputControl, { ref: ref, id: inputId, type: "text", placeholder: placeholder, value: value, onChange: handleChange, onKeyDown: handleKeyDown, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, "data-selected": selected ? "true" : undefined, className: cn("bg-background pl-8 pr-7 data-[selected=true]:border-interactive data-[selected=true]:bg-highlight data-[selected=true]:ring-[3px] data-[selected=true]:ring-interactive/20", className), ...props }), value ? (_jsx("button", { type: "button", onClick: () => {
68
68
  onValueChange("");
69
69
  runSearch("");
70
70
  }, disabled: disabled, className: "absolute right-1.5 top-1/2 -translate-y-1/2 cursor-pointer rounded-sm p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:pointer-events-none", "aria-label": "Clear search", children: _jsx(XIcon, { size: 14 }) })) : null] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
@@ -36,7 +36,7 @@ const SelectContent = React.forwardRef(({ className, children, header, position
36
36
  SelectContent.displayName = SelectPrimitive.Content.displayName;
37
37
  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 })));
38
38
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
39
- 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] })] })));
39
+ 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]:cursor-not-allowed data-[disabled]:opacity-50 data-[disabled]:hover:bg-transparent data-[disabled]:hover:text-foreground", 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] })] })));
40
40
  SelectItem.displayName = SelectPrimitive.Item.displayName;
41
41
  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 })));
42
42
  SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
package/dist/select.d.ts CHANGED
@@ -5,6 +5,7 @@ export interface SelectOption {
5
5
  value: string;
6
6
  description?: React.ReactNode;
7
7
  disabled?: boolean;
8
+ disabledReason?: React.ReactNode;
8
9
  group?: string;
9
10
  textValue?: string;
10
11
  }
@@ -12,6 +13,7 @@ export interface SelectProps extends Pick<React.ComponentProps<typeof SelectRoot
12
13
  label: React.ReactNode;
13
14
  options: SelectOption[];
14
15
  placeholder?: string;
16
+ size?: "default" | "comfortable";
15
17
  description?: React.ReactNode;
16
18
  error?: React.ReactNode;
17
19
  invalid?: boolean;
@@ -28,5 +30,5 @@ export interface SelectProps extends Pick<React.ComponentProps<typeof SelectRoot
28
30
  errorClassName?: string;
29
31
  id?: string;
30
32
  }
31
- declare function Select({ label, options, placeholder, description, error, invalid, hideLabel, searchable, searchPlaceholder, searchEmptyMessage, searchInputAriaLabel, className, triggerClassName, contentClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, open, onOpenChange, ...props }: SelectProps): import("react/jsx-runtime").JSX.Element;
33
+ declare function Select({ label, options, placeholder, size, description, error, invalid, hideLabel, searchable, searchPlaceholder, searchEmptyMessage, searchInputAriaLabel, className, triggerClassName, contentClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, open, onOpenChange, ...props }: SelectProps): import("react/jsx-runtime").JSX.Element;
32
34
  export { Select };
package/dist/select.js CHANGED
@@ -5,6 +5,7 @@ import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
5
5
  import { InputControl } from "./input-control";
6
6
  import { cn } from "./lib/utils";
7
7
  import { usePopoverTabExit } from "./popover-focus";
8
+ import { Tooltip } from "./tooltip";
8
9
  import { SearchHighlight } from "./use-search-highlight";
9
10
  import { SelectContent, SelectGroup, SelectItem, SelectLabel, SelectRoot, SelectSeparator, SelectTrigger, SelectValue, } from "./select-primitive";
10
11
  function groupOptions(options) {
@@ -40,7 +41,7 @@ function renderHighlightedLabel(label, query) {
40
41
  }
41
42
  return label;
42
43
  }
43
- function Select({ label, options, placeholder = "Select an option", description, error, invalid = false, hideLabel = false, searchable = false, searchPlaceholder = "Search options...", searchEmptyMessage = "No options found.", searchInputAriaLabel = "Search options", className, triggerClassName, contentClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, open, onOpenChange, ...props }) {
44
+ function Select({ label, options, placeholder = "Select an option", size = "default", description, error, invalid = false, hideLabel = false, searchable = false, searchPlaceholder = "Search options...", searchEmptyMessage = "No options found.", searchInputAriaLabel = "Search options", className, triggerClassName, contentClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, open, onOpenChange, ...props }) {
44
45
  const generatedId = React.useId();
45
46
  const selectId = id !== null && id !== void 0 ? id : `select-${generatedId}`;
46
47
  const triggerRef = React.useRef(null);
@@ -153,7 +154,8 @@ function Select({ label, options, placeholder = "Select an option", description,
153
154
  searchable,
154
155
  searchQuery,
155
156
  ]);
156
- return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: selectId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsxs(SelectRoot, { disabled: disabled, open: isOpen, onOpenChange: handleOpenChange, ...props, children: [_jsx(SelectTrigger, { ref: triggerRef, id: selectId, className: triggerClassName, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { ref: contentRef, className: contentClassName, onCloseAutoFocus: handleCloseAutoFocus, onKeyDownCapture: handleContentKeyDownCapture, header: searchable ? (_jsx(InputControl, { ref: searchInputRef, value: searchQuery, onChange: (event) => setSearchQuery(event.target.value), placeholder: searchPlaceholder, "aria-label": searchInputAriaLabel, autoComplete: "off", className: "h-control border-0 bg-transparent px-2 py-0 text-sm shadow-none", onKeyDown: (event) => {
157
+ return (_jsxs(Field, { className: cn(size === "comfortable" && "gap-[8px]", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: selectId, className: cn(size === "comfortable" && "text-[16px] leading-[24px]", hideLabel && "sr-only", labelClassName), children: label }), _jsxs(SelectRoot, { disabled: disabled, open: isOpen, onOpenChange: handleOpenChange, ...props, children: [_jsx(SelectTrigger, { ref: triggerRef, id: selectId, className: cn(size === "comfortable" &&
158
+ "h-[46px] px-[16px] py-[11px] text-[16px] leading-[24px]", triggerClassName), "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { ref: contentRef, className: contentClassName, onCloseAutoFocus: handleCloseAutoFocus, onKeyDownCapture: handleContentKeyDownCapture, header: searchable ? (_jsx(InputControl, { ref: searchInputRef, value: searchQuery, onChange: (event) => setSearchQuery(event.target.value), placeholder: searchPlaceholder, "aria-label": searchInputAriaLabel, autoComplete: "off", className: "h-control border-0 bg-transparent px-2 py-0 text-sm shadow-none", onKeyDown: (event) => {
157
159
  if (event.key === "ArrowDown" ||
158
160
  event.key === "ArrowUp") {
159
161
  event.preventDefault();
@@ -167,7 +169,10 @@ function Select({ label, options, placeholder = "Select an option", description,
167
169
  event.stopPropagation();
168
170
  } })) : undefined, viewportClassName: searchable ? "max-h-64" : undefined, children: hasVisibleOptions ? (optionGroups.map((group, groupIndex) => {
169
171
  var _a;
170
- return (_jsxs(React.Fragment, { children: [_jsxs(SelectGroup, { children: [group.group ? (_jsx(SelectLabel, { children: _jsx(SearchHighlight, { text: group.group, query: searchQuery }) })) : null, group.options.map((option) => (_jsx(SelectItem, { value: option.value, disabled: option.disabled, textValue: getOptionTextValue(option), description: option.description, children: renderHighlightedLabel(option.label, searchQuery) }, option.value)))] }), groupIndex < optionGroups.length - 1 ? _jsx(SelectSeparator, {}) : null] }, (_a = group.group) !== null && _a !== void 0 ? _a : `group-${groupIndex}`));
171
- })) : (_jsx("div", { className: "px-2.5 py-1.5 text-sm text-muted-foreground", children: searchEmptyMessage })) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
172
+ return (_jsxs(React.Fragment, { children: [_jsxs(SelectGroup, { children: [group.group ? (_jsx(SelectLabel, { children: _jsx(SearchHighlight, { text: group.group, query: searchQuery }) })) : null, group.options.map((option) => {
173
+ const optionItem = (_jsx(SelectItem, { value: option.value, disabled: option.disabled, textValue: getOptionTextValue(option), description: option.description, children: renderHighlightedLabel(option.label, searchQuery) }));
174
+ return option.disabled && option.disabledReason ? (_jsx(Tooltip, { content: option.disabledReason, side: "right", children: optionItem }, option.value)) : (_jsx(React.Fragment, { children: optionItem }, option.value));
175
+ })] }), groupIndex < optionGroups.length - 1 ? (_jsx(SelectSeparator, {})) : null] }, (_a = group.group) !== null && _a !== void 0 ? _a : `group-${groupIndex}`));
176
+ })) : (_jsx("div", { className: "px-2.5 py-1.5 text-sm text-muted-foreground", children: searchEmptyMessage })) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: cn(size === "comfortable" && "text-[14px] leading-[20px]", descriptionClassName), children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
172
177
  }
173
178
  export { Select };
@@ -0,0 +1,13 @@
1
+ import * as React from "react";
2
+ export type StatusDotVariant = "default" | "muted" | "success" | "warning" | "info" | "destructive";
3
+ export type StatusDotSize = "sm" | "md" | "lg";
4
+ export interface StatusDotProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
5
+ /** Semantic colour of the status. */
6
+ variant?: StatusDotVariant;
7
+ /** Diameter of the dot. */
8
+ size?: StatusDotSize;
9
+ /** Accessible name. Omit when nearby text already describes the status. */
10
+ label?: string;
11
+ }
12
+ declare const StatusDot: React.ForwardRefExoticComponent<StatusDotProps & React.RefAttributes<HTMLSpanElement>>;
13
+ export { StatusDot };
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "./lib/utils";
4
+ const sizeClasses = {
5
+ sm: "size-[6px]",
6
+ md: "size-[8px]",
7
+ lg: "size-[10px]",
8
+ };
9
+ const variantClasses = {
10
+ default: "bg-primary",
11
+ muted: "bg-muted-foreground",
12
+ success: "bg-[hsl(var(--nea-green-400))]",
13
+ warning: "bg-[hsl(var(--nea-amber-400))]",
14
+ info: "bg-[hsl(var(--nea-blue-400))]",
15
+ destructive: "bg-[hsl(var(--nea-red-400))]",
16
+ };
17
+ const StatusDot = React.forwardRef(({ variant = "default", size = "md", label, className, ...props }, ref) => (_jsx("span", { ref: ref, "data-slot": "status-dot", role: label ? "img" : undefined, "aria-label": label, "aria-hidden": label ? undefined : true, className: cn("inline-block shrink-0 rounded-full", sizeClasses[size], variantClasses[variant], className), ...props })));
18
+ StatusDot.displayName = "StatusDot";
19
+ export { StatusDot };
package/dist/styles.css CHANGED
@@ -413,25 +413,6 @@
413
413
  }
414
414
 
415
415
  @layer components {
416
- [data-slot="routing-timeline"] {
417
- --routing-timeline-background: var(--layout-right-panel-background);
418
- }
419
-
420
- [data-slot="routing-timeline-list"]::before {
421
- position: absolute;
422
- top: var(--space-2);
423
- bottom: var(--space-0);
424
- left: calc(var(--space-3) / 2);
425
- width: 1px;
426
- content: "";
427
- transform: translateX(-50%);
428
- background: hsl(var(--border));
429
- }
430
-
431
- [data-slot="routing-timeline-marker"] {
432
- box-shadow: 0 0 0 var(--space-1) hsl(var(--routing-timeline-background));
433
- }
434
-
435
416
  [data-slot="routing-timeline-event-card"] {
436
417
  container-type: inline-size;
437
418
  }
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { FilterIcon } from "./animated-icons";
5
+ import { buttonVariants } from "./button";
5
6
  import { DateInput } from "./date-input";
6
7
  import { FilterPopover } from "./filter-popover";
7
8
  import { FilterToggle, filterToggleVariants, } from "./filter-toggle";
@@ -29,7 +30,7 @@ function getOptionsFilterSummary(filter) {
29
30
  }
30
31
  return ((_c = (_b = filter.options.find((option) => option.value === filter.values[0])) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : filter.values[0]);
31
32
  }
32
- const TableFilterTrigger = React.forwardRef(({ active, disabled, label, children, ...props }, ref) => (_jsx(FilterToggle, { ref: ref, type: "button", disabled: disabled, selected: active, "aria-label": `Filter by ${label}${active ? ", active" : ""}`, className: cn("h-9 w-full min-w-0 justify-start overflow-hidden px-2 text-sm font-normal", !active &&
33
+ const TableFilterTrigger = React.forwardRef(({ active, disabled, label, children, ...props }, ref) => (_jsx(FilterToggle, { ref: ref, type: "button", disabled: disabled, selected: active, "aria-label": `Filter by ${label}${active ? ", active" : ""}`, className: cn("h-8 w-full min-w-0 justify-start overflow-hidden px-2 text-sm font-normal", !active &&
33
34
  "bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground"), icon: _jsx(FilterIcon, { "aria-hidden": "true", className: "shrink-0", size: 16 }), ...props, children: _jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: children }) })));
34
35
  TableFilterTrigger.displayName = "TableFilterTrigger";
35
36
  function TextColumnFilter({ filter, label, }) {
@@ -71,15 +72,16 @@ function DateColumnFilter({ filter, label, }) {
71
72
  var _a, _b;
72
73
  const active = isTableColumnFilterActive(filter);
73
74
  const accessibleLabel = (_a = filter.label) !== null && _a !== void 0 ? _a : label;
74
- return (_jsx(DateInput, { label: `Filter by ${accessibleLabel}${active ? ", active" : ""}`, hideLabel: true, value: filter.value, onValueChange: filter.onValueChange, placeholder: (_b = filter.placeholder) !== null && _b !== void 0 ? _b : "Pick a date", disabled: filter.disabled, minDate: filter.minDate, maxDate: filter.maxDate, clearable: true, clearLabel: `Clear ${accessibleLabel} filter`, className: "gap-0", triggerClassName: cn(filterToggleVariants({ selected: active }), "h-9 w-full min-w-0 justify-start text-sm font-normal shadow-none", active ? "pl-2 pr-16" : "pl-2 pr-9", !active &&
75
- "border-0 bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground") }));
75
+ return (_jsx(DateInput, { label: `Filter by ${accessibleLabel}${active ? ", active" : ""}`, hideLabel: true, value: filter.value, onValueChange: filter.onValueChange, placeholder: (_b = filter.placeholder) !== null && _b !== void 0 ? _b : "Pick a date", popoverTitle: `Filter by ${accessibleLabel}`, disabled: filter.disabled, minDate: filter.minDate, maxDate: filter.maxDate, clearable: true, clearLabel: `Clear ${accessibleLabel} filter`, className: "gap-0", triggerClassName: cn(buttonVariants({ variant: "outline", size: "sm" }), "h-8 w-full min-w-0 justify-start font-normal shadow-none", active ? "pl-2 pr-16" : "pl-2 pr-9", active
76
+ ? "border-transparent bg-highlight text-highlight-foreground hover:bg-highlight/80"
77
+ : "bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground") }));
76
78
  }
77
79
  function DateRangeColumnFilter({ filter, label, }) {
78
80
  var _a, _b;
79
81
  const active = isTableColumnFilterActive(filter);
80
82
  const accessibleLabel = (_a = filter.label) !== null && _a !== void 0 ? _a : label;
81
- return (_jsx(DateInput, { mode: "range", label: `Filter by ${accessibleLabel}${active ? ", active" : ""}`, hideLabel: true, value: filter.value, onValueChange: filter.onValueChange, placeholder: (_b = filter.placeholder) !== null && _b !== void 0 ? _b : "Pick date range", disabled: filter.disabled, minDate: filter.minDate, maxDate: filter.maxDate, clearable: true, clearLabel: `Clear ${accessibleLabel} filter`, className: "gap-0", triggerClassName: cn(filterToggleVariants({ selected: active }), "h-9 w-full min-w-0 justify-start text-sm font-normal shadow-none", active ? "pl-2 pr-16" : "pl-2 pr-9", !active &&
82
- "border-0 bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground") }));
83
+ return (_jsx(DateInput, { mode: "range", label: `Filter by ${accessibleLabel}${active ? ", active" : ""}`, hideLabel: true, value: filter.value, onValueChange: filter.onValueChange, placeholder: (_b = filter.placeholder) !== null && _b !== void 0 ? _b : "Pick date range", popoverTitle: `Filter by ${accessibleLabel}`, disabled: filter.disabled, minDate: filter.minDate, maxDate: filter.maxDate, clearable: true, clearLabel: `Clear ${accessibleLabel} filter`, className: "gap-0", triggerClassName: cn(filterToggleVariants({ selected: active }), "h-8 w-full min-w-0 justify-start border-0 text-sm font-normal shadow-none", active ? "pl-2 pr-16" : "pl-2 pr-9", !active &&
84
+ "bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground") }));
83
85
  }
84
86
  function TableColumnFilterControl({ filter, label, }) {
85
87
  if (filter.type === "text") {
package/dist/table.d.ts CHANGED
@@ -123,6 +123,12 @@ export interface TableRowExpansionProps<Row> {
123
123
  /** Accessible label for a row's expansion toggle. */
124
124
  getToggleLabel?: (row: Row, rowIndex: number, expanded: boolean) => string;
125
125
  }
126
+ export interface TableRowIndicatorProps<Row> {
127
+ /** Renders a compact status marker in a fixed leading column for each row. */
128
+ render: (row: Row, rowIndex: number) => React.ReactNode;
129
+ /** Accessible name for the otherwise visually empty indicator column header. */
130
+ headerLabel?: string;
131
+ }
126
132
  interface SortableTableHeadProps extends Omit<React.ThHTMLAttributes<HTMLTableCellElement>, "children" | "style"> {
127
133
  field: string;
128
134
  children: React.ReactNode;
@@ -194,6 +200,8 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
194
200
  emptyStateMinHeight?: React.CSSProperties["minHeight"];
195
201
  /** When true, applies alternating row backgrounds for easier scanning. */
196
202
  striped?: boolean;
203
+ /** Adds a compact, non-reorderable status column at the leading edge of every row. */
204
+ rowIndicator?: TableRowIndicatorProps<Row>;
197
205
  /** Adds a disclosure column and full-width expandable content for nested tables or row details. */
198
206
  rowExpansion?: TableRowExpansionProps<Row>;
199
207
  /** When true, non-sticky header cells become draggable for column reordering. */
@@ -201,5 +209,5 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
201
209
  /** Called with the new ordered column key array after a drag-and-drop reorder. */
202
210
  onColumnOrderChange?: (columnKeys: string[]) => void;
203
211
  }
204
- 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, reorderableColumns, onColumnOrderChange, onScroll, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
212
+ 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, rowIndicator, rowExpansion, reorderableColumns, onColumnOrderChange, onScroll, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
205
213
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableRowHeader, TableCaption, SortableTableHead, TableRowSkeleton, };
package/dist/table.js CHANGED
@@ -19,6 +19,7 @@ const DEFAULT_COLUMN_RESIZE_MIN_WIDTH = 64;
19
19
  const DEFAULT_COLUMN_RESIZE_START_WIDTH = 160;
20
20
  const DEFAULT_COLUMN_RESIZE_MAX_WIDTH = 1200;
21
21
  const COLUMN_RESIZE_KEYBOARD_STEP = 16;
22
+ const TABLE_ROW_INDICATOR_COLUMN_WIDTH = 32;
22
23
  const TABLE_EXPANSION_COLUMN_WIDTH = 48;
23
24
  const TABLE_MIN_WIDTH_CSS_VAR = "--table-min-width";
24
25
  const DEFAULT_TABLE_MIN_HEIGHT = "var(--table-min-height, var(--app-table-min-height, 24rem))";
@@ -159,7 +160,7 @@ const TableFooter = React.forwardRef(({ className, ...props }, ref) => (_jsx("tf
159
160
  TableFooter.displayName = "TableFooter";
160
161
  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 })));
161
162
  TableRow.displayName = "TableRow";
162
- 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({
163
+ 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-[4px] text-left align-middle font-medium text-instructional [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
163
164
  style: {
164
165
  ...style,
165
166
  fontWeight: 500,
@@ -170,7 +171,7 @@ const TableHead = React.forwardRef(({ className, style, width, minWidth, maxWidt
170
171
  treatWidthAsMinWidth,
171
172
  }), ...props })));
172
173
  TableHead.displayName = "TableHead";
173
- const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, ...props }, ref) => (_jsx("td", { ref: ref, className: cn("h-12 px-4 py-2 align-middle text-card-foreground [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
174
+ const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, ...props }, ref) => (_jsx("td", { ref: ref, className: cn("h-12 px-4 py-[4px] align-middle text-card-foreground [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
174
175
  style,
175
176
  width,
176
177
  minWidth,
@@ -178,7 +179,7 @@ const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidt
178
179
  treatWidthAsMinWidth,
179
180
  }), ...props })));
180
181
  TableCell.displayName = "TableCell";
181
- 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({
182
+ 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-[4px] 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({
182
183
  style,
183
184
  width,
184
185
  minWidth,
@@ -240,7 +241,7 @@ function SkeletonCell({ config }) {
240
241
  return (_jsx(TableCell, { children: _jsx(SkeletonBlock, { width: config.width, height: config.height, badge: config.badge }) }));
241
242
  }
242
243
  function TableRowSkeleton({ columns, rows = 1 }) {
243
- return (_jsx(_Fragment, { children: Array.from({ length: rows }).map((_, rowIndex) => (_jsx(TableRow, { children: columns.map((config, columnIndex) => (_jsx(SkeletonCell, { config: config }, columnIndex))) }, rowIndex))) }));
244
+ return (_jsx(_Fragment, { children: Array.from({ length: rows }).map((_, rowIndex) => (_jsx(TableRow, { "data-slot": "table-skeleton-row", className: "h-12", children: columns.map((config, columnIndex) => (_jsx(SkeletonCell, { config: config }, columnIndex))) }, rowIndex))) }));
244
245
  }
245
246
  export const TABLE_SKELETON_PRESETS = {
246
247
  text: (width = "8rem", height = "1rem") => ({
@@ -278,7 +279,7 @@ const SortableTableHead = React.forwardRef(function SortableTableHead({ field, c
278
279
  ? currentSortDirection === "asc"
279
280
  ? "ascending"
280
281
  : "descending"
281
- : undefined, className: cn(resizeHandle && "relative", alignClassName(align), className), style: style, wrap: allowWrap, width: width, minWidth: minWidth, maxWidth: maxWidth, treatWidthAsMinWidth: treatWidthAsMinWidth, containOverflow: containOverflow, ...rest, children: [gripIcon, _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
282
+ : undefined, className: cn(resizeHandle && "relative", alignClassName(align), className), style: style, wrap: allowWrap, width: width, minWidth: minWidth, maxWidth: maxWidth, treatWidthAsMinWidth: treatWidthAsMinWidth, containOverflow: containOverflow, ...rest, children: [gripIcon, _jsxs("button", { type: "button", onClick: () => onSort(field), className: cn("-ml-1.5 flex h-auto min-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
282
283
  ? "max-w-full whitespace-normal text-left"
283
284
  : "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 &&
284
285
  allowWrap &&
@@ -398,8 +399,10 @@ function getColumnPairResizeRange(column, nextColumn, columnWidths) {
398
399
  maxWidth: Math.round(currentWidth + maxDelta),
399
400
  };
400
401
  }
401
- 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, reorderableColumns = false, onColumnOrderChange, onScroll, style, ...props }) {
402
+ 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, rowIndicator, rowExpansion, reorderableColumns = false, onColumnOrderChange, onScroll, style, ...props }) {
403
+ var _a;
402
404
  const visibleColumns = getVisibleColumns(columns, visibleColumnKeys);
405
+ const hasRowIndicatorColumn = Boolean(rowIndicator);
403
406
  const hasExpansionColumn = Boolean(rowExpansion);
404
407
  const hasColumnFilters = visibleColumns.some((column) => column.filter);
405
408
  const isGrowResizeMode = columnResizeMode === "grow";
@@ -413,6 +416,7 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
413
416
  const hasResolvedColumnWidths = hasColumnWidthOverrides(resolvedColumnWidths);
414
417
  const growTableMinWidth = isGrowResizeMode
415
418
  ? getTableMinWidth(visibleColumns, resolvedColumnWidths) +
419
+ (hasRowIndicatorColumn ? TABLE_ROW_INDICATOR_COLUMN_WIDTH : 0) +
416
420
  (hasExpansionColumn ? TABLE_EXPANSION_COLUMN_WIDTH : 0)
417
421
  : undefined;
418
422
  const resolvedMinHeight = minHeight !== null && minHeight !== void 0 ? minHeight : (centerEmptyState ? DEFAULT_TABLE_MIN_HEIGHT : undefined);
@@ -673,7 +677,11 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
673
677
  }, ...props, children: [_jsxs(TableRoot, { "aria-busy": loading, className: cn(shouldUseFixedLayout && "table-fixed", isGrowResizeMode &&
674
678
  (hasResolvedColumnWidths
675
679
  ? "w-[var(--table-min-width)]"
676
- : "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: {
680
+ : "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: [hasRowIndicatorColumn ? (_jsx("col", { style: {
681
+ width: `${TABLE_ROW_INDICATOR_COLUMN_WIDTH}px`,
682
+ minWidth: `${TABLE_ROW_INDICATOR_COLUMN_WIDTH}px`,
683
+ maxWidth: `${TABLE_ROW_INDICATOR_COLUMN_WIDTH}px`,
684
+ } })) : null, hasExpansionColumn ? (_jsx("col", { style: {
677
685
  width: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
678
686
  minWidth: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
679
687
  maxWidth: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
@@ -686,14 +694,17 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
686
694
  minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled),
687
695
  maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled),
688
696
  treatWidthAsMinWidth: !isColumnResizingEnabled,
689
- }) }, 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, renderColumnHeaders(visibleColumns)] }), 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) => {
697
+ }) }, column.key)))] }), _jsxs(TableHeader, { children: [_jsxs(TableRow, { children: [hasRowIndicatorColumn ? (_jsx(TableHead, { "data-slot": "table-row-indicator-head", "aria-label": (_a = rowIndicator === null || rowIndicator === void 0 ? void 0 : rowIndicator.headerLabel) !== null && _a !== void 0 ? _a : "Row status", scope: "col", className: "w-8 px-2" })) : null, hasExpansionColumn ? (_jsx(TableHead, { "data-slot": "table-expansion-head", "aria-label": "Expand rows", scope: "col", className: "w-12 px-2" })) : null, renderColumnHeaders(visibleColumns)] }), hasColumnFilters ? (_jsxs(TableRow, { "data-slot": "table-filter-row", className: "!bg-card", children: [hasRowIndicatorColumn ? (_jsx(TableCell, { "data-slot": "table-row-indicator-filter-cell", "aria-hidden": "true", className: "h-11 w-8 px-2 py-[4px]" })) : null, hasExpansionColumn ? (_jsx(TableCell, { "data-slot": "table-expansion-filter-cell", "aria-hidden": "true", className: "h-11 w-12 px-2 py-[4px]" })) : null, visibleColumns.map((column) => {
690
698
  const columnWidth = getRenderedColumnWidth({
691
699
  column,
692
700
  columnWidths: resolvedColumnWidths,
693
701
  resizing: isColumnResizingEnabled,
694
702
  });
695
- 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));
703
+ 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-[4px] 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-[4px] min-w-0", children: _jsx(TableColumnFilterControl, { filter: column.filter, label: getColumnResizeLabel(column) }) })) : null }, column.key));
696
704
  })] })) : null] }), _jsx(TableBody, { "aria-hidden": loading ? "true" : undefined, children: loading ? (_jsx(TableRowSkeleton, { rows: loadingRows, columns: [
705
+ ...(hasRowIndicatorColumn
706
+ ? [{ width: "0.5rem", height: "0.5rem", badge: true }]
707
+ : []),
697
708
  ...(hasExpansionColumn
698
709
  ? [{ width: "1rem", height: "1rem" }]
699
710
  : []),
@@ -715,7 +726,7 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
715
726
  !resolvedTone &&
716
727
  "bg-[hsl(var(--table-row-striped-background))]", tableRowToneClassName(resolvedTone), typeof rowClassName === "function"
717
728
  ? rowClassName(row, rowIndex)
718
- : 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) => {
729
+ : rowClassName), children: [hasRowIndicatorColumn ? (_jsx(TableCell, { "data-slot": "table-row-indicator-cell", className: "w-8 px-2 text-center", children: rowIndicator === null || rowIndicator === void 0 ? void 0 : rowIndicator.render(row, rowIndex) })) : null, 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) => {
719
730
  event.stopPropagation();
720
731
  toggleExpandedRow(rowKey);
721
732
  }, 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) => {
@@ -733,8 +744,11 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
733
744
  : TableCell;
734
745
  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));
735
746
  })] }), 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 +
747
+ (hasRowIndicatorColumn ? 1 : 0) +
736
748
  (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));
737
- })) : (_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 })] }));
749
+ })) : (_jsx(TableRow, { className: "cursor-default hover:bg-card", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length +
750
+ (hasRowIndicatorColumn ? 1 : 0) +
751
+ (hasExpansionColumn ? 1 : 0), 1), className: "p-6", children: resolvedEmptyState }) })) })] }), _jsx("div", { role: "status", className: "sr-only", children: loading ? loadingLabel : null })] }));
738
752
  const reorderableContainer = reorderableColumns ? (_jsx(DndContext, { sensors: columnReorderSensors, collisionDetection: closestCenter, onDragEnd: handleColumnDragEnd, children: _jsx(SortableContext, { items: reorderableColumnIds, strategy: horizontalListSortingStrategy, children: container }) })) : (container);
739
753
  if (!pagination) {
740
754
  return (_jsxs("div", { className: "space-y-4", children: [toolbar, reorderableContainer] }));
package/dist/tabs.d.ts CHANGED
@@ -65,7 +65,7 @@ export interface TabsProps extends Omit<React.ComponentPropsWithoutRef<typeof Ta
65
65
  defaultValue?: string;
66
66
  value?: string;
67
67
  onValueChange?: (value: string) => void;
68
- size?: "default" | "compact";
68
+ size?: "default" | "compact" | "comfortable";
69
69
  renderPanels?: boolean;
70
70
  listWrapperClassName?: string;
71
71
  listClassName?: string;
package/dist/tabs.js CHANGED
@@ -144,13 +144,15 @@ function Tabs({ items, defaultValue, value, onValueChange, size = "default", ren
144
144
  if (!items.length) {
145
145
  return null;
146
146
  }
147
- return (_jsxs(TabsRoot, { defaultValue: fallbackValue, value: value, onValueChange: handleValueChange, className: cn("space-y-4", className), ...props, children: [_jsx("div", { ref: listWrapperRef, className: listWrapperClassName, children: _jsx(TabsList, { className: cn(size === "compact" && "p-0.5", listClassName), children: items.map((item) => item.sections && item.sections.length > 0 ? (_jsx(TabTriggerWithDropdown, { item: item, suppressAriaControls: !renderPanels, triggerClassName: cn(size === "compact" &&
148
- "min-h-7 px-2.5 py-0.5 data-[state=active]:shadow-sm", triggerClassName) }, item.value)) : (_jsx(TabTriggerItem, { item: item, suppressAriaControls: !renderPanels, triggerClassName: cn(size === "compact" &&
149
- "min-h-7 px-2.5 py-0.5 data-[state=active]:shadow-sm", triggerClassName) }, item.value))) }) }), renderPanels
147
+ return (_jsxs(TabsRoot, { defaultValue: fallbackValue, value: value, onValueChange: handleValueChange, className: cn("space-y-4", className), ...props, children: [_jsx("div", { ref: listWrapperRef, className: listWrapperClassName, children: _jsx(TabsList, { className: cn(size === "compact" && "p-0.5", size === "comfortable" && "rounded-[10px]", listClassName), children: items.map((item) => item.sections && item.sections.length > 0 ? (_jsx(TabTriggerWithDropdown, { item: item, suppressAriaControls: !renderPanels, triggerClassName: cn(size === "compact" &&
148
+ "min-h-7 px-2.5 py-0.5 data-[state=active]:shadow-sm", size === "comfortable" &&
149
+ "min-h-[40px] rounded-md px-[12px] py-[8px] text-[16px]", triggerClassName) }, item.value)) : (_jsx(TabTriggerItem, { item: item, suppressAriaControls: !renderPanels, triggerClassName: cn(size === "compact" &&
150
+ "min-h-7 px-2.5 py-0.5 data-[state=active]:shadow-sm", size === "comfortable" &&
151
+ "min-h-[40px] rounded-md px-[12px] py-[8px] text-[16px]", triggerClassName) }, item.value))) }) }), renderPanels
150
152
  ? items.map((item) => (_jsx(TabsContent, { value: item.value, className: cn("pt-4", panelClassName), children: item.content }, item.value)))
151
153
  : null] }));
152
154
  }
153
155
  function SubTabs({ className, listWrapperClassName, listClassName, triggerClassName, panelClassName, ...props }) {
154
- return (_jsx(Tabs, { className: cn("space-y-0", className), listWrapperClassName: cn("min-w-0 overflow-x-auto", listWrapperClassName), listClassName: cn("w-full min-w-max justify-start overflow-visible rounded-none border-0 border-b border-[hsl(var(--tab-list-border))] bg-transparent p-0", listClassName), triggerClassName: cn("min-h-9 rounded-none border-b-2 border-transparent bg-transparent px-3 py-2 text-[hsl(var(--tab-trigger-foreground))] shadow-none focus-visible:ring-inset focus-visible:ring-offset-0 data-[state=inactive]:hover:bg-[hsl(var(--tab-trigger-hover-background))] data-[state=active]:border-[hsl(var(--tab-indicator))] data-[state=active]:bg-transparent data-[state=active]:text-[hsl(var(--tab-trigger-active-foreground))] data-[state=active]:shadow-none", triggerClassName), panelClassName: cn("pt-4", panelClassName), ...props }));
156
+ return (_jsx(Tabs, { className: cn("space-y-0", className), listWrapperClassName: cn("min-w-0 overflow-x-auto", listWrapperClassName), listClassName: cn("w-full min-w-max justify-start overflow-visible rounded-none border-0 border-b border-[hsl(var(--tab-list-border))] bg-transparent p-0", listClassName), triggerClassName: cn("min-h-9 rounded-none border-b-[3px] border-transparent bg-transparent px-3 py-2 text-[hsl(var(--tab-trigger-foreground))] shadow-none focus-visible:ring-inset focus-visible:ring-offset-0 data-[state=inactive]:hover:bg-[hsl(var(--tab-trigger-hover-background))] data-[state=active]:border-[hsl(var(--tab-indicator))] data-[state=active]:bg-transparent data-[state=active]:text-[hsl(var(--tab-trigger-active-foreground))] data-[state=active]:shadow-none", triggerClassName), panelClassName: cn("pt-4", panelClassName), ...props }));
155
157
  }
156
158
  export { Tabs, SubTabs, TabsRoot, TabsList, TabsTrigger, TabsContent, TabWithDropdown, };
@@ -2,6 +2,7 @@ import * as React from "react";
2
2
  declare const TextareaControl: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>, "ref"> & React.RefAttributes<HTMLTextAreaElement>>;
3
3
  export interface TextareaProps extends Omit<React.ComponentProps<typeof TextareaControl>, "className"> {
4
4
  label: React.ReactNode;
5
+ size?: "default" | "comfortable";
5
6
  description?: React.ReactNode;
6
7
  error?: React.ReactNode;
7
8
  invalid?: boolean;