@neasg/design-system 0.4.21 → 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";
@@ -7,7 +7,7 @@ import { Button } from "./button";
7
7
  import { CountBadge } from "./count-badge";
8
8
  import { cn } from "./lib/utils";
9
9
  import { Popover } from "./popover";
10
- import { PopoverMenuFooter, PopoverMenuFooterAction, PopoverMenuGroup, PopoverMenuGroupLabel, PopoverMenuItem, PopoverMenuScrollArea, PopoverMenuSearch, PopoverMenuSection, PopoverMenuView, popoverMenuItemFocusClassName, } from "./popover-menu";
10
+ import { PopoverMenuFooter, PopoverMenuFooterAction, PopoverMenuCheckboxItem, PopoverMenuGroup, PopoverMenuGroupLabel, PopoverMenuItem, PopoverMenuScrollArea, PopoverMenuSearch, PopoverMenuSection, PopoverMenuView, popoverMenuItemFocusClassName, } from "./popover-menu";
11
11
  import { Typography } from "./typography";
12
12
  function getText(value) {
13
13
  if (typeof value === "string" || typeof value === "number") {
@@ -72,7 +72,9 @@ function FilterPopover({ align = "start", ariaLabel, contentClassName, defaultOp
72
72
  onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(nextOpen);
73
73
  };
74
74
  const resolvedTitle = (_c = title !== null && title !== void 0 ? title : directGroup === null || directGroup === void 0 ? void 0 : directGroup.label) !== null && _c !== void 0 ? _c : (!activeGroup ? triggerLabel : undefined);
75
- return (_jsxs(Popover, { align: align, contentClassName: cn("w-[360px]", contentClassName), defaultOpen: defaultOpen, modal: modal, onOpenChange: handleOpenChange, open: open, side: side, surface: "menu", trigger: trigger !== null && trigger !== void 0 ? trigger : defaultTrigger, title: resolvedTitle, "aria-label": (_e = (_d = ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : getText(resolvedTitle)) !== null && _d !== void 0 ? _d : getText(triggerLabel)) !== null && _e !== void 0 ? _e : "Filters", children: [_jsx(PopoverMenuView, { className: cn("-mx-1 -mt-1 px-1 pt-1", viewClassName), direction: transitionDirection, viewKey: activeGroup ? `group-${activeGroup.id}` : "filter-groups", children: activeGroup ? (_jsxs(_Fragment, { children: [!directGroup ? (_jsx("div", { className: "-mx-1 -mt-1 border-b p-1", children: _jsxs(Button, { type: "button", variant: "ghost", size: "sm", className: cn("w-full justify-start", popoverMenuItemFocusClassName), onClick: showFilterGroups, children: [_jsx(ChevronLeft, { className: "h-4 w-4" }), activeGroup.label] }) })) : null, activeGroup.searchable ? (_jsx(PopoverMenuSearch, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: (_f = activeGroup.searchPlaceholder) !== null && _f !== void 0 ? _f : `Search ${getGroupLabel(activeGroup).toLowerCase()}...`, "aria-label": (_g = activeGroup.searchPlaceholder) !== null && _g !== void 0 ? _g : `Search ${getGroupLabel(activeGroup).toLowerCase()}` })) : null, _jsx(PopoverMenuScrollArea, { maxHeight: maxHeight, children: _jsx(PopoverMenuSection, { children: filteredOptions.length ? (filteredOptions.map((option) => (_jsx(PopoverMenuItem, { active: option.selected, "aria-label": option.ariaLabel, "aria-pressed": option.selected, disabled: option.disabled, onClick: option.onSelect, children: option.label }, option.id)))) : (_jsx(Typography, { as: "p", variant: "caption", className: "px-2 py-2", children: (_h = activeGroup.emptyLabel) !== null && _h !== void 0 ? _h : emptyLabel })) }) })] })) : (_jsx(PopoverMenuScrollArea, { maxHeight: maxHeight, children: _jsxs(PopoverMenuGroup, { children: [!resolvedTitle ? (_jsx(PopoverMenuGroupLabel, { children: groupListLabel })) : null, _jsx(PopoverMenuSection, { children: groups.map((group) => {
75
+ return (_jsxs(Popover, { align: align, contentClassName: cn("w-[360px]", contentClassName), defaultOpen: defaultOpen, modal: modal, onOpenChange: handleOpenChange, open: open, side: side, surface: "menu", tabBehavior: "close-at-boundary", trigger: trigger !== null && trigger !== void 0 ? trigger : defaultTrigger, title: resolvedTitle, "aria-label": (_e = (_d = ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : getText(resolvedTitle)) !== null && _d !== void 0 ? _d : getText(triggerLabel)) !== null && _e !== void 0 ? _e : "Filters", children: [_jsx(PopoverMenuView, { className: cn("-mx-1 -mt-1 px-1 pt-1", viewClassName), direction: transitionDirection, viewKey: activeGroup ? `group-${activeGroup.id}` : "filter-groups", children: activeGroup ? (_jsxs(_Fragment, { children: [!directGroup ? (_jsx("div", { className: "-mx-1 -mt-1 border-b p-1", children: _jsxs(Button, { type: "button", variant: "ghost", size: "sm", className: cn("w-full justify-start", popoverMenuItemFocusClassName), onClick: showFilterGroups, children: [_jsx(ChevronLeft, { className: "h-4 w-4" }), activeGroup.label] }) })) : null, activeGroup.searchable ? (_jsx(PopoverMenuSearch, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: (_f = activeGroup.searchPlaceholder) !== null && _f !== void 0 ? _f : `Search ${getGroupLabel(activeGroup).toLowerCase()}...`, "aria-label": (_g = activeGroup.searchPlaceholder) !== null && _g !== void 0 ? _g : `Search ${getGroupLabel(activeGroup).toLowerCase()}` })) : null, _jsx(PopoverMenuScrollArea, { maxHeight: maxHeight, children: _jsx(PopoverMenuSection, { children: filteredOptions.length ? (filteredOptions.map((option) => (_jsx(PopoverMenuCheckboxItem, { "aria-label": option.ariaLabel, checked: Boolean(option.selected), disabled: option.disabled, onChange: option.onSelect
76
+ ? () => { var _a; return (_a = option.onSelect) === null || _a === void 0 ? void 0 : _a.call(option); }
77
+ : undefined, children: option.label }, option.id)))) : (_jsx(Typography, { as: "p", variant: "caption", className: "px-2 py-2", children: (_h = activeGroup.emptyLabel) !== null && _h !== void 0 ? _h : emptyLabel })) }) })] })) : (_jsx(PopoverMenuScrollArea, { maxHeight: maxHeight, children: _jsxs(PopoverMenuGroup, { children: [!resolvedTitle ? (_jsx(PopoverMenuGroupLabel, { children: groupListLabel })) : null, _jsx(PopoverMenuSection, { children: groups.map((group) => {
76
78
  var _a;
77
79
  const groupSelectedCount = getGroupSelectedCount(group);
78
80
  const selectionLabel = (_a = group.selectionLabel) !== null && _a !== void 0 ? _a : getFilterSelectionLabel(groupSelectedCount);
@@ -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, {
@@ -6,9 +6,11 @@ import { XIcon } from "./animated-icons/x";
6
6
  import { Badge } from "./badge";
7
7
  import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
8
8
  import { Checkbox } from "./checkbox";
9
- import { InputControl } from "./input-control";
10
9
  import { cn } from "./lib/utils";
10
+ import { Popover } from "./popover";
11
+ import { PopoverMenuSearch } from "./popover-menu";
11
12
  import { Typography } from "./typography";
13
+ import { SearchHighlight } from "./use-search-highlight";
12
14
  function MultiSelect({ label, options, value, onValueChange, placeholder = "Select options", description, error, invalid = false, disabled = false, hideLabel = false, searchable = false, searchPlaceholder = "Search...", maxVisibleTags = 2, className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, }) {
13
15
  const generatedId = React.useId();
14
16
  const selectId = id !== null && id !== void 0 ? id : `multi-select-${generatedId}`;
@@ -20,7 +22,6 @@ function MultiSelect({ label, options, value, onValueChange, placeholder = "Sele
20
22
  const isInvalid = invalid || Boolean(error);
21
23
  const [open, setOpen] = React.useState(false);
22
24
  const [searchQuery, setSearchQuery] = React.useState("");
23
- const containerRef = React.useRef(null);
24
25
  const triggerRef = React.useRef(null);
25
26
  const searchInputRef = React.useRef(null);
26
27
  const listboxRef = React.useRef(null);
@@ -67,6 +68,14 @@ function MultiSelect({ label, options, value, onValueChange, placeholder = "Sele
67
68
  const removeOption = React.useCallback((optionValue) => {
68
69
  onValueChange(value.filter((v) => v !== optionValue));
69
70
  }, [value, onValueChange]);
71
+ const handleOpenChange = React.useCallback((nextOpen) => {
72
+ if (disabled)
73
+ return;
74
+ if (nextOpen) {
75
+ openFocusTargetRef.current = searchable ? "search" : "first";
76
+ }
77
+ setOpen(nextOpen);
78
+ }, [disabled, searchable]);
70
79
  React.useEffect(() => {
71
80
  if (!open) {
72
81
  setSearchQuery("");
@@ -82,18 +91,6 @@ function MultiSelect({ label, options, value, onValueChange, placeholder = "Sele
82
91
  });
83
92
  return () => window.cancelAnimationFrame(frame);
84
93
  }, [focusBoundaryOption, open, searchable]);
85
- React.useEffect(() => {
86
- const handleClickOutside = (event) => {
87
- if (containerRef.current &&
88
- !containerRef.current.contains(event.target)) {
89
- setOpen(false);
90
- }
91
- };
92
- if (open) {
93
- document.addEventListener("mousedown", handleClickOutside);
94
- return () => document.removeEventListener("mousedown", handleClickOutside);
95
- }
96
- }, [open]);
97
94
  const handleTriggerKeyDown = React.useCallback((event) => {
98
95
  if (disabled)
99
96
  return;
@@ -165,25 +162,16 @@ function MultiSelect({ label, options, value, onValueChange, placeholder = "Sele
165
162
  var _a;
166
163
  event.preventDefault();
167
164
  (_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
168
- }, children: label }), _jsxs("div", { ref: containerRef, className: "relative", children: [_jsxs("div", { ref: triggerRef, role: "combobox", id: selectId, tabIndex: disabled ? -1 : 0, onClick: () => {
169
- if (!disabled) {
170
- if (open) {
171
- setOpen(false);
172
- }
173
- else {
174
- openWithFocus(searchable ? "search" : "first");
175
- }
176
- }
177
- }, 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: (e) => {
178
- e.stopPropagation();
179
- removeOption(opt.value);
180
- }, "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
181
- .slice(maxVisibleTags)
182
- .map((o) => o.label)
183
- .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") })] }), open ? (_jsxs("div", { className: "absolute z-50 mt-1 w-full rounded-overlay border bg-popover shadow-md", children: [searchable ? (_jsx("div", { className: "border-b p-1", children: _jsx(InputControl, { ref: searchInputRef, value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: searchPlaceholder, className: "h-control border-0 bg-transparent px-2 py-0 text-sm shadow-none", onKeyDown: handleSearchKeyDown }) })) : 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 p-1", onKeyDown: handleListboxKeyDown, children: filteredOptions.length > 0 ? (filteredOptions.map((option) => {
184
- const isSelected = value.includes(option.value);
185
- 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 &&
186
- "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("span", { children: option.label }), option.description ? (_jsx(Typography, { as: "span", variant: "caption", children: option.description })) : null] })] }, option.value));
187
- })) : (_jsx("div", { className: "px-2.5 py-1.5 text-center text-sm text-muted-foreground", children: "No options found." })) })] })) : null] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
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
+ event.stopPropagation();
167
+ removeOption(opt.value);
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
169
+ .slice(maxVisibleTags)
170
+ .map((option) => option.label)
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
+ const isSelected = value.includes(option.value);
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-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
+ })) : (_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 })] }));
188
176
  }
189
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 };
@@ -0,0 +1,14 @@
1
+ import * as React from "react";
2
+ type PopoverTabBehavior = "contain" | "close" | "close-at-boundary";
3
+ interface UsePopoverTabExitOptions<TTrigger extends HTMLElement, TContent extends HTMLElement> {
4
+ behavior: PopoverTabBehavior;
5
+ triggerRef: React.RefObject<TTrigger>;
6
+ contentRef: React.RefObject<TContent>;
7
+ onExit: () => void;
8
+ }
9
+ declare function usePopoverTabExit<TTrigger extends HTMLElement, TContent extends HTMLElement>({ behavior, triggerRef, contentRef, onExit, }: UsePopoverTabExitOptions<TTrigger, TContent>): {
10
+ handleCloseAutoFocus: (event: Event) => void;
11
+ handleTabKeyDown: (event: React.KeyboardEvent<HTMLElement>) => boolean;
12
+ };
13
+ export { usePopoverTabExit };
14
+ export type { PopoverTabBehavior };
@@ -0,0 +1,64 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ const tabbableSelector = 'a[href], button:not(:disabled), input:not(:disabled):not([type="hidden"]), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex="-1"])';
4
+ function usePopoverTabExit({ behavior, triggerRef, contentRef, onExit, }) {
5
+ const tabExitPendingRef = React.useRef(false);
6
+ const focusFrameRef = React.useRef(null);
7
+ React.useEffect(() => () => {
8
+ if (focusFrameRef.current !== null) {
9
+ window.cancelAnimationFrame(focusFrameRef.current);
10
+ }
11
+ }, []);
12
+ const handleTabKeyDown = React.useCallback((event) => {
13
+ if (behavior === "contain" ||
14
+ event.defaultPrevented ||
15
+ event.key !== "Tab") {
16
+ return false;
17
+ }
18
+ const trigger = triggerRef.current;
19
+ const content = contentRef.current;
20
+ if (!trigger) {
21
+ return false;
22
+ }
23
+ if (behavior === "close-at-boundary" && content) {
24
+ const contentControls = Array.from(content.querySelectorAll(tabbableSelector)).filter((candidate) => !candidate.closest('[hidden], [aria-hidden="true"], [inert]'));
25
+ const boundaryControl = event.shiftKey
26
+ ? contentControls[0]
27
+ : contentControls[contentControls.length - 1];
28
+ if (document.activeElement !== boundaryControl) {
29
+ return false;
30
+ }
31
+ }
32
+ const candidates = Array.from(document.querySelectorAll(tabbableSelector)).filter((candidate) => {
33
+ const hiddenAncestor = candidate.closest('[hidden], [aria-hidden="true"], [inert]');
34
+ const isHidden = hiddenAncestor &&
35
+ !hiddenAncestor.hasAttribute("data-aria-hidden");
36
+ return (!candidate.hasAttribute("data-radix-focus-guard") &&
37
+ !isHidden &&
38
+ (candidate === trigger ||
39
+ (!trigger.contains(candidate) && !(content === null || content === void 0 ? void 0 : content.contains(candidate)))));
40
+ });
41
+ const triggerIndex = candidates.indexOf(trigger);
42
+ const target = triggerIndex >= 0
43
+ ? candidates[triggerIndex + (event.shiftKey ? -1 : 1)]
44
+ : undefined;
45
+ event.preventDefault();
46
+ event.stopPropagation();
47
+ tabExitPendingRef.current = true;
48
+ onExit();
49
+ focusFrameRef.current = window.requestAnimationFrame(() => {
50
+ (target !== null && target !== void 0 ? target : trigger).focus({ preventScroll: true });
51
+ focusFrameRef.current = null;
52
+ });
53
+ return true;
54
+ }, [behavior, contentRef, onExit, triggerRef]);
55
+ const handleCloseAutoFocus = React.useCallback((event) => {
56
+ if (!tabExitPendingRef.current) {
57
+ return;
58
+ }
59
+ event.preventDefault();
60
+ tabExitPendingRef.current = false;
61
+ }, []);
62
+ return { handleCloseAutoFocus, handleTabKeyDown };
63
+ }
64
+ export { usePopoverTabExit };