@neasg/design-system 0.4.17 → 0.4.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animated-icons/filter.js +1 -1
- package/dist/calendar.d.ts +1 -1
- package/dist/calendar.js +2 -2
- package/dist/command-search.js +3 -2
- package/dist/date-input.d.ts +2 -0
- package/dist/date-input.js +6 -6
- package/dist/filter-popover.d.ts +5 -1
- package/dist/filter-popover.js +15 -6
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/popover-menu.d.ts +5 -3
- package/dist/popover-menu.js +5 -2
- package/dist/popover.d.ts +9 -3
- package/dist/popover.js +12 -3
- package/dist/table-column-filter.d.ts +42 -0
- package/dist/table-column-filter.js +80 -0
- package/dist/table.d.ts +5 -0
- package/dist/table.js +25 -16
- package/package.json +1 -1
|
@@ -72,7 +72,7 @@ const FilterIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 2
|
|
|
72
72
|
controls.start("normal");
|
|
73
73
|
}
|
|
74
74
|
}, [controls, onMouseLeave, isParentHovered]);
|
|
75
|
-
return (_jsx("div", { ref: divRef, className: cn(className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ...props, children: _jsx(motion.svg, { animate: controls, initial: "normal", fill: "none", height: size, stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", transition: DEFAULT_TRANSITION, variants: SVG_VARIANTS, viewBox: "0 0 24 24", width: size, xmlns: "http://www.w3.org/2000/svg", children: _jsx("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" }) }) }));
|
|
75
|
+
return (_jsx("div", { ref: divRef, "data-slot": "filter-icon", className: cn(className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ...props, children: _jsx(motion.svg, { animate: controls, initial: "normal", fill: "none", height: size, stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", transition: DEFAULT_TRANSITION, variants: SVG_VARIANTS, viewBox: "0 0 24 24", width: size, xmlns: "http://www.w3.org/2000/svg", children: _jsx("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" }) }) }));
|
|
76
76
|
});
|
|
77
77
|
FilterIcon.displayName = "FilterIcon";
|
|
78
78
|
export { FilterIcon };
|
package/dist/calendar.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { DayButton, DayPicker } from "react-day-picker";
|
|
3
3
|
import { Button } from "./button";
|
|
4
4
|
declare function CalendarDayButton({ className, day, modifiers, children, disabled, onClick, onFocus, onBlur, onKeyDown, onMouseEnter, onMouseLeave, tabIndex, type, "aria-label": ariaLabel, "aria-pressed": ariaPressed, "aria-selected": ariaSelected, }: React.ComponentProps<typeof DayButton>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
declare function Calendar({ className, classNames, showOutsideDays, captionLayout, buttonVariant, formatters, components, ...props }: React.ComponentProps<typeof DayPicker> & {
|
|
5
|
+
declare function Calendar({ className, classNames, showOutsideDays, fixedWeeks, captionLayout, buttonVariant, formatters, components, ...props }: React.ComponentProps<typeof DayPicker> & {
|
|
6
6
|
buttonVariant?: React.ComponentProps<typeof Button>["variant"];
|
|
7
7
|
}): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export { Calendar, CalendarDayButton };
|
package/dist/calendar.js
CHANGED
|
@@ -18,9 +18,9 @@ function CalendarDayButton({ className, day, modifiers, children, disabled, onCl
|
|
|
18
18
|
!modifiers.range_end &&
|
|
19
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 }) }));
|
|
20
20
|
}
|
|
21
|
-
function Calendar({ className, classNames, showOutsideDays = true, captionLayout = "label", buttonVariant = "ghost", formatters, components, ...props }) {
|
|
21
|
+
function Calendar({ className, classNames, showOutsideDays = true, fixedWeeks = true, captionLayout = "label", buttonVariant = "ghost", formatters, components, ...props }) {
|
|
22
22
|
const defaultClassNames = getDefaultClassNames();
|
|
23
|
-
return (_jsx(DayPicker, { showOutsideDays: showOutsideDays, className: cn("bg-background group/calendar p-3 [--cell-size:2rem] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent", String.raw `rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw `rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className), captionLayout: captionLayout, formatters: {
|
|
23
|
+
return (_jsx(DayPicker, { showOutsideDays: showOutsideDays, fixedWeeks: fixedWeeks, className: cn("bg-background group/calendar p-3 [--cell-size:2rem] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent", String.raw `rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw `rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className), captionLayout: captionLayout, formatters: {
|
|
24
24
|
formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }),
|
|
25
25
|
...formatters,
|
|
26
26
|
}, classNames: {
|
package/dist/command-search.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { ArrowRight, ArrowUpRight
|
|
4
|
+
import { ArrowRight, ArrowUpRight } from "lucide-react";
|
|
5
|
+
import { FilterIcon } from "./animated-icons";
|
|
5
6
|
import { Badge } from "./badge";
|
|
6
7
|
import { Button } from "./button";
|
|
7
8
|
import { Command, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "./command";
|
|
@@ -211,7 +212,7 @@ function CommandSearch({ value, onValueChange, sections, children, className, is
|
|
|
211
212
|
? "h-control w-full min-w-[14rem] justify-start gap-[var(--space-2)] rounded-control border border-[hsl(var(--layout-sidebar-border))] bg-card px-[var(--space-2)] !font-normal text-muted-foreground shadow-none hover:bg-card hover:text-foreground focus-visible:border-ring focus-visible:bg-card focus-visible:ring-ring/30"
|
|
212
213
|
: isCollapsed
|
|
213
214
|
? "h-control min-h-control w-control min-w-control max-w-control shrink-0 rounded-control p-[var(--space-0)] !font-normal text-muted-foreground hover:bg-card hover:text-foreground"
|
|
214
|
-
: "h-control w-full min-w-[14rem] justify-start gap-[var(--space-2)] rounded-control border border-border bg-card px-[var(--space-2)] !font-normal text-muted-foreground hover:bg-card hover:text-foreground", triggerClassName), children: [_jsx(SearchIcon, { className: "shrink-0", size: 16 }), !isCollapsed ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex-1 text-left text-sm", children: triggerPlaceholder }), _jsx("kbd", { className: "hidden h-[var(--space-5)] items-center gap-[var(--space-1)] rounded border border-border bg-muted px-[var(--space-1)] font-mono text-[11px] text-muted-foreground sm:inline-flex", children: resolvedLabel })] })) : null] }), _jsx(DialogRoot, { open: isOpen, onOpenChange: setIsOpen, children: _jsxs(DialogSurface, { className: cn("w-[calc(100vw-var(--space-8))] max-w-[60rem] gap-[var(--space-0)] overflow-hidden p-[var(--space-0)] sm:w-[min(72vw,60rem)]", dialogClassName), "aria-describedby": undefined, hideCloseButton: true, children: [_jsx(DialogTitle, { className: "sr-only", children: inputPlaceholder }), _jsxs(Command, { shouldFilter: false, className: "rounded-none border-0", children: [_jsx(CommandInput, { ref: inputRef, value: value, onValueChange: onValueChange, placeholder: inputPlaceholder, wrapperClassName: "border-b px-[var(--space-3)] py-[var(--space-2)]", iconClassName: "hidden", className: "h-control border-0 bg-transparent px-[var(--space-3)] py-[var(--space-0)] pr-[calc(var(--space-12)+var(--space-6))] text-sm shadow-none focus-visible:border-transparent focus-visible:ring-0", endAdornment: _jsxs(_Fragment, { children: [value ? (_jsx("button", { type: "button", "aria-label": "Clear search", onMouseDown: preserveInputFocus, onClick: clearSearch, className: "flex size-[var(--space-6)] 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", children: _jsx(XIcon, { size: 14, "aria-hidden": "true" }) })) : null, _jsx("kbd", { className: "inline-flex h-[var(--space-5)] items-center rounded border border-border bg-muted px-[var(--space-1)] font-mono text-[11px] leading-none text-muted-foreground", "aria-hidden": "true", children: "Esc" })] }) }), allFilter || filters.length ? (_jsxs("div", { role: "group", "aria-label": "Search filters", "data-multiselectable": allowMultiSelect ? "true" : "false", className: "flex flex-wrap items-center gap-[var(--space-1)] border-b px-[var(--space-3)] py-[var(--space-2)]", children: [allFilter ? (_jsxs(FilterToggle, { selected: allFilter.selected, "aria-pressed": Boolean(allFilter.selected), onMouseDown: preserveInputFocus, onClick: () => selectFilter(allFilter), children: [allFilter.label, typeof allFilter.count === "number" ? (_jsx(CountBadge, { count: allFilter.count, hideWhenZero: true, size: "sm", variant: allFilter.selected ? "default" : "muted" })) : null] })) : null, filters.map((filter) => (_jsxs(FilterToggle, { selected: filter.selected, "aria-pressed": Boolean(filter.selected), onMouseDown: preserveInputFocus, onClick: () => selectFilter(filter), children: [filter.label, typeof filter.count === "number" ? (_jsx(CountBadge, { count: filter.count, hideWhenZero: true, size: "sm", variant: filter.selected ? "default" : "muted" })) : null] }, filter.value)))] })) : null, subFilters.length ? (_jsxs("div", { role: "group", "aria-label": "Search sub-filters", "data-multiselectable": allowMultiSelect ? "true" : "false", className: "flex flex-wrap items-center gap-[var(--space-1)] border-b bg-muted/30 px-[var(--space-3)] py-[var(--space-2)]", children: [_jsx(
|
|
215
|
+
: "h-control w-full min-w-[14rem] justify-start gap-[var(--space-2)] rounded-control border border-border bg-card px-[var(--space-2)] !font-normal text-muted-foreground hover:bg-card hover:text-foreground", triggerClassName), children: [_jsx(SearchIcon, { className: "shrink-0", size: 16 }), !isCollapsed ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex-1 text-left text-sm", children: triggerPlaceholder }), _jsx("kbd", { className: "hidden h-[var(--space-5)] items-center gap-[var(--space-1)] rounded border border-border bg-muted px-[var(--space-1)] font-mono text-[11px] text-muted-foreground sm:inline-flex", children: resolvedLabel })] })) : null] }), _jsx(DialogRoot, { open: isOpen, onOpenChange: setIsOpen, children: _jsxs(DialogSurface, { className: cn("w-[calc(100vw-var(--space-8))] max-w-[60rem] gap-[var(--space-0)] overflow-hidden p-[var(--space-0)] sm:w-[min(72vw,60rem)]", dialogClassName), "aria-describedby": undefined, hideCloseButton: true, children: [_jsx(DialogTitle, { className: "sr-only", children: inputPlaceholder }), _jsxs(Command, { shouldFilter: false, className: "rounded-none border-0", children: [_jsx(CommandInput, { ref: inputRef, value: value, onValueChange: onValueChange, placeholder: inputPlaceholder, wrapperClassName: "border-b px-[var(--space-3)] py-[var(--space-2)]", iconClassName: "hidden", className: "h-control border-0 bg-transparent px-[var(--space-3)] py-[var(--space-0)] pr-[calc(var(--space-12)+var(--space-6))] text-sm shadow-none focus-visible:border-transparent focus-visible:ring-0", endAdornment: _jsxs(_Fragment, { children: [value ? (_jsx("button", { type: "button", "aria-label": "Clear search", onMouseDown: preserveInputFocus, onClick: clearSearch, className: "flex size-[var(--space-6)] 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", children: _jsx(XIcon, { size: 14, "aria-hidden": "true" }) })) : null, _jsx("kbd", { className: "inline-flex h-[var(--space-5)] items-center rounded border border-border bg-muted px-[var(--space-1)] font-mono text-[11px] leading-none text-muted-foreground", "aria-hidden": "true", children: "Esc" })] }) }), allFilter || filters.length ? (_jsxs("div", { role: "group", "aria-label": "Search filters", "data-multiselectable": allowMultiSelect ? "true" : "false", className: "flex flex-wrap items-center gap-[var(--space-1)] border-b px-[var(--space-3)] py-[var(--space-2)]", children: [allFilter ? (_jsxs(FilterToggle, { selected: allFilter.selected, "aria-pressed": Boolean(allFilter.selected), onMouseDown: preserveInputFocus, onClick: () => selectFilter(allFilter), children: [allFilter.label, typeof allFilter.count === "number" ? (_jsx(CountBadge, { count: allFilter.count, hideWhenZero: true, size: "sm", variant: allFilter.selected ? "default" : "muted" })) : null] })) : null, filters.map((filter) => (_jsxs(FilterToggle, { selected: filter.selected, "aria-pressed": Boolean(filter.selected), onMouseDown: preserveInputFocus, onClick: () => selectFilter(filter), children: [filter.label, typeof filter.count === "number" ? (_jsx(CountBadge, { count: filter.count, hideWhenZero: true, size: "sm", variant: filter.selected ? "default" : "muted" })) : null] }, filter.value)))] })) : null, subFilters.length ? (_jsxs("div", { role: "group", "aria-label": "Search sub-filters", "data-multiselectable": allowMultiSelect ? "true" : "false", className: "flex flex-wrap items-center gap-[var(--space-1)] border-b bg-muted/30 px-[var(--space-3)] py-[var(--space-2)]", children: [_jsx(FilterIcon, { "data-slot": "command-search-sub-filter-icon", className: "mr-[var(--space-1)] shrink-0 text-muted-foreground", size: 14, "aria-hidden": "true" }), subFilters.map((filter) => (_jsxs(FilterToggle, { selected: filter.selected, "aria-pressed": Boolean(filter.selected), onMouseDown: preserveInputFocus, onClick: () => selectFilter(filter), children: [filter.label, typeof filter.count === "number" ? (_jsx(CountBadge, { count: filter.count, hideWhenZero: true, size: "sm", variant: filter.selected ? "default" : "muted" })) : null] }, filter.value)))] })) : null, _jsx(CommandList, { "aria-busy": loading || undefined, className: cn("p-[var(--space-1)]", resultsClassName), style: {
|
|
215
216
|
minHeight: resultsMinHeight,
|
|
216
217
|
maxHeight: resultsMaxHeight,
|
|
217
218
|
}, children: loading ? (_jsx(CommandSearchSkeleton, { rows: loadingRows })) : children !== undefined ? children : sections ? (visibleSections.length ? (visibleSections.map((section, index) => {
|
package/dist/date-input.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export interface DateRangeInputValue {
|
|
|
5
5
|
}
|
|
6
6
|
interface DateInputSharedProps {
|
|
7
7
|
label: React.ReactNode;
|
|
8
|
+
/** Visible heading for the calendar popover. */
|
|
9
|
+
popoverTitle?: React.ReactNode;
|
|
8
10
|
appearance?: "field" | "inline";
|
|
9
11
|
description?: React.ReactNode;
|
|
10
12
|
error?: React.ReactNode;
|
package/dist/date-input.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { format, parse } from "date-fns";
|
|
5
5
|
import { CalendarIcon } from "lucide-react";
|
|
@@ -7,8 +7,8 @@ import { XIcon } from "./animated-icons/x";
|
|
|
7
7
|
import { Calendar } from "./calendar";
|
|
8
8
|
import { inputControlClassName } from "./input-control";
|
|
9
9
|
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
|
|
10
|
-
import { PopoverContent, PopoverRoot, PopoverTrigger } from "./popover-primitive";
|
|
11
10
|
import { cn } from "./lib/utils";
|
|
11
|
+
import { Popover } from "./popover";
|
|
12
12
|
function createLocalDate(year, monthIndex, day) {
|
|
13
13
|
const date = new Date(year, monthIndex, day);
|
|
14
14
|
if (date.getFullYear() !== year ||
|
|
@@ -93,7 +93,7 @@ function formatDisplayRange(range) {
|
|
|
93
93
|
}
|
|
94
94
|
const DateInput = React.forwardRef((props, ref) => {
|
|
95
95
|
var _a, _b, _c;
|
|
96
|
-
const { label, appearance = "field", description, error, invalid = false, hideLabel = false, disabled = false, clearable = false, clearLabel, minDate, maxDate, className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, } = props;
|
|
96
|
+
const { label, popoverTitle, appearance = "field", description, error, invalid = false, hideLabel = false, disabled = false, clearable = false, clearLabel, minDate, maxDate, className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, } = props;
|
|
97
97
|
const [open, setOpen] = React.useState(false);
|
|
98
98
|
const [isSelectingRangeEnd, setIsSelectingRangeEnd] = React.useState(false);
|
|
99
99
|
const generatedId = React.useId();
|
|
@@ -164,11 +164,11 @@ const DateInput = React.forwardRef((props, ref) => {
|
|
|
164
164
|
}
|
|
165
165
|
};
|
|
166
166
|
const disabledDays = (date) => isDateOutsideBounds(date, minDate, maxDate);
|
|
167
|
-
const calendarContent = (_jsx(
|
|
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: [
|
|
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] }));
|
|
170
170
|
}
|
|
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(
|
|
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
|
});
|
|
173
173
|
DateInput.displayName = "DateInput";
|
|
174
174
|
export { DateInput };
|
package/dist/filter-popover.d.ts
CHANGED
|
@@ -25,14 +25,18 @@ export interface FilterPopoverProps extends Pick<PopoverProps, "align" | "defaul
|
|
|
25
25
|
selectedCount?: number;
|
|
26
26
|
trigger?: React.ReactElement;
|
|
27
27
|
triggerLabel?: React.ReactNode;
|
|
28
|
+
title?: React.ReactNode;
|
|
28
29
|
groupListLabel?: React.ReactNode;
|
|
29
30
|
resetLabel?: React.ReactNode;
|
|
30
31
|
emptyLabel?: React.ReactNode;
|
|
32
|
+
ariaLabel?: string;
|
|
31
33
|
onReset?: () => void;
|
|
32
34
|
contentClassName?: string;
|
|
33
35
|
viewClassName?: string;
|
|
34
36
|
maxHeight?: string;
|
|
37
|
+
/** Opens the only group directly, without the grouped-filter navigation screen. */
|
|
38
|
+
directSingleGroup?: boolean;
|
|
35
39
|
}
|
|
36
40
|
declare function getFilterSelectionLabel(count: number): string;
|
|
37
|
-
declare function FilterPopover({ align, contentClassName, defaultOpen, emptyLabel, groupListLabel, groups, maxHeight, modal, onOpenChange, onReset, open, resetLabel, selectedCount, side, trigger, triggerLabel, viewClassName, }: FilterPopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
41
|
+
declare function FilterPopover({ align, ariaLabel, contentClassName, defaultOpen, directSingleGroup, emptyLabel, groupListLabel, groups, maxHeight, modal, onOpenChange, onReset, open, resetLabel, selectedCount, side, trigger, triggerLabel, title, viewClassName, }: FilterPopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
38
42
|
export { FilterPopover, getFilterSelectionLabel };
|
package/dist/filter-popover.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { ChevronLeft, ChevronRight
|
|
4
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
5
|
+
import { FilterIcon } from "./animated-icons";
|
|
5
6
|
import { Button } from "./button";
|
|
6
7
|
import { CountBadge } from "./count-badge";
|
|
7
8
|
import { cn } from "./lib/utils";
|
|
@@ -32,12 +33,13 @@ function getFilterSelectionLabel(count) {
|
|
|
32
33
|
}
|
|
33
34
|
return count === 1 ? "1 selected" : `${count} selected`;
|
|
34
35
|
}
|
|
35
|
-
function FilterPopover({ align = "start", contentClassName, defaultOpen, emptyLabel = "No options found.", groupListLabel = "Filter by", groups, maxHeight = "max-h-80", modal, onOpenChange, onReset, open, resetLabel = "Reset filters", selectedCount, side, trigger, triggerLabel = "Filters", viewClassName, }) {
|
|
36
|
-
var _a, _b, _c, _d, _e;
|
|
36
|
+
function FilterPopover({ align = "start", ariaLabel, contentClassName, defaultOpen, directSingleGroup = false, emptyLabel = "No options found.", groupListLabel = "Filter by", groups, maxHeight = "max-h-80", modal, onOpenChange, onReset, open, resetLabel = "Reset filters", selectedCount, side, trigger, triggerLabel = "Filters", title, viewClassName, }) {
|
|
37
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
37
38
|
const [activeGroupId, setActiveGroupId] = React.useState(null);
|
|
38
39
|
const [transitionDirection, setTransitionDirection] = React.useState("forward");
|
|
39
40
|
const [query, setQuery] = React.useState("");
|
|
40
|
-
const
|
|
41
|
+
const directGroup = directSingleGroup && groups.length === 1 ? groups[0] : null;
|
|
42
|
+
const activeGroup = (_a = directGroup !== null && directGroup !== void 0 ? directGroup : groups.find((group) => group.id === activeGroupId)) !== null && _a !== void 0 ? _a : null;
|
|
41
43
|
const resolvedSelectedCount = selectedCount !== null && selectedCount !== void 0 ? selectedCount : groups.reduce((total, group) => total + getGroupSelectedCount(group), 0);
|
|
42
44
|
const normalizedQuery = query.trim().toLowerCase();
|
|
43
45
|
const filteredOptions = activeGroup && normalizedQuery
|
|
@@ -62,8 +64,15 @@ function FilterPopover({ align = "start", contentClassName, defaultOpen, emptyLa
|
|
|
62
64
|
setTransitionDirection("backward");
|
|
63
65
|
setActiveGroupId(null);
|
|
64
66
|
};
|
|
65
|
-
const defaultTrigger = (_jsxs(Button, { type: "button", variant: "outline", children: [_jsx(
|
|
66
|
-
|
|
67
|
+
const defaultTrigger = (_jsxs(Button, { type: "button", variant: "outline", children: [_jsx(FilterIcon, { size: 16 }), triggerLabel, resolvedSelectedCount > 0 ? (_jsx(CountBadge, { count: resolvedSelectedCount, size: "sm" })) : null] }));
|
|
68
|
+
const handleOpenChange = (nextOpen) => {
|
|
69
|
+
if (!nextOpen && directGroup) {
|
|
70
|
+
setQuery("");
|
|
71
|
+
}
|
|
72
|
+
onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(nextOpen);
|
|
73
|
+
};
|
|
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: "w-full justify-start", 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: [_jsx(PopoverMenuGroupLabel, { children: groupListLabel }), _jsx(PopoverMenuSection, { children: groups.map((group) => {
|
|
67
76
|
var _a;
|
|
68
77
|
const groupSelectedCount = getGroupSelectedCount(group);
|
|
69
78
|
const selectionLabel = (_a = group.selectionLabel) !== null && _a !== void 0 ? _a : getFilterSelectionLabel(groupSelectedCount);
|
package/dist/index.d.ts
CHANGED
|
@@ -82,8 +82,8 @@ export { TaskModeShell, TASK_MODE_SHELL_ANIMATION_DURATION_MS, } from "./task-mo
|
|
|
82
82
|
export type { TaskModeShellProps } from "./task-mode-shell";
|
|
83
83
|
export { useFileUpload } from "./use-file-upload";
|
|
84
84
|
export type { FileUploadProgress, FileUploadResult, FileUploadStatus, UseFileUploadOptions, UseFileUploadReturn, } from "./use-file-upload";
|
|
85
|
-
export { Popover } from "./popover";
|
|
86
|
-
export type { PopoverProps } from "./popover";
|
|
85
|
+
export { Popover, PopoverTitle } from "./popover";
|
|
86
|
+
export type { PopoverProps, PopoverTitleProps } from "./popover";
|
|
87
87
|
export { PopoverMenuContent, PopoverMenuCheckboxItem, PopoverMenuHeader, PopoverMenuSection, PopoverMenuItem, PopoverMenuDivider, PopoverMenuFooter, PopoverMenuFooterAction, PopoverMenuFooterMeta, PopoverMenuGroup, PopoverMenuGroupLabel, PopoverMenuSearch, PopoverMenuScrollArea, PopoverMenuView, } from "./popover-menu";
|
|
88
88
|
export type { PopoverMenuContentProps, PopoverMenuCheckboxItemProps, PopoverMenuHeaderProps, PopoverMenuSectionProps, PopoverMenuItemProps, PopoverMenuDividerProps, PopoverMenuFooterActionProps, PopoverMenuFooterMetaProps, PopoverMenuFooterProps, PopoverMenuGroupProps, PopoverMenuGroupLabelProps, PopoverMenuSearchProps, PopoverMenuScrollAreaProps, PopoverMenuViewProps, } from "./popover-menu";
|
|
89
89
|
export { SearchInput } from "./search-input";
|
|
@@ -133,6 +133,7 @@ export { useTableSort } from "./use-table-sort";
|
|
|
133
133
|
export type { UseTableSortOptions, UseTableSortReturn, } from "./use-table-sort";
|
|
134
134
|
export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
|
|
135
135
|
export type { TableColumn, TableColumnWidths, TableProps, TableResizeMode, TableRowExpansionProps, TableRowTone, TableSortDirection, TableSortingProps, TableSkeletonConfig, TableRowSkeletonProps, } from "./table";
|
|
136
|
+
export type { TableColumnFilter, TableColumnFilterOption, TableDateColumnFilter, TableOptionsColumnFilter, TableTextColumnFilter, } from "./table-column-filter";
|
|
136
137
|
export { Tabs, TabsRoot, TabWithDropdown, type TabsItem, type TabsItemLabel, type TabsItemLabelProps, type TabsProps, TabsList, TabsTrigger, TabsContent, type TabWithDropdownProps, useTabsContext, type TabDropdownSection, } from "./tabs";
|
|
137
138
|
export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, type TooltipProps, } from "./tooltip";
|
|
138
139
|
export { Toaster, toast } from "./toaster";
|
package/dist/index.js
CHANGED
|
@@ -42,7 +42,7 @@ export { RadioGroup } from "./radio-group";
|
|
|
42
42
|
export { ScrollHint } from "./scroll-hint";
|
|
43
43
|
export { TaskModeShell, TASK_MODE_SHELL_ANIMATION_DURATION_MS, } from "./task-mode-shell";
|
|
44
44
|
export { useFileUpload } from "./use-file-upload";
|
|
45
|
-
export { Popover } from "./popover";
|
|
45
|
+
export { Popover, PopoverTitle } from "./popover";
|
|
46
46
|
export { PopoverMenuContent, PopoverMenuCheckboxItem, PopoverMenuHeader, PopoverMenuSection, PopoverMenuItem, PopoverMenuDivider, PopoverMenuFooter, PopoverMenuFooterAction, PopoverMenuFooterMeta, PopoverMenuGroup, PopoverMenuGroupLabel, PopoverMenuSearch, PopoverMenuScrollArea, PopoverMenuView, } from "./popover-menu";
|
|
47
47
|
export { SearchInput } from "./search-input";
|
|
48
48
|
export { SectionNav } from "./section-nav";
|
package/dist/popover-menu.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type ButtonProps } from "./button";
|
|
3
3
|
import { type CheckboxProps } from "./checkbox";
|
|
4
|
-
import {
|
|
4
|
+
import { type InputProps } from "./input";
|
|
5
5
|
import { PopoverContent } from "./popover-primitive";
|
|
6
6
|
import { Typography } from "./typography";
|
|
7
7
|
export interface PopoverMenuHeaderProps extends Omit<React.ComponentPropsWithoutRef<typeof Typography>, "as" | "asChild" | "variant" | "color"> {
|
|
@@ -41,10 +41,12 @@ declare function PopoverMenuGroup({ className, children, ...props }: PopoverMenu
|
|
|
41
41
|
export interface PopoverMenuGroupLabelProps extends Omit<React.ComponentPropsWithoutRef<typeof Typography>, "as" | "asChild" | "variant" | "color"> {
|
|
42
42
|
}
|
|
43
43
|
declare function PopoverMenuGroupLabel({ className, children, ...props }: PopoverMenuGroupLabelProps): import("react/jsx-runtime").JSX.Element;
|
|
44
|
-
export interface PopoverMenuSearchProps extends
|
|
44
|
+
export interface PopoverMenuSearchProps extends Omit<InputProps, "className" | "hideLabel" | "inputClassName" | "label"> {
|
|
45
|
+
label?: React.ReactNode;
|
|
46
|
+
className?: string;
|
|
45
47
|
containerClassName?: string;
|
|
46
48
|
}
|
|
47
|
-
declare const PopoverMenuSearch: React.ForwardRefExoticComponent<PopoverMenuSearchProps & React.RefAttributes<HTMLInputElement>>;
|
|
49
|
+
declare const PopoverMenuSearch: React.ForwardRefExoticComponent<Omit<PopoverMenuSearchProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
|
|
48
50
|
export interface PopoverMenuScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
49
51
|
maxHeight?: string;
|
|
50
52
|
}
|
package/dist/popover-menu.js
CHANGED
|
@@ -4,8 +4,8 @@ import * as React from "react";
|
|
|
4
4
|
import { Check } from "lucide-react";
|
|
5
5
|
import { Button } from "./button";
|
|
6
6
|
import { Checkbox } from "./checkbox";
|
|
7
|
+
import { Input } from "./input";
|
|
7
8
|
import { cn } from "./lib/utils";
|
|
8
|
-
import { InputControl } from "./input-control";
|
|
9
9
|
import { PopoverContent } from "./popover-primitive";
|
|
10
10
|
import { Typography } from "./typography";
|
|
11
11
|
const POPOVER_MENU_VIEW_TRANSITION_MS = 300;
|
|
@@ -44,7 +44,10 @@ function PopoverMenuGroup({ className, children, ...props }) {
|
|
|
44
44
|
function PopoverMenuGroupLabel({ className, children, ...props }) {
|
|
45
45
|
return (_jsx(Typography, { as: "p", variant: "caption", color: "muted", className: cn("px-2 py-1.5", className), ...props, children: children }));
|
|
46
46
|
}
|
|
47
|
-
const PopoverMenuSearch = React.forwardRef(({
|
|
47
|
+
const PopoverMenuSearch = React.forwardRef(({ "aria-label": ariaLabel, className, containerClassName, label, placeholder, ...props }, ref) => {
|
|
48
|
+
var _a, _b;
|
|
49
|
+
return (_jsx("div", { className: cn("relative -mx-1 -mt-1 mb-1 px-2 py-1 after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-border after:content-['']", containerClassName), children: _jsx(Input, { ref: ref, label: (_b = (_a = label !== null && label !== void 0 ? label : ariaLabel) !== null && _a !== void 0 ? _a : placeholder) !== null && _b !== void 0 ? _b : "Search", hideLabel: true, className: "gap-0", inputClassName: className, "aria-label": ariaLabel, placeholder: placeholder, ...props }) }));
|
|
50
|
+
});
|
|
48
51
|
PopoverMenuSearch.displayName = "PopoverMenuSearch";
|
|
49
52
|
function PopoverMenuScrollArea({ className, children, maxHeight = "max-h-64", ...props }) {
|
|
50
53
|
return (_jsx("div", { className: cn(maxHeight, "space-y-1 overflow-y-auto overflow-x-hidden", className), ...props, children: children }));
|
package/dist/popover.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
3
3
|
import { type TooltipProps } from "./tooltip";
|
|
4
|
+
import { Typography } from "./typography";
|
|
4
5
|
type PopoverRootProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Root>;
|
|
5
6
|
type PopoverContentProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>;
|
|
6
|
-
export interface PopoverProps extends Pick<PopoverRootProps, "defaultOpen" | "modal" | "onOpenChange" | "open">, Omit<PopoverContentProps, "children" | "className"> {
|
|
7
|
+
export interface PopoverProps extends Pick<PopoverRootProps, "defaultOpen" | "modal" | "onOpenChange" | "open">, Omit<PopoverContentProps, "children" | "className" | "title"> {
|
|
7
8
|
trigger: React.ReactElement;
|
|
8
9
|
children: React.ReactNode;
|
|
10
|
+
/** Visible popover heading. Also labels the popover for assistive technology. */
|
|
11
|
+
title?: React.ReactNode;
|
|
9
12
|
surface?: "panel" | "menu";
|
|
10
13
|
contentClassName?: string;
|
|
11
14
|
triggerTooltip?: React.ReactNode;
|
|
@@ -13,5 +16,8 @@ export interface PopoverProps extends Pick<PopoverRootProps, "defaultOpen" | "mo
|
|
|
13
16
|
triggerTooltipSideOffset?: TooltipProps["sideOffset"];
|
|
14
17
|
triggerTooltipClassName?: TooltipProps["contentClassName"];
|
|
15
18
|
}
|
|
16
|
-
declare function Popover({ defaultOpen, modal, onOpenChange, open, trigger, children, surface, contentClassName, triggerTooltip, triggerTooltipSide, triggerTooltipSideOffset, triggerTooltipClassName, align, sideOffset, ...contentProps }: PopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
-
export
|
|
19
|
+
declare function Popover({ defaultOpen, modal, onOpenChange, open, trigger, children, title, surface, contentClassName, triggerTooltip, triggerTooltipSide, triggerTooltipSideOffset, triggerTooltipClassName, align, sideOffset, ...contentProps }: PopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export interface PopoverTitleProps extends Omit<React.ComponentPropsWithoutRef<typeof Typography>, "as" | "asChild" | "variant" | "color"> {
|
|
21
|
+
}
|
|
22
|
+
declare function PopoverTitle({ className, children, ...props }: PopoverTitleProps): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export { Popover, PopoverTitle };
|
package/dist/popover.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
3
4
|
import { cn } from "./lib/utils";
|
|
4
5
|
import { PopoverContent, PopoverRoot, PopoverTrigger, } from "./popover-primitive";
|
|
5
6
|
import { Tooltip } from "./tooltip";
|
|
6
|
-
|
|
7
|
+
import { Typography } from "./typography";
|
|
8
|
+
function Popover({ defaultOpen, modal, onOpenChange, open, trigger, children, title, surface = "panel", contentClassName, triggerTooltip, triggerTooltipSide, triggerTooltipSideOffset, triggerTooltipClassName, align = "center", sideOffset = 4, ...contentProps }) {
|
|
9
|
+
const generatedTitleId = React.useId();
|
|
10
|
+
const titleId = title ? `popover-title-${generatedTitleId}` : undefined;
|
|
7
11
|
const triggerElement = _jsx(PopoverTrigger, { asChild: true, children: trigger });
|
|
8
|
-
return (_jsxs(PopoverRoot, { defaultOpen: defaultOpen, modal: modal, onOpenChange: onOpenChange, open: open, children: [triggerTooltip ? (_jsx(Tooltip, { content: triggerTooltip, side: triggerTooltipSide, sideOffset: triggerTooltipSideOffset, contentClassName: triggerTooltipClassName, children: triggerElement })) : (triggerElement),
|
|
12
|
+
return (_jsxs(PopoverRoot, { defaultOpen: defaultOpen, modal: modal, onOpenChange: onOpenChange, open: open, children: [triggerTooltip ? (_jsx(Tooltip, { content: triggerTooltip, side: triggerTooltipSide, sideOffset: triggerTooltipSideOffset, contentClassName: triggerTooltipClassName, children: triggerElement })) : (triggerElement), _jsxs(PopoverContent, { align: align, sideOffset: sideOffset, "aria-labelledby": titleId !== null && titleId !== void 0 ? titleId : contentProps["aria-labelledby"], className: cn(surface === "menu" && "overflow-hidden p-1", contentClassName), ...contentProps, children: [title ? (_jsx(PopoverTitle, { id: titleId, className: cn(surface === "menu"
|
|
13
|
+
? "px-2 py-1.5"
|
|
14
|
+
: "-mx-4 -mt-4 mb-4 border-b px-4 py-3"), children: title })) : null, children] })] }));
|
|
9
15
|
}
|
|
10
|
-
|
|
16
|
+
function PopoverTitle({ className, children, ...props }) {
|
|
17
|
+
return (_jsx(Typography, { as: "p", variant: "label", "data-slot": "popover-title", className: className, ...props, children: children }));
|
|
18
|
+
}
|
|
19
|
+
export { Popover, PopoverTitle };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
interface TableColumnFilterBase {
|
|
3
|
+
/** Overrides the column header text in accessible filter labels. */
|
|
4
|
+
label?: string;
|
|
5
|
+
/** Short text shown in the filter row before a value is selected. */
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface TableTextColumnFilter extends TableColumnFilterBase {
|
|
10
|
+
type: "text";
|
|
11
|
+
value: string;
|
|
12
|
+
onValueChange: (value: string) => void;
|
|
13
|
+
}
|
|
14
|
+
export interface TableColumnFilterOption {
|
|
15
|
+
label: React.ReactNode;
|
|
16
|
+
value: string;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface TableOptionsColumnFilter extends TableColumnFilterBase {
|
|
20
|
+
type: "options";
|
|
21
|
+
values: readonly string[];
|
|
22
|
+
onValuesChange: (values: string[]) => void;
|
|
23
|
+
options: readonly TableColumnFilterOption[];
|
|
24
|
+
/** Adds a compact option search field. Defaults to true for more than seven options. */
|
|
25
|
+
searchable?: boolean;
|
|
26
|
+
searchPlaceholder?: string;
|
|
27
|
+
emptyMessage?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface TableDateColumnFilter extends TableColumnFilterBase {
|
|
30
|
+
type: "date";
|
|
31
|
+
value: string;
|
|
32
|
+
onValueChange: (value: string) => void;
|
|
33
|
+
minDate?: Date;
|
|
34
|
+
maxDate?: Date;
|
|
35
|
+
}
|
|
36
|
+
export type TableColumnFilter = TableTextColumnFilter | TableOptionsColumnFilter | TableDateColumnFilter;
|
|
37
|
+
interface TableColumnFilterControlProps {
|
|
38
|
+
filter: TableColumnFilter;
|
|
39
|
+
label: string;
|
|
40
|
+
}
|
|
41
|
+
declare function TableColumnFilterControl({ filter, label, }: TableColumnFilterControlProps): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
export { TableColumnFilterControl };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { FilterIcon } from "./animated-icons";
|
|
5
|
+
import { DateInput } from "./date-input";
|
|
6
|
+
import { FilterPopover } from "./filter-popover";
|
|
7
|
+
import { FilterToggle, filterToggleVariants, } from "./filter-toggle";
|
|
8
|
+
import { cn } from "./lib/utils";
|
|
9
|
+
import { Popover } from "./popover";
|
|
10
|
+
import { PopoverMenuFooter, PopoverMenuFooterAction, PopoverMenuSearch, } from "./popover-menu";
|
|
11
|
+
function isTableColumnFilterActive(filter) {
|
|
12
|
+
return filter.type === "options"
|
|
13
|
+
? filter.values.length > 0
|
|
14
|
+
: filter.value.trim().length > 0;
|
|
15
|
+
}
|
|
16
|
+
function getOptionsFilterSummary(filter) {
|
|
17
|
+
var _a, _b, _c;
|
|
18
|
+
if (filter.values.length === 0) {
|
|
19
|
+
return (_a = filter.placeholder) !== null && _a !== void 0 ? _a : "Filter";
|
|
20
|
+
}
|
|
21
|
+
if (filter.values.length > 1) {
|
|
22
|
+
return `${filter.values.length} selected`;
|
|
23
|
+
}
|
|
24
|
+
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]);
|
|
25
|
+
}
|
|
26
|
+
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}`, "aria-pressed": active, className: cn("h-9 w-full min-w-0 justify-start overflow-hidden px-2 text-sm font-normal", !active &&
|
|
27
|
+
"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 }) })));
|
|
28
|
+
TableFilterTrigger.displayName = "TableFilterTrigger";
|
|
29
|
+
function TextColumnFilter({ filter, label, }) {
|
|
30
|
+
var _a, _b, _c;
|
|
31
|
+
const active = isTableColumnFilterActive(filter);
|
|
32
|
+
const accessibleLabel = (_a = filter.label) !== null && _a !== void 0 ? _a : label;
|
|
33
|
+
return (_jsxs(Popover, { align: "start", surface: "menu", "aria-label": `${accessibleLabel} filter`, title: `Filter by ${accessibleLabel}`, trigger: _jsx(TableFilterTrigger, { active: active, disabled: filter.disabled, label: accessibleLabel, children: active ? filter.value : (_b = filter.placeholder) !== null && _b !== void 0 ? _b : "Filter" }), children: [_jsx(PopoverMenuSearch, { autoFocus: true, value: filter.value, onChange: (event) => filter.onValueChange(event.target.value), placeholder: (_c = filter.placeholder) !== null && _c !== void 0 ? _c : `Search ${accessibleLabel}`, "aria-label": `Filter ${accessibleLabel}`, disabled: filter.disabled }), active ? (_jsx(PopoverMenuFooter, { children: _jsx(PopoverMenuFooterAction, { onClick: () => filter.onValueChange(""), children: "Clear filter" }) })) : null] }));
|
|
34
|
+
}
|
|
35
|
+
function OptionsColumnFilter({ filter, label, }) {
|
|
36
|
+
var _a, _b, _c, _d;
|
|
37
|
+
const active = isTableColumnFilterActive(filter);
|
|
38
|
+
const accessibleLabel = (_a = filter.label) !== null && _a !== void 0 ? _a : label;
|
|
39
|
+
const searchable = (_b = filter.searchable) !== null && _b !== void 0 ? _b : filter.options.length > 7;
|
|
40
|
+
const toggleOption = (optionValue) => {
|
|
41
|
+
const nextValues = filter.values.includes(optionValue)
|
|
42
|
+
? filter.values.filter((value) => value !== optionValue)
|
|
43
|
+
: [...filter.values, optionValue];
|
|
44
|
+
filter.onValuesChange(nextValues);
|
|
45
|
+
};
|
|
46
|
+
return (_jsx(FilterPopover, { directSingleGroup: true, ariaLabel: `${accessibleLabel} filter`, title: `Filter by ${accessibleLabel}`, trigger: _jsx(TableFilterTrigger, { active: active, disabled: filter.disabled, label: accessibleLabel, children: getOptionsFilterSummary(filter) }), groups: [
|
|
47
|
+
{
|
|
48
|
+
id: `table-column-${accessibleLabel}`,
|
|
49
|
+
label: `Filter by ${accessibleLabel}`,
|
|
50
|
+
searchable,
|
|
51
|
+
searchPlaceholder: (_c = filter.searchPlaceholder) !== null && _c !== void 0 ? _c : "Search options",
|
|
52
|
+
emptyLabel: (_d = filter.emptyMessage) !== null && _d !== void 0 ? _d : "No options found",
|
|
53
|
+
selectedCount: filter.values.length,
|
|
54
|
+
options: filter.options.map((option) => ({
|
|
55
|
+
id: option.value,
|
|
56
|
+
label: option.label,
|
|
57
|
+
selected: filter.values.includes(option.value),
|
|
58
|
+
disabled: filter.disabled || option.disabled,
|
|
59
|
+
onSelect: () => toggleOption(option.value),
|
|
60
|
+
})),
|
|
61
|
+
},
|
|
62
|
+
], selectedCount: filter.values.length, onReset: active ? () => filter.onValuesChange([]) : undefined, resetLabel: "Clear filter" }));
|
|
63
|
+
}
|
|
64
|
+
function DateColumnFilter({ filter, label, }) {
|
|
65
|
+
var _a, _b;
|
|
66
|
+
const active = isTableColumnFilterActive(filter);
|
|
67
|
+
const accessibleLabel = (_a = filter.label) !== null && _a !== void 0 ? _a : label;
|
|
68
|
+
return (_jsx(DateInput, { label: `Filter by ${accessibleLabel}`, 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 &&
|
|
69
|
+
"border-0 bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground") }));
|
|
70
|
+
}
|
|
71
|
+
function TableColumnFilterControl({ filter, label, }) {
|
|
72
|
+
if (filter.type === "text") {
|
|
73
|
+
return _jsx(TextColumnFilter, { filter: filter, label: label });
|
|
74
|
+
}
|
|
75
|
+
if (filter.type === "options") {
|
|
76
|
+
return _jsx(OptionsColumnFilter, { filter: filter, label: label });
|
|
77
|
+
}
|
|
78
|
+
return _jsx(DateColumnFilter, { filter: filter, label: label });
|
|
79
|
+
}
|
|
80
|
+
export { TableColumnFilterControl };
|
package/dist/table.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type PaginationProps } from "./pagination";
|
|
3
|
+
import { type TableColumnFilter } from "./table-column-filter";
|
|
3
4
|
interface TableRootProps extends React.HTMLAttributes<HTMLTableElement> {
|
|
4
5
|
wrapperClassName?: string;
|
|
5
6
|
}
|
|
@@ -73,6 +74,10 @@ export interface TableColumn<Row> {
|
|
|
73
74
|
sortable?: boolean;
|
|
74
75
|
sortKey?: string;
|
|
75
76
|
sortLabel?: string;
|
|
77
|
+
/** Controlled filter rendered in a second header row. Filter the supplied `rows` in the screen or data hook. */
|
|
78
|
+
filter?: TableColumnFilter;
|
|
79
|
+
/** Optional class name for this column's filter cell. */
|
|
80
|
+
filterClassName?: string;
|
|
76
81
|
/** When false, restore-defaults helpers can omit the column from the visible set. */
|
|
77
82
|
defaultVisible?: boolean;
|
|
78
83
|
/** When false, the column visibility menu keeps the column out of its checklist. */
|
package/dist/table.js
CHANGED
|
@@ -6,6 +6,7 @@ import { ArrowUpIcon } from "./animated-icons/arrow-up";
|
|
|
6
6
|
import { EmptyState } from "./empty-state";
|
|
7
7
|
import { Pagination } from "./pagination";
|
|
8
8
|
import { Skeleton } from "./skeleton";
|
|
9
|
+
import { TableColumnFilterControl, } from "./table-column-filter";
|
|
9
10
|
import { cn } from "./lib/utils";
|
|
10
11
|
const TableRoot = React.forwardRef(({ className, wrapperClassName: _wrapperClassName, ...props }, ref) => (_jsx("table", { ref: ref, className: cn("w-full caption-bottom text-sm", className), ...props })));
|
|
11
12
|
TableRoot.displayName = "TableRoot";
|
|
@@ -110,7 +111,7 @@ function hasColumnWidthOverrides(columnWidths) {
|
|
|
110
111
|
}
|
|
111
112
|
function getMeasuredColumnWidths(resizeHandle, columns, fallbackColumnWidths) {
|
|
112
113
|
const table = resizeHandle.closest("table");
|
|
113
|
-
const headerCells = table === null || table === void 0 ? void 0 : table.querySelectorAll('thead th
|
|
114
|
+
const headerCells = table === null || table === void 0 ? void 0 : table.querySelectorAll('thead th[data-slot="table-column-head"]');
|
|
114
115
|
if (!(headerCells === null || headerCells === void 0 ? void 0 : headerCells.length)) {
|
|
115
116
|
return fallbackColumnWidths;
|
|
116
117
|
}
|
|
@@ -177,8 +178,8 @@ function stickyClassName(sticky, kind, showShadow = false) {
|
|
|
177
178
|
: "shadow-[inset_1px_0_0_0_hsl(var(--table-border)),_-8px_0_12px_-10px_hsl(var(--foreground)_/_0.35)]"
|
|
178
179
|
: undefined;
|
|
179
180
|
const background = kind === "head" ? "bg-[hsl(var(--table-header-background))]" : "bg-card";
|
|
180
|
-
//
|
|
181
|
-
const z = kind === "
|
|
181
|
+
// Header and filter cells layer above sticky body cells on the other axis.
|
|
182
|
+
const z = kind === "cell" ? "z-10" : "z-20";
|
|
182
183
|
return cn("sticky", background, side, z, shadow);
|
|
183
184
|
}
|
|
184
185
|
function getStickyShadowVisibility(sticky, scrollState) {
|
|
@@ -245,7 +246,7 @@ export const TABLE_SKELETON_PRESETS = {
|
|
|
245
246
|
};
|
|
246
247
|
function SortableTableHead({ field, children, currentSortField, currentSortDirection, onSort, className, allowWrap = false, width, minWidth, maxWidth, align, sortLabel, resizeHandle, treatWidthAsMinWidth, containOverflow, }) {
|
|
247
248
|
const isSorted = currentSortField === field;
|
|
248
|
-
return (_jsxs(TableHead, { className: cn(resizeHandle && "relative", alignClassName(align), className), wrap: allowWrap, width: width, minWidth: minWidth, maxWidth: maxWidth, treatWidthAsMinWidth: treatWidthAsMinWidth, containOverflow: containOverflow, children: [_jsxs("button", { type: "button", onClick: () => onSort(field), className: cn("-ml-1.5 flex h-7 cursor-pointer items-center gap-1 rounded px-1.5 py-0 font-medium transition-colors hover:text-foreground", containOverflow && "max-w-full overflow-hidden", allowWrap
|
|
249
|
+
return (_jsxs(TableHead, { "data-slot": "table-column-head", className: cn(resizeHandle && "relative", alignClassName(align), className), wrap: allowWrap, width: width, minWidth: minWidth, maxWidth: maxWidth, treatWidthAsMinWidth: treatWidthAsMinWidth, containOverflow: containOverflow, children: [_jsxs("button", { type: "button", onClick: () => onSort(field), className: cn("-ml-1.5 flex h-7 cursor-pointer items-center gap-1 rounded px-1.5 py-0 font-medium transition-colors hover:text-foreground", containOverflow && "max-w-full overflow-hidden", allowWrap
|
|
249
250
|
? "max-w-full whitespace-normal text-left"
|
|
250
251
|
: "whitespace-nowrap", align === "right" && "ml-auto justify-end", align === "center" && "mx-auto justify-center"), style: { font: "inherit", color: "inherit", fontWeight: 500 }, "aria-label": `Sort by ${sortLabel !== null && sortLabel !== void 0 ? sortLabel : field}`, children: [_jsx("span", { className: cn("min-w-0", containOverflow && !allowWrap && "truncate", containOverflow && allowWrap && "break-words [overflow-wrap:anywhere]"), children: children }), _jsx("span", { className: "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center", children: isSorted ? (currentSortDirection === "asc" ? (_jsx(ArrowUpIcon, { className: "shrink-0", size: 14 })) : (_jsx(ArrowDownIcon, { className: "shrink-0", size: 14 }))) : (_jsx(ArrowUpDown, { className: "h-3.5 w-3.5 shrink-0 opacity-50" })) })] }), resizeHandle] }));
|
|
251
252
|
}
|
|
@@ -331,6 +332,7 @@ function isStickyResizeBoundary(column, nextColumn) {
|
|
|
331
332
|
function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { message: "No data available", description: "There are no rows to display in this table yet.", className: "border-0 bg-transparent px-0 py-6" })), toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, minHeight, sorting, pagination, loading = false, loadingRows = 5, visibleColumnKeys, resizableColumns = false, columnResizeMode = "pair", columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState = false, emptyStateMinHeight = DEFAULT_CENTERED_EMPTY_STATE_MIN_HEIGHT, striped = false, rowExpansion, onRowClick, onScroll, style, ...props }) {
|
|
332
333
|
const visibleColumns = getVisibleColumns(columns, visibleColumnKeys);
|
|
333
334
|
const hasExpansionColumn = Boolean(rowExpansion);
|
|
335
|
+
const hasColumnFilters = visibleColumns.some((column) => column.filter);
|
|
334
336
|
const isGrowResizeMode = columnResizeMode === "grow";
|
|
335
337
|
const isColumnResizingEnabled = resizableColumns || visibleColumns.some((column) => column.resizable);
|
|
336
338
|
// Use auto layout by default so the browser can grow a column to contain its
|
|
@@ -561,18 +563,25 @@ function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { mess
|
|
|
561
563
|
minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled),
|
|
562
564
|
maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled),
|
|
563
565
|
treatWidthAsMinWidth: !isColumnResizingEnabled,
|
|
564
|
-
}) }, column.key)))] }),
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
566
|
+
}) }, column.key)))] }), _jsxs(TableHeader, { children: [_jsxs(TableRow, { children: [hasExpansionColumn ? (_jsx(TableHead, { "data-slot": "table-expansion-head", "aria-label": "Expand rows", className: "w-12 px-2" })) : null, visibleColumns.map((column, columnIndex) => {
|
|
567
|
+
var _a;
|
|
568
|
+
const sortField = (_a = column.sortKey) !== null && _a !== void 0 ? _a : column.key;
|
|
569
|
+
const isSortable = column.sortable && sorting;
|
|
570
|
+
const columnWidth = getRenderedColumnWidth({
|
|
571
|
+
column,
|
|
572
|
+
columnWidths: resolvedColumnWidths,
|
|
573
|
+
resizing: isColumnResizingEnabled,
|
|
574
|
+
});
|
|
575
|
+
const resizeHandle = renderResizeHandle(column, columnIndex);
|
|
576
|
+
return (isSortable ? (_jsx(SortableTableHead, { field: sortField, currentSortField: sorting.field, currentSortDirection: sorting.direction, onSort: sorting.onSort, className: cn(stickyClassName(column.sticky, "head", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.headerClassName), allowWrap: column.wrap, width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), align: column.align, sortLabel: column.sortLabel, resizeHandle: resizeHandle, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, children: column.header }, column.key)) : (_jsxs(TableHead, { "data-slot": "table-column-head", width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), wrap: column.wrap, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn(resizeHandle && "relative", alignClassName(column.align), stickyClassName(column.sticky, "head", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.headerClassName), children: [column.header, resizeHandle] }, column.key)));
|
|
577
|
+
})] }), hasColumnFilters ? (_jsxs(TableRow, { "data-slot": "table-filter-row", className: "!bg-card", children: [hasExpansionColumn ? (_jsx(TableHead, { "data-slot": "table-expansion-filter-cell", "aria-hidden": "true", className: "w-12 px-2" })) : null, visibleColumns.map((column) => {
|
|
578
|
+
const columnWidth = getRenderedColumnWidth({
|
|
579
|
+
column,
|
|
580
|
+
columnWidths: resolvedColumnWidths,
|
|
581
|
+
resizing: isColumnResizingEnabled,
|
|
582
|
+
});
|
|
583
|
+
return (_jsx(TableHead, { "data-slot": "table-filter-cell", width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn("relative h-11 px-2 py-1 font-normal", stickyClassName(column.sticky, "filter", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.filterClassName), children: column.filter ? (_jsx("div", { "data-slot": "table-filter-control", className: "absolute inset-x-2 inset-y-1 min-w-0", children: _jsx(TableColumnFilterControl, { filter: column.filter, label: getColumnResizeLabel(column) }) })) : null }, column.key));
|
|
584
|
+
})] })) : null] }), _jsx(TableBody, { children: loading ? (_jsx(TableRowSkeleton, { rows: loadingRows, columns: [
|
|
576
585
|
...(hasExpansionColumn
|
|
577
586
|
? [{ width: "1rem", height: "1rem" }]
|
|
578
587
|
: []),
|