@caratlogic/design-system 0.1.34 → 0.1.36
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/components/molecules/Filters/DateRangeFilter/DateRangeFilter.d.ts +2 -2
- package/dist/components/molecules/Filters/DateRangeFilter/DateRangeFilter.d.ts.map +1 -1
- package/dist/components/molecules/Filters/DateRangeFilter/DateRangeFilter.hooks.d.ts +0 -1
- package/dist/components/molecules/Filters/DateRangeFilter/DateRangeFilter.hooks.d.ts.map +1 -1
- package/dist/components/molecules/Filters/DateRangeFilter/DateRangeFilter.types.d.ts +2 -0
- package/dist/components/molecules/Filters/DateRangeFilter/DateRangeFilter.types.d.ts.map +1 -1
- package/dist/components/molecules/Filters/Filters.utils.d.ts.map +1 -1
- package/dist/components/molecules/Filters/NumberRangeInputs/NumberRangeInputs.d.ts.map +1 -1
- package/dist/components/molecules/Filters/NumberRangeInputs/NumberRangeInputs.utils.d.ts +2 -3
- package/dist/components/molecules/Filters/NumberRangeInputs/NumberRangeInputs.utils.d.ts.map +1 -1
- package/dist/components/molecules/Filters/PickListDropdown/PickListDropdown.accessibility.d.ts +2 -0
- package/dist/components/molecules/Filters/PickListDropdown/PickListDropdown.accessibility.d.ts.map +1 -0
- package/dist/components/molecules/Filters/PickListDropdown/PickListDropdown.d.ts +6 -0
- package/dist/components/molecules/Filters/PickListDropdown/PickListDropdown.d.ts.map +1 -0
- package/dist/components/molecules/Filters/PickListDropdown/PickListDropdown.hooks.d.ts +4 -0
- package/dist/components/molecules/Filters/PickListDropdown/PickListDropdown.hooks.d.ts.map +1 -0
- package/dist/components/molecules/Filters/PickListDropdown/PickListDropdown.types.d.ts +22 -0
- package/dist/components/molecules/Filters/PickListDropdown/PickListDropdown.types.d.ts.map +1 -0
- package/dist/components/molecules/Filters/PickListDropdown/PickListDropdown.utils.d.ts +6 -0
- package/dist/components/molecules/Filters/PickListDropdown/PickListDropdown.utils.d.ts.map +1 -0
- package/dist/components/molecules/Filters/PickListDropdown/index.d.ts +3 -0
- package/dist/components/molecules/Filters/PickListDropdown/index.d.ts.map +1 -0
- package/dist/components/molecules/Filters/PickListFilter/PickListFilter.d.ts.map +1 -1
- package/dist/components/molecules/Filters/PickListFilter/PickListFilter.utils.d.ts +8 -1
- package/dist/components/molecules/Filters/PickListFilter/PickListFilter.utils.d.ts.map +1 -1
- package/dist/components/molecules/Filters/ScaleRangeFilter/ScaleRangeFilter.d.ts.map +1 -1
- package/dist/components/molecules/Filters/ScaleRangeFilter/ScaleRangeFilter.utils.d.ts +4 -1
- package/dist/components/molecules/Filters/ScaleRangeFilter/ScaleRangeFilter.utils.d.ts.map +1 -1
- package/dist/components/molecules/Filters/TriStateFilter/TriStateFilter.d.ts +1 -1
- package/dist/components/molecules/Filters/TriStateFilter/TriStateFilter.d.ts.map +1 -1
- package/dist/components/molecules/Filters/TriStateFilter/TriStateFilter.types.d.ts +2 -0
- package/dist/components/molecules/Filters/TriStateFilter/TriStateFilter.types.d.ts.map +1 -1
- package/dist/components/molecules/Filters/index.d.ts +1 -0
- package/dist/components/molecules/Filters/index.d.ts.map +1 -1
- package/dist/components/molecules/SavedSearches/SaveSearchDialog/SaveSearchDialog.parts.d.ts +2 -2
- package/dist/components/molecules/SavedSearches/SaveSearchDialog/SaveSearchDialog.parts.d.ts.map +1 -1
- package/dist/components/molecules/SavedSearches/SaveSearchDialog/SaveSearchDialog.types.d.ts +2 -0
- package/dist/components/molecules/SavedSearches/SaveSearchDialog/SaveSearchDialog.types.d.ts.map +1 -1
- package/dist/components/molecules/SavedSearches/SaveSearchDialog/index.d.ts +1 -0
- package/dist/components/molecules/SavedSearches/SaveSearchDialog/index.d.ts.map +1 -1
- package/dist/components/organisms/Filters/FilterField/FilterField.d.ts.map +1 -1
- package/dist/components/organisms/Filters/FilterField/FilterField.types.d.ts +4 -4
- package/dist/components/organisms/Filters/FilterField/FilterField.types.d.ts.map +1 -1
- package/dist/components/organisms/Filters/FilterField/FilterField.utils.d.ts +4 -4
- package/dist/components/organisms/Filters/FilterField/FilterField.utils.d.ts.map +1 -1
- package/dist/components/organisms/Filters/FilterSurface/FilterSurface.d.ts +2 -2
- package/dist/components/organisms/Filters/FilterSurface/FilterSurface.d.ts.map +1 -1
- package/dist/components/organisms/Filters/FilterSurface/FilterSurface.hooks.d.ts +5 -0
- package/dist/components/organisms/Filters/FilterSurface/FilterSurface.hooks.d.ts.map +1 -1
- package/dist/components/organisms/Filters/FilterSurface/FilterSurface.parts.d.ts +3 -1
- package/dist/components/organisms/Filters/FilterSurface/FilterSurface.parts.d.ts.map +1 -1
- package/dist/components/organisms/Filters/FilterSurface/FilterSurface.types.d.ts +8 -0
- package/dist/components/organisms/Filters/FilterSurface/FilterSurface.types.d.ts.map +1 -1
- package/dist/components/organisms/Filters/FilterSurface/FilterSurface.utils.d.ts +6 -0
- package/dist/components/organisms/Filters/FilterSurface/FilterSurface.utils.d.ts.map +1 -1
- package/dist/index.cjs.js +138 -51
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +137 -52
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -15446,7 +15446,8 @@ function ApiComboBox({ contactType, selectedItem, onSelect, fetchBusinessContact
|
|
|
15446
15446
|
}
|
|
15447
15447
|
|
|
15448
15448
|
// A number with an optional `k` (thousand), `M` (million) or `B` (billion), any case, e.g. `1.5`, `2k`, `1.2M`.
|
|
15449
|
-
|
|
15449
|
+
// An optional minus sign allows negative values (e.g. a Rapnet discount of -35); `-30--10` still reads as a range.
|
|
15450
|
+
const NUMBER = String.raw `(-?(?:\d+(?:\.\d+)?|\.\d+)[kKmMbB]?)`;
|
|
15450
15451
|
const SUFFIX_MULTIPLIER = {
|
|
15451
15452
|
k: 1000,
|
|
15452
15453
|
m: 1000000,
|
|
@@ -15871,8 +15872,9 @@ function filterOptionsByQuery(options, query) {
|
|
|
15871
15872
|
return options.filter((option) => option.label.toLowerCase().includes(needle));
|
|
15872
15873
|
}
|
|
15873
15874
|
const getChipClassName = getPillClassName;
|
|
15874
|
-
|
|
15875
|
-
|
|
15875
|
+
/** A tile with a count reads like a list row: name on the left, count badge on the right. */
|
|
15876
|
+
function getTileClassName(selected, hasCount = false) {
|
|
15877
|
+
return getSelectableOptionClassName(selected, cn('flex min-h-12 min-w-0 items-center gap-2 rounded-md px-3 py-1.5', hasCount ? 'justify-between text-left' : 'justify-center text-center'));
|
|
15876
15878
|
}
|
|
15877
15879
|
/** The list's search box only appears once there are enough options to need it. */
|
|
15878
15880
|
const SEARCH_MIN_OPTIONS = 9;
|
|
@@ -15885,6 +15887,15 @@ const FILL_WRAPPER_CLASS_NAME = 'flex min-h-0 flex-1 flex-col';
|
|
|
15885
15887
|
const LIST_COLUMN_MIN_WIDTH = '10rem';
|
|
15886
15888
|
const getOptionsScrollClassName = (fill) => fill ? OPTIONS_FILL_CLASS_NAME : OPTIONS_SCROLL_CLASS_NAME;
|
|
15887
15889
|
const LIST_ROW_CLASS_NAME = getOptionRowClassName();
|
|
15890
|
+
const COUNT_BADGE_TONES = {
|
|
15891
|
+
neutral: 'bg-muted text-muted-foreground',
|
|
15892
|
+
tinted: 'bg-primary/10 text-primary',
|
|
15893
|
+
solid: 'bg-primary-foreground/20 text-primary-foreground',
|
|
15894
|
+
};
|
|
15895
|
+
/** How many items carry the option, as a small round badge at the right. */
|
|
15896
|
+
const getCountBadgeClassName = (tone) => cn('inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 text-xs font-medium tabular-nums', COUNT_BADGE_TONES[tone]);
|
|
15897
|
+
const COUNT_BADGE_CLASS_NAME = getCountBadgeClassName('neutral');
|
|
15898
|
+
const formatOptionCount = (count) => count.toLocaleString('en-US');
|
|
15888
15899
|
/** Picking a value: a single-choice list replaces (or clears) it, a multi-choice list toggles it. */
|
|
15889
15900
|
const pickValue = (values, value, single) => {
|
|
15890
15901
|
if (!single)
|
|
@@ -15933,12 +15944,12 @@ function PickListFilter({ options, value, onChange, ariaLabel, layout = 'list',
|
|
|
15933
15944
|
? getGridColumnsStyle(undefined, LIST_COLUMN_MIN_WIDTH)
|
|
15934
15945
|
: undefined, wrapperClassName: wrapperClassName, children: visibleOptions.map((option) => (jsxs("label", { className: LIST_ROW_CLASS_NAME, children: [single ? (jsx("input", { type: "radio", name: radioName, checked: value.includes(option.value),
|
|
15935
15946
|
// A click on the picked row clears it (radios can't be unticked natively).
|
|
15936
|
-
onClick: () => toggle(option), onChange: () => undefined, className: LIST_RADIO_CLASS_NAME })) : (jsx(Checkbox, { className: LIST_CHECKBOX_CLASS_NAME, checked: value.includes(option.value), onCheckedChange: () => toggle(option) })), option.content ? (jsx("span", { className: "min-w-0 flex-1", children: option.content })) : (jsx(TruncatedLabel, { text: option.label, className: "flex-1" })),
|
|
15947
|
+
onClick: () => toggle(option), onChange: () => undefined, className: LIST_RADIO_CLASS_NAME })) : (jsx(Checkbox, { className: LIST_CHECKBOX_CLASS_NAME, checked: value.includes(option.value), onCheckedChange: () => toggle(option) })), option.content ? (jsx("span", { className: "min-w-0 flex-1", children: option.content })) : (jsx(TruncatedLabel, { text: option.label, className: "flex-1" })), option.hint && (jsx("span", { className: "shrink-0 text-muted-foreground", children: option.hint })), jsx(OptionSwatch, { color: option.swatch }), option.count !== undefined && (jsx("span", { className: COUNT_BADGE_CLASS_NAME, children: formatOptionCount(option.count) }))] }, option.value))) })] }));
|
|
15937
15948
|
}
|
|
15938
15949
|
if (layout === 'tiles') {
|
|
15939
|
-
return (jsx(ScrollFade, { role: "group", "aria-label": ariaLabel, wrapperClassName: wrapperClassName, className: cn('grid content-start gap-1.5', scrollClassName), style: getGridColumnsStyle(columns), children: visibleOptions.map((option) => (jsxs("button", { type: "button", "aria-pressed": value.includes(option.value), onClick: () => toggle(option), className: getTileClassName(value.includes(option.value)), children: [jsx(TruncatedLabel, { text: option.label }), option.count !== undefined && (jsx("span", { className:
|
|
15950
|
+
return (jsx(ScrollFade, { role: "group", "aria-label": ariaLabel, wrapperClassName: wrapperClassName, className: cn('grid content-start gap-1.5', scrollClassName), style: getGridColumnsStyle(columns), children: visibleOptions.map((option) => (jsxs("button", { type: "button", "aria-pressed": value.includes(option.value), onClick: () => toggle(option), className: getTileClassName(value.includes(option.value), option.count !== undefined), children: [jsx(TruncatedLabel, { text: option.label }), option.count !== undefined && (jsx("span", { className: getCountBadgeClassName(value.includes(option.value) ? 'tinted' : 'neutral'), children: formatOptionCount(option.count) }))] }, option.value))) }));
|
|
15940
15951
|
}
|
|
15941
|
-
return (jsx(ScrollFade, { role: "group", "aria-label": ariaLabel, wrapperClassName: wrapperClassName, className: cn('flex flex-wrap content-start gap-1.5', scrollClassName), children: visibleOptions.map((option) => (jsxs("button", { type: "button", "aria-pressed": value.includes(option.value), onClick: () => toggle(option), className: getChipClassName(value.includes(option.value)), children: [jsx(OptionSwatch, { color: option.swatch, round: true }), jsx("span", { children: option.label }), option.count !== undefined && (jsx("span", { className: "font-normal text-muted-foreground", children: option.count }))] }, option.value))) }));
|
|
15952
|
+
return (jsx(ScrollFade, { role: "group", "aria-label": ariaLabel, wrapperClassName: wrapperClassName, className: cn('flex flex-wrap content-start gap-1.5', scrollClassName), children: visibleOptions.map((option) => (jsxs("button", { type: "button", "aria-pressed": value.includes(option.value), onClick: () => toggle(option), className: getChipClassName(value.includes(option.value)), children: [jsx(OptionSwatch, { color: option.swatch, round: true }), jsx("span", { children: option.label }), option.count !== undefined && (jsx("span", { className: "text-xs font-normal tabular-nums text-muted-foreground", children: formatOptionCount(option.count) }))] }, option.value))) }));
|
|
15942
15953
|
}
|
|
15943
15954
|
|
|
15944
15955
|
const TRIGGER_BASE = 'h-9 !w-fit shadow-[0_1px_2px_0_rgba(0,0,0,0.05)] gap-2 text-foreground bg-card hover:bg-muted/40 border border-border rounded-lg text-sm font-medium px-3';
|
|
@@ -38698,18 +38709,20 @@ function useDateRangeFilter(value, onChange, today, fiscalYearStartMonth) {
|
|
|
38698
38709
|
selectPreset: (preset) => onChange(togglePreset(value, preset)),
|
|
38699
38710
|
handleFromChange: (date) => onChange(buildCustomRange(toIsoOrEmpty(date), to)),
|
|
38700
38711
|
handleToChange: (date) => onChange(buildCustomRange(from, toIsoOrEmpty(date))),
|
|
38701
|
-
clear: () => onChange(undefined),
|
|
38702
38712
|
};
|
|
38703
38713
|
}
|
|
38704
38714
|
|
|
38705
|
-
/** Date filter body: presets on the left, a From / To range on the right. */
|
|
38706
|
-
function DateRangeFilter({ value, onChange, ariaLabel, fiscalYearStartMonth = 1, today = new Date(), }) {
|
|
38707
|
-
const { from, to, selectPreset, handleFromChange, handleToChange
|
|
38715
|
+
/** Date filter body: presets on the left, a From / To range on the right. Clearing is the surrounding panel's job. */
|
|
38716
|
+
function DateRangeFilter({ value, onChange, ariaLabel, fiscalYearStartMonth = 1, today = new Date(), compact = false, }) {
|
|
38717
|
+
const { from, to, selectPreset, handleFromChange, handleToChange } = useDateRangeFilter(value, onChange, today, fiscalYearStartMonth);
|
|
38718
|
+
if (compact) {
|
|
38719
|
+
return (jsxs("div", { className: "flex flex-col gap-2.5 text-xs", children: [jsx("div", { role: "group", "aria-label": getPresetGroupLabel(ariaLabel), className: "flex flex-wrap gap-1.5", children: DATE_PRESET_OPTIONS.map((preset) => (jsx("button", { type: "button", "aria-pressed": value?.preset === preset.value, onClick: () => selectPreset(preset.value), className: getPillClassName(value?.preset === preset.value), children: preset.label }, preset.value))) }), jsxs("div", { className: "grid grid-cols-2 gap-2", children: [jsx(DateInput, { value: fromIsoDate(from), max: to || undefined, placeholder: "From", ariaLabel: getDateInputLabel(ariaLabel, 'from'), onChange: handleFromChange }), jsx(DateInput, { value: fromIsoDate(to), min: from || undefined, placeholder: "To", ariaLabel: getDateInputLabel(ariaLabel, 'to'), onChange: handleToChange, align: "end" })] })] }));
|
|
38720
|
+
}
|
|
38708
38721
|
return (jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)] divide-x divide-border text-xs", children: [jsx("div", { role: "group", "aria-label": getPresetGroupLabel(ariaLabel), className: "flex flex-col gap-1 pr-3", children: DATE_PRESET_OPTIONS.map((preset) => (jsx("button", { type: "button", "aria-pressed": value?.preset === preset.value, onClick: () => selectPreset(preset.value), className: getPresetClassName$1(value?.preset === preset.value), children: preset.label }, preset.value))) }), jsxs("div", { className: "flex min-w-0 flex-col gap-3 pl-3", children: [jsxs("label", { className: "flex min-w-0 flex-col gap-1 text-muted-foreground", children: ["From", jsx(DateInput
|
|
38709
38722
|
// The inputs sit on the card's right; an end-aligned calendar stays under them.
|
|
38710
38723
|
, {
|
|
38711
38724
|
// The inputs sit on the card's right; an end-aligned calendar stays under them.
|
|
38712
|
-
align: "end", value: fromIsoDate(from), max: to || undefined, ariaLabel: getDateInputLabel(ariaLabel, 'from'), onChange: handleFromChange })] }), jsxs("label", { className: "flex min-w-0 flex-col gap-1 text-muted-foreground", children: ["To", jsx(DateInput, { value: fromIsoDate(to), min: from || undefined, ariaLabel: getDateInputLabel(ariaLabel, 'to'), onChange: handleToChange, align: "end" })] })
|
|
38725
|
+
align: "end", value: fromIsoDate(from), max: to || undefined, ariaLabel: getDateInputLabel(ariaLabel, 'from'), onChange: handleFromChange })] }), jsxs("label", { className: "flex min-w-0 flex-col gap-1 text-muted-foreground", children: ["To", jsx(DateInput, { value: fromIsoDate(to), min: from || undefined, ariaLabel: getDateInputLabel(ariaLabel, 'to'), onChange: handleToChange, align: "end" })] })] })] }));
|
|
38713
38726
|
}
|
|
38714
38727
|
|
|
38715
38728
|
const getClearButtonLabel = (ariaLabel) => `Clear ${ariaLabel}`;
|
|
@@ -38769,13 +38782,17 @@ function getNextScaleRange(anchor, option) {
|
|
|
38769
38782
|
? { from: option, to: option }
|
|
38770
38783
|
: { from: anchor, to: option };
|
|
38771
38784
|
}
|
|
38772
|
-
function getScaleButtonClassName(position, fill = false) {
|
|
38773
|
-
return cn('flex min-w-0 cursor-pointer
|
|
38785
|
+
function getScaleButtonClassName(position, fill = false, hasCount = false) {
|
|
38786
|
+
return cn('flex min-w-0 cursor-pointer items-center gap-2 rounded-md border transition-colors',
|
|
38787
|
+
// With a count: name left, badge right (like a list row); without: name centred.
|
|
38788
|
+
hasCount ? 'justify-between text-left' : 'justify-center text-center', fill ? 'min-h-12 px-2 py-1.5 text-sm' : 'min-h-10 px-1.5 py-1 text-xs', FILTER_FOCUS_RING,
|
|
38774
38789
|
// Unselected options look exactly like unselected pick-list tiles.
|
|
38775
38790
|
position === 'outside' && getSelectableOptionClassName(false), position === 'end' &&
|
|
38776
38791
|
'border-primary bg-primary font-medium text-primary-foreground', position === 'between' &&
|
|
38777
38792
|
'border-primary/30 bg-primary/10 font-medium text-primary');
|
|
38778
38793
|
}
|
|
38794
|
+
/** The count badge follows the tile: plain, tinted inside the range, solid on an end. */
|
|
38795
|
+
const getScaleCountTone = (position) => position === 'end' ? 'solid' : position === 'between' ? 'tinted' : 'neutral';
|
|
38779
38796
|
|
|
38780
38797
|
/** First click picks the start option, the next click the end option; a click after that starts over. */
|
|
38781
38798
|
function useScaleRangeFilter({ value, onChange, }) {
|
|
@@ -38796,7 +38813,7 @@ function ScaleRangeFilter({ options, value, onChange, ariaLabel, columns, fill =
|
|
|
38796
38813
|
const { selectOption } = useScaleRangeFilter({ value, onChange });
|
|
38797
38814
|
return (jsx("div", { role: "group", "aria-label": ariaLabel, className: fill ? 'grid gap-2' : 'grid gap-1.5', style: getGridColumnsStyle(columns), children: options.map((option) => {
|
|
38798
38815
|
const position = getScalePosition(options, value, option.value);
|
|
38799
|
-
return (
|
|
38816
|
+
return (jsxs("button", { type: "button", "aria-pressed": position !== 'outside', "aria-label": option.label, onClick: () => selectOption(option.value), className: getScaleButtonClassName(position, fill, option.count !== undefined), children: [jsx(TruncatedLabel, { text: option.label }), option.count !== undefined && (jsx("span", { className: getCountBadgeClassName(getScaleCountTone(position)), children: formatOptionCount(option.count) }))] }, option.value));
|
|
38800
38817
|
}) }));
|
|
38801
38818
|
}
|
|
38802
38819
|
|
|
@@ -38830,6 +38847,43 @@ function FilterCardSkeleton({ variant = 'list', className, }) {
|
|
|
38830
38847
|
return (jsxs("div", { "aria-hidden": "true", className: cn(CARD_CLASS_NAME, className), children: [jsx(SquareSkeleton, { height: 16, width: 96, rounded: "sm" }), jsx(Body, { variant: variant })] }));
|
|
38831
38848
|
}
|
|
38832
38849
|
|
|
38850
|
+
const getRemoveLabel = (label) => `Remove ${label}`;
|
|
38851
|
+
|
|
38852
|
+
function usePickListDropdown(options, value, onChange, single) {
|
|
38853
|
+
const [open, setOpen] = useState(false);
|
|
38854
|
+
const selected = useMemo(() => options.filter((option) => value.includes(option.value)), [options, value]);
|
|
38855
|
+
return {
|
|
38856
|
+
open,
|
|
38857
|
+
setOpen,
|
|
38858
|
+
selected,
|
|
38859
|
+
handleChange: (values) => {
|
|
38860
|
+
onChange(values);
|
|
38861
|
+
if (single)
|
|
38862
|
+
setOpen(false);
|
|
38863
|
+
},
|
|
38864
|
+
remove: (removed) => onChange(value.filter((current) => current !== removed)),
|
|
38865
|
+
};
|
|
38866
|
+
}
|
|
38867
|
+
|
|
38868
|
+
const TRIGGER_CLASS_NAME = cn('flex h-9 w-full min-w-0 cursor-pointer items-center justify-between gap-2 rounded-md border border-input bg-card px-3 text-left text-sm transition-colors hover:bg-muted/40', FILTER_FOCUS_RING);
|
|
38869
|
+
const CHIP_CLASS_NAME = 'inline-flex h-7 max-w-full items-center gap-1 rounded-full border border-primary/30 bg-primary/5 pr-1 pl-2.5 text-xs text-foreground';
|
|
38870
|
+
const CHIP_REMOVE_CLASS_NAME = cn('inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-full text-muted-foreground hover:bg-primary/10 hover:text-foreground', FILTER_FOCUS_RING);
|
|
38871
|
+
/** `Ruby` for one, `3 selected` for several; the pills below name them. */
|
|
38872
|
+
function getTriggerText(selectedLabels, placeholder) {
|
|
38873
|
+
if (selectedLabels.length === 0)
|
|
38874
|
+
return placeholder;
|
|
38875
|
+
if (selectedLabels.length === 1)
|
|
38876
|
+
return selectedLabels[0];
|
|
38877
|
+
return `${selectedLabels.length} selected`;
|
|
38878
|
+
}
|
|
38879
|
+
|
|
38880
|
+
/** A long pick list as a compact select: the searchable list opens on demand, picks show as removable pills. */
|
|
38881
|
+
function PickListDropdown({ options, value, onChange, ariaLabel, placeholder, searchPlaceholder, single = false, }) {
|
|
38882
|
+
const { open, setOpen, selected, handleChange, remove } = usePickListDropdown(options, value, onChange, single);
|
|
38883
|
+
const labels = selected.map((option) => option.label);
|
|
38884
|
+
return (jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [jsxs(Popover, { open: open, onOpenChange: setOpen, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs("button", { type: "button", "aria-label": ariaLabel, className: TRIGGER_CLASS_NAME, children: [jsx("span", { className: cn('min-w-0 flex-1', labels.length === 0 && 'text-placeholder'), children: jsx(TruncatedLabel, { text: getTriggerText(labels, placeholder) }) }), jsx(ChevronDown, { "aria-hidden": "true", className: "size-4 shrink-0 text-muted-foreground" })] }) }), jsx(PopoverContent, { align: "start", sideOffset: 4, className: "w-[var(--radix-popover-trigger-width)] min-w-64 p-3", children: jsx(PickListFilter, { ariaLabel: ariaLabel, options: options, value: value, onChange: handleChange, layout: "list", searchPlaceholder: searchPlaceholder ?? 'Search', alwaysSearchable: true, single: single }) })] }), !single && selected.length > 0 && (jsx("ul", { className: "flex flex-wrap gap-1.5", children: selected.map((option) => (jsxs("li", { className: CHIP_CLASS_NAME, children: [jsx(TruncatedLabel, { text: option.label }), jsx("button", { type: "button", "aria-label": getRemoveLabel(option.label), onClick: () => remove(option.value), className: CHIP_REMOVE_CLASS_NAME, children: jsx(X$2, { "aria-hidden": "true", className: "size-3" }) })] }, option.value))) }))] }));
|
|
38885
|
+
}
|
|
38886
|
+
|
|
38833
38887
|
const getClearIdsLabel = (ariaLabel) => `Clear ${ariaLabel}`;
|
|
38834
38888
|
|
|
38835
38889
|
const DEFAULT_ID_HELPER_TEXT = 'Paste from Excel or an email. Commas, spaces and new lines all split IDs.';
|
|
@@ -38968,13 +39022,13 @@ function NumberExpressionFilter({ value, onChange, ariaLabel, placeholder, note,
|
|
|
38968
39022
|
const REVERSED_RANGE_MESSAGE = 'From must not be more than To.';
|
|
38969
39023
|
const getBoundLabel = (ariaLabel, bound) => `${ariaLabel} ${bound}`;
|
|
38970
39024
|
|
|
38971
|
-
const DECIMAL_DRAFT_RE =
|
|
38972
|
-
/** Allows partial decimals (`1.`) while typing; rejects anything else. */
|
|
39025
|
+
const DECIMAL_DRAFT_RE = /^-?\d*\.?\d*$/;
|
|
39026
|
+
/** Allows partial decimals (`1.`, `-`, `-0.`) while typing; rejects anything else. */
|
|
38973
39027
|
function isDecimalDraft(text) {
|
|
38974
39028
|
return DECIMAL_DRAFT_RE.test(text);
|
|
38975
39029
|
}
|
|
38976
39030
|
function toBound(text) {
|
|
38977
|
-
if (!text || text === '.')
|
|
39031
|
+
if (!text || text === '.' || text === '-' || text === '-.')
|
|
38978
39032
|
return undefined;
|
|
38979
39033
|
return Number(text);
|
|
38980
39034
|
}
|
|
@@ -39003,7 +39057,7 @@ function isSameRange(a, b) {
|
|
|
39003
39057
|
function getBoundInputClassName(filled) {
|
|
39004
39058
|
return cn('h-9 min-w-0 flex-1 text-sm', filled && 'border-primary bg-primary/5');
|
|
39005
39059
|
}
|
|
39006
|
-
const getPresetClassName = getPillClassName;
|
|
39060
|
+
const getPresetClassName = (selected) => cn(getPillClassName(selected), 'shrink-0 whitespace-nowrap');
|
|
39007
39061
|
|
|
39008
39062
|
function useNumberRangeInputs(value, onChange) {
|
|
39009
39063
|
const minText = toBoundText(value?.min);
|
|
@@ -39037,7 +39091,7 @@ function useNumberRangeInputs(value, onChange) {
|
|
|
39037
39091
|
/** From – To number inputs for full-page filter cards, with optional quick-pick presets. */
|
|
39038
39092
|
function NumberRangeInputs({ value, onChange, ariaLabel, fromPlaceholder = 'From', toPlaceholder = 'To', presets, presetsLabel, }) {
|
|
39039
39093
|
const { fromDraft, toDraft, reversed, handleFromChange, handleToChange, isPresetSelected, togglePreset, } = useNumberRangeInputs(value, onChange);
|
|
39040
|
-
return (jsxs("div", { className: "flex flex-col gap-3", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Input, { inputMode: "decimal", value: fromDraft, placeholder: fromPlaceholder, "aria-label": getBoundLabel(ariaLabel, 'from'), "aria-invalid": reversed || undefined, onChange: handleFromChange, className: getBoundInputClassName(fromDraft !== '') }), jsx("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u2013" }), jsx(Input, { inputMode: "decimal", value: toDraft, placeholder: toPlaceholder, "aria-label": getBoundLabel(ariaLabel, 'to'), "aria-invalid": reversed || undefined, onChange: handleToChange, className: getBoundInputClassName(toDraft !== '') })] }), reversed && (jsx("p", { role: "alert", className: "text-xs text-destructive", children: REVERSED_RANGE_MESSAGE })), presets && presets.length > 0 && (jsxs("div", { className: "flex flex-col gap-2", children: [presetsLabel && (jsx("p", { className: "text-xs text-muted-foreground", children: presetsLabel })), jsx("div", { className: "flex
|
|
39094
|
+
return (jsxs("div", { className: "flex flex-col gap-3", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Input, { inputMode: "decimal", value: fromDraft, placeholder: fromPlaceholder, "aria-label": getBoundLabel(ariaLabel, 'from'), "aria-invalid": reversed || undefined, onChange: handleFromChange, className: getBoundInputClassName(fromDraft !== '') }), jsx("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u2013" }), jsx(Input, { inputMode: "decimal", value: toDraft, placeholder: toPlaceholder, "aria-label": getBoundLabel(ariaLabel, 'to'), "aria-invalid": reversed || undefined, onChange: handleToChange, className: getBoundInputClassName(toDraft !== '') })] }), reversed && (jsx("p", { role: "alert", className: "text-xs text-destructive", children: REVERSED_RANGE_MESSAGE })), presets && presets.length > 0 && (jsxs("div", { className: "flex flex-col gap-2", children: [presetsLabel && (jsx("p", { className: "text-xs text-muted-foreground", children: presetsLabel })), jsx("div", { className: "flex gap-1.5 overflow-x-auto pb-1 [scrollbar-width:thin]", children: presets.map((preset) => (jsx("button", { type: "button", "aria-pressed": isPresetSelected(preset), onClick: () => togglePreset(preset), className: getPresetClassName(isPresetSelected(preset)), children: preset.label }, preset.label))) })] }))] }));
|
|
39041
39095
|
}
|
|
39042
39096
|
|
|
39043
39097
|
const NEXT_KEYS = new Set(['ArrowDown', 'ArrowRight']);
|
|
@@ -39070,7 +39124,10 @@ function getRadioCircleClassName(checked) {
|
|
|
39070
39124
|
}
|
|
39071
39125
|
|
|
39072
39126
|
/** Any / Yes / No radio group for boolean columns (e.g. Active). */
|
|
39073
|
-
function TriStateFilter({ value, onChange, ariaLabel, }) {
|
|
39127
|
+
function TriStateFilter({ value, onChange, ariaLabel, compact = false, }) {
|
|
39128
|
+
if (compact) {
|
|
39129
|
+
return (jsx("div", { role: "radiogroup", "aria-label": ariaLabel, onKeyDown: createRadioGroupKeyDownHandler(value, onChange), className: "flex flex-wrap gap-1.5", children: TRI_STATE_OPTIONS.map((option) => (jsx("button", { type: "button", role: "radio", "aria-checked": option.value === value, tabIndex: option.value === value ? 0 : -1, "data-value": option.value, onClick: () => onChange(option.value), className: getPillClassName(option.value === value), children: option.label }, option.value))) }));
|
|
39130
|
+
}
|
|
39074
39131
|
return (jsx("div", { role: "radiogroup", "aria-label": ariaLabel, onKeyDown: createRadioGroupKeyDownHandler(value, onChange), className: "flex flex-col gap-0.5", children: TRI_STATE_OPTIONS.map((option) => {
|
|
39075
39132
|
const checked = option.value === value;
|
|
39076
39133
|
return (jsxs("button", { type: "button", role: "radio", "aria-checked": checked, tabIndex: checked ? 0 : -1, "data-value": option.value, onClick: () => onChange(option.value), className: getRadioRowClassName(checked), children: [jsx("span", { "aria-hidden": "true", className: getRadioCircleClassName(checked), children: checked && jsx("span", { className: "size-2 rounded-full bg-primary" }) }), option.label] }, option.value));
|
|
@@ -39340,9 +39397,9 @@ function useSaveSearchDialog({ open, defaultName, onSave, saving, existingNames,
|
|
|
39340
39397
|
};
|
|
39341
39398
|
}
|
|
39342
39399
|
|
|
39343
|
-
/** The search a
|
|
39344
|
-
function
|
|
39345
|
-
return (jsxs("div", { className: WARNING_DETAIL_CLASS_NAME, children: [jsx("span", { className: "text-xs text-muted-foreground", children: label }), jsx(TruncatedLabel, { text: value, className: "text-sm font-semibold text-foreground" })] }));
|
|
39400
|
+
/** The search a dialog is about, in its body (long names stay on one line, full text on hover). */
|
|
39401
|
+
function SavedSearchDetail({ label, value, chips, }) {
|
|
39402
|
+
return (jsxs("div", { className: WARNING_DETAIL_CLASS_NAME, children: [jsx("span", { className: "text-xs text-muted-foreground", children: label }), jsx(TruncatedLabel, { text: value, className: "text-sm font-semibold text-foreground" }), chips && chips.length > 0 && (jsx(SavedSearchChipList, { chips: chips, collapsible: true, className: "mt-1.5" }))] }));
|
|
39346
39403
|
}
|
|
39347
39404
|
|
|
39348
39405
|
/** Names the current filters and saves them; also used to rename a saved search. */
|
|
@@ -39368,7 +39425,7 @@ function SaveSearchDialog({ open, onClose, onSave, defaultName = '', chips, titl
|
|
|
39368
39425
|
open: open && step === 'form', onClose: onClose, title: title, icon: icon, footer: jsxs(Fragment$2, { children: [jsx(Button, { appearance: "outline", color: "secondary", size: "md", onClick: onClose, children: "Cancel" }), jsx(Button, { appearance: "solid", color: "primary", size: "md", type: "submit", form: formId, loading: saving, children: submitLabel })] }), children: jsxs("form", { id: formId, noValidate: true, className: "flex flex-col gap-4 px-6 pb-5", onSubmit: (event) => {
|
|
39369
39426
|
event.preventDefault();
|
|
39370
39427
|
submit();
|
|
39371
|
-
}, children: [jsx(Field, { label: NAME_LABEL, required: true, error: error, input: jsx(Input, { id: inputId, "aria-label": NAME_LABEL, "aria-invalid": Boolean(error) || undefined, value: name, maxLength: SAVED_SEARCH_NAME_MAX_LENGTH, onChange: (event) => setName(event.target.value), autoFocus: true }) }), chips && (jsxs("div", { className: "flex flex-col gap-1", children: [jsx("span", { className: "text-[13px] text-label", children: FILTERS_LABEL }), jsx(SavedSearchChipList, { chips: chips, className: FILTERS_PANEL_CLASS_NAME })] })), note && (jsxs("p", { className: "flex items-start gap-1.5 text-xs text-muted-foreground", children: [jsx(Info, { "aria-hidden": "true", className: "mt-px size-3.5 shrink-0" }), jsx("span", { children: note })] }))] }) }), jsx(ConfirmDialog, { open: open && step !== 'form', onOpenChange: (next) => !next && !saving && cancelWarning(), icon: TriangleAlert, title: warning?.title ?? '', description: warning?.description, cancelLabel: getWarningCancelLabel(step), confirmLabel: warning?.confirmLabel ?? SAVE_ANYWAY_LABEL, confirmVariant: "default", disabled: saving, onConfirm: confirmWarning, children: warning?.detail && jsx(
|
|
39428
|
+
}, children: [jsx(Field, { label: NAME_LABEL, required: true, error: error, input: jsx(Input, { id: inputId, "aria-label": NAME_LABEL, "aria-invalid": Boolean(error) || undefined, value: name, maxLength: SAVED_SEARCH_NAME_MAX_LENGTH, onChange: (event) => setName(event.target.value), autoFocus: true }) }), chips && (jsxs("div", { className: "flex flex-col gap-1", children: [jsx("span", { className: "text-[13px] text-label", children: FILTERS_LABEL }), jsx(SavedSearchChipList, { chips: chips, className: FILTERS_PANEL_CLASS_NAME })] })), note && (jsxs("p", { className: "flex items-start gap-1.5 text-xs text-muted-foreground", children: [jsx(Info, { "aria-hidden": "true", className: "mt-px size-3.5 shrink-0" }), jsx("span", { children: note })] }))] }) }), jsx(ConfirmDialog, { open: open && step !== 'form', onOpenChange: (next) => !next && !saving && cancelWarning(), icon: TriangleAlert, title: warning?.title ?? '', description: warning?.description, cancelLabel: getWarningCancelLabel(step), confirmLabel: warning?.confirmLabel ?? SAVE_ANYWAY_LABEL, confirmVariant: "default", disabled: saving, onConfirm: confirmWarning, children: warning?.detail && jsx(SavedSearchDetail, { ...warning.detail }) })] }));
|
|
39372
39429
|
}
|
|
39373
39430
|
|
|
39374
39431
|
/**
|
|
@@ -76952,16 +77009,17 @@ function getPopoverWidthClassName(field) {
|
|
|
76952
77009
|
}
|
|
76953
77010
|
const POPOVER_TILE_COLUMNS = 2;
|
|
76954
77011
|
/** Above this many options, chips / tiles give way to the searchable checkbox list. */
|
|
76955
|
-
const MAX_PILL_OPTIONS =
|
|
77012
|
+
const MAX_PILL_OPTIONS = 4;
|
|
76956
77013
|
/** The list layout actually used: the configured one, or the checkbox list once there are too many options for pills. */
|
|
76957
77014
|
function resolveListLayout(field) {
|
|
76958
77015
|
return (field.options?.length ?? 0) > MAX_PILL_OPTIONS
|
|
76959
77016
|
? 'list'
|
|
76960
77017
|
: (field.layout ?? 'chips');
|
|
76961
77018
|
}
|
|
76962
|
-
/**
|
|
76963
|
-
const
|
|
76964
|
-
|
|
77019
|
+
/** A card dropdown with nothing picked: `inputPlaceholder`, else `Any <label>` (single) or `Select <label>` (set `inputPlaceholder` for acronyms). */
|
|
77020
|
+
const getDropdownPlaceholder = (field) => field.inputPlaceholder ??
|
|
77021
|
+
`${field.single ? 'Any' : 'Select'} ${field.label.toLowerCase()}`;
|
|
77022
|
+
/** Popover variant: the same control and layout as the card (pills up to 4 options, then the list); tile grids drop to two columns. */
|
|
76965
77023
|
function toPopoverField(field) {
|
|
76966
77024
|
if (field.type === 'scale' ||
|
|
76967
77025
|
(field.type === 'list' && resolveListLayout(field) === 'tiles')) {
|
|
@@ -76983,21 +77041,17 @@ function getPopoverStatus(field, value) {
|
|
|
76983
77041
|
return describeFilterValue(field, value) || undefined;
|
|
76984
77042
|
}
|
|
76985
77043
|
|
|
76986
|
-
/** Single-choice list on a card, as the design's dropdown ("Any supplier"); the filter row lists the same options as a pick-one list. */
|
|
76987
|
-
function SingleSelectFilter({ field, selected, onSelect, }) {
|
|
76988
|
-
const options = field.options ?? [];
|
|
76989
|
-
return (jsx(SearchableSelect, { options: options, selectedItem: options.find((option) => option.value === selected) ?? null, onChange: (option) => onSelect(option.value), getOptionKey: (option) => option.value, getOptionLabel: (option) => option.label, searchPlaceholder: getSingleSelectPlaceholder(field), hideTriggerSearchIcon: true }));
|
|
76990
|
-
}
|
|
76991
77044
|
/** The control alone — identical in a filter-row popover and on a card, except the list layout. Compose it into custom cards. */
|
|
76992
77045
|
function FilterFieldControl({ field, value, onChange, fiscalYearStartMonth, fill, inPopover, }) {
|
|
76993
77046
|
const ariaLabel = `${field.label} filter`;
|
|
76994
77047
|
switch (field.type) {
|
|
76995
77048
|
case 'list': {
|
|
76996
|
-
if (field.single && !inPopover) {
|
|
76997
|
-
return (jsx(SingleSelectFilter, { field: field, selected: valueOfType(value, 'list')?.values[0], onSelect: (next) => onChange(toListValue([next])) }));
|
|
76998
|
-
}
|
|
76999
77049
|
const layout = resolveListLayout(field);
|
|
77000
|
-
|
|
77050
|
+
// On a card a long list is a compact dropdown with its picks as pills; the filter row's popover already is one.
|
|
77051
|
+
if (!inPopover && layout === 'list') {
|
|
77052
|
+
return (jsx(PickListDropdown, { ariaLabel: ariaLabel, options: field.options ?? [], value: valueOfType(value, 'list')?.values ?? [], onChange: (values) => onChange(toListValue(values)), placeholder: getDropdownPlaceholder(field), searchPlaceholder: field.searchPlaceholder, single: field.single }));
|
|
77053
|
+
}
|
|
77054
|
+
return (jsx(PickListFilter, { ariaLabel: ariaLabel, options: field.options ?? [], value: valueOfType(value, 'list')?.values ?? [], onChange: (values) => onChange(toListValue(values)), layout: layout, searchPlaceholder: layout === 'list' ? field.searchPlaceholder : undefined, columns: field.columns, single: field.single }));
|
|
77001
77055
|
}
|
|
77002
77056
|
case 'scale':
|
|
77003
77057
|
return (jsx(ScaleRangeFilter, { ariaLabel: ariaLabel, options: field.options ?? [], value: valueOfType(value, 'scale')?.range, onChange: (range) => onChange(toScaleValue(range)), columns: field.columns, fill: fill }));
|
|
@@ -77010,9 +77064,9 @@ function FilterFieldControl({ field, value, onChange, fiscalYearStartMonth, fill
|
|
|
77010
77064
|
case 'contains':
|
|
77011
77065
|
return (jsx(ContainsFilter, { ariaLabel: ariaLabel, size: "field", placeholder: field.inputPlaceholder, value: valueOfType(value, 'contains')?.text ?? '', onChange: (text) => onChange(toContainsValue(text)) }));
|
|
77012
77066
|
case 'date':
|
|
77013
|
-
return (jsx(DateRangeFilter, { ariaLabel: ariaLabel, value: valueOfType(value, 'date')?.range, onChange: (range) => onChange(toDateValue(range)), fiscalYearStartMonth: fiscalYearStartMonth }));
|
|
77067
|
+
return (jsx(DateRangeFilter, { ariaLabel: ariaLabel, value: valueOfType(value, 'date')?.range, onChange: (range) => onChange(toDateValue(range)), fiscalYearStartMonth: fiscalYearStartMonth, compact: !inPopover }));
|
|
77014
77068
|
case 'bool':
|
|
77015
|
-
return (jsx(TriStateFilter, { ariaLabel: ariaLabel, value: valueOfType(value, 'bool')?.value ?? 'any', onChange: (next) => onChange(toBoolValue(next)) }));
|
|
77069
|
+
return (jsx(TriStateFilter, { ariaLabel: ariaLabel, value: valueOfType(value, 'bool')?.value ?? 'any', onChange: (next) => onChange(toBoolValue(next)), compact: !inPopover }));
|
|
77016
77070
|
}
|
|
77017
77071
|
}
|
|
77018
77072
|
/** Filter-row cell: inline input for number/contains, otherwise a trigger that opens the control in a popover. */
|
|
@@ -77057,16 +77111,12 @@ const RETRY_LABEL = 'Try again';
|
|
|
77057
77111
|
/** Placeholder cards while the filters load; roughly the first rows of a card grid. */
|
|
77058
77112
|
const SKELETON_CARD_COUNT = 8;
|
|
77059
77113
|
const SKELETON_GRID_CLASS_NAME = 'grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4';
|
|
77060
|
-
|
|
77061
|
-
const
|
|
77062
|
-
/**
|
|
77063
|
-
|
|
77064
|
-
|
|
77065
|
-
|
|
77066
|
-
/** Shown in place of the cards when the filters couldn't load. */
|
|
77067
|
-
function FilterSurfaceError({ message, onRetry, }) {
|
|
77068
|
-
return (jsxs("div", { role: "alert", className: "flex flex-col items-center rounded-xl border border-border bg-card px-6 py-12 text-center", children: [jsx("span", { className: "flex size-10 items-center justify-center rounded-full bg-destructive/10 text-destructive", children: jsx(TriangleAlert, { "aria-hidden": "true", className: "size-5" }) }), jsx("p", { className: "mt-4 text-base font-semibold text-foreground", children: ERROR_TITLE }), jsx("p", { className: "mt-1.5 max-w-sm text-sm text-muted-foreground", children: message }), onRetry && (jsx(Button, { appearance: "outline", color: "secondary", size: "md", leftIcon: jsx(RotateCcw, {}), onClick: onRetry, className: "mt-5", children: RETRY_LABEL }))] }));
|
|
77069
|
-
}
|
|
77114
|
+
/** Masonry grid rows are this tall; a card spans as many as its height needs. */
|
|
77115
|
+
const MASONRY_ROW_PX = 4;
|
|
77116
|
+
/** Rows a card of `height` px (including its bottom gap) spans; 0 until measured. */
|
|
77117
|
+
const getMasonrySpan = (height) => height > 0 ? Math.ceil(height / MASONRY_ROW_PX) : 0;
|
|
77118
|
+
/** Dense 4px rows; the vertical gap comes from each item's bottom padding. */
|
|
77119
|
+
const MASONRY_GRID_CLASS_NAME = 'grid-flow-row-dense auto-rows-[4px] gap-x-3';
|
|
77070
77120
|
|
|
77071
77121
|
const NESTED_LAYER_SELECTOR = '[data-slot="popover-content"], [role="dialog"], [role="menu"], [role="listbox"]';
|
|
77072
77122
|
function useCloseOnEscape(onClose) {
|
|
@@ -77088,14 +77138,49 @@ function useFilterSurfaceMore(defaultExpanded = false) {
|
|
|
77088
77138
|
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
77089
77139
|
return { expanded, toggle: () => setExpanded((current) => !current) };
|
|
77090
77140
|
}
|
|
77141
|
+
/** Measures a masonry card and keeps its row span in step with its height (content opening, wrapping…). */
|
|
77142
|
+
function useMasonrySpan() {
|
|
77143
|
+
const [node, setNode] = useState(null);
|
|
77144
|
+
const [span, setSpan] = useState(0);
|
|
77145
|
+
useEffect(() => {
|
|
77146
|
+
if (!node)
|
|
77147
|
+
return;
|
|
77148
|
+
const measure = () => {
|
|
77149
|
+
const next = getMasonrySpan(node.offsetHeight);
|
|
77150
|
+
setSpan((current) => (current === next ? current : next));
|
|
77151
|
+
};
|
|
77152
|
+
measure();
|
|
77153
|
+
const observer = new ResizeObserver(measure);
|
|
77154
|
+
observer.observe(node);
|
|
77155
|
+
return () => observer.disconnect();
|
|
77156
|
+
}, [node]);
|
|
77157
|
+
return { ref: useCallback((next) => setNode(next), []), span };
|
|
77158
|
+
}
|
|
77159
|
+
|
|
77160
|
+
const DEFAULT_SKELETON_CARDS = Array.from({ length: SKELETON_CARD_COUNT }, (_, index) => `card-${index}`);
|
|
77161
|
+
/** Loading placeholder: the page's own skeleton, or generic list cards. */
|
|
77162
|
+
function FilterSurfaceSkeleton({ children, }) {
|
|
77163
|
+
return (jsx("div", { role: "status", "aria-label": LOADING_LABEL, children: children ?? (jsx("div", { className: SKELETON_GRID_CLASS_NAME, children: DEFAULT_SKELETON_CARDS.map((id) => (jsx(FilterCardSkeleton, {}, id))) })) }));
|
|
77164
|
+
}
|
|
77165
|
+
/** Shown in place of the cards when the filters couldn't load. */
|
|
77166
|
+
function FilterSurfaceError({ message, onRetry, }) {
|
|
77167
|
+
return (jsxs("div", { role: "alert", className: "flex flex-col items-center rounded-xl border border-border bg-card px-6 py-12 text-center", children: [jsx("span", { className: "flex size-10 items-center justify-center rounded-full bg-destructive/10 text-destructive", children: jsx(TriangleAlert, { "aria-hidden": "true", className: "size-5" }) }), jsx("p", { className: "mt-4 text-base font-semibold text-foreground", children: ERROR_TITLE }), jsx("p", { className: "mt-1.5 max-w-sm text-sm text-muted-foreground", children: message }), onRetry && (jsx(Button, { appearance: "outline", color: "secondary", size: "md", leftIcon: jsx(RotateCcw, {}), onClick: onRetry, className: "mt-5", children: RETRY_LABEL }))] }));
|
|
77168
|
+
}
|
|
77169
|
+
/** One masonry cell: spans the rows its card needs; the bottom padding is the gap to the next card. */
|
|
77170
|
+
function MasonryItem({ children }) {
|
|
77171
|
+
const { ref, span } = useMasonrySpan();
|
|
77172
|
+
return (jsx("div", { style: span > 0 ? { gridRowEnd: `span ${span}` } : undefined, children: jsx("div", { ref: ref, className: "pb-3", children: children }) }));
|
|
77173
|
+
}
|
|
77091
77174
|
|
|
77092
77175
|
/** Full-page filter screen (e.g. an advanced search): header, scrolling card grid, sticky apply footer. */
|
|
77093
77176
|
function FilterSurface({ title, headerActions, footerChips, footerActions, onClose, status = 'ready', errorMessage, onRetry, skeleton, children, }) {
|
|
77094
77177
|
useCloseOnEscape(onClose);
|
|
77095
77178
|
return (jsxs("div", { role: "region", "aria-label": title, className: "flex h-full min-h-0 flex-col bg-background", children: [jsxs("header", { className: "sticky top-0 z-20 flex shrink-0 flex-wrap items-end justify-between gap-3 bg-background px-3 pt-2 pb-3", children: [jsx("div", { className: "flex flex-col gap-1", children: jsxs("div", { className: "flex items-center gap-2", children: [jsx(IconButton, { appearance: "ghost", size: "sm", "aria-label": BACK_LABEL, title: BACK_LABEL, onClick: onClose, children: jsx(ArrowLeft, {}) }), jsx("h1", { className: "text-xl font-semibold text-foreground", children: title })] }) }), jsxs("div", { className: "flex items-center gap-2", children: [headerActions, jsx(IconButton, { appearance: "outline", size: "md", "aria-label": CLOSE_LABEL, title: CLOSE_LABEL, onClick: onClose, children: jsx(X$2, {}) })] })] }), jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-3 pb-3", children: status === 'loading' ? (jsx(FilterSurfaceSkeleton, { children: skeleton })) : status === 'error' ? (jsx(FilterSurfaceError, { message: errorMessage ?? DEFAULT_ERROR_MESSAGE, onRetry: onRetry })) : (jsx("div", { className: "flex flex-col gap-3", children: children })) }), status === 'ready' && (footerChips || footerActions) && (jsxs("footer", { className: "sticky bottom-0 z-20 flex flex-wrap items-center justify-between gap-3 border-t border-border bg-card px-3 py-2", children: [jsx("div", { className: "min-w-0 flex-1", children: footerChips }), jsx("div", { className: "flex shrink-0 items-center gap-2", children: footerActions })] }))] }));
|
|
77096
77179
|
}
|
|
77097
|
-
function FilterSurfaceGroup({ title, className, children, }) {
|
|
77098
|
-
return (jsxs("section", { className: "flex flex-col gap-3", children: [title && (jsx("h2", { className: "text-xs font-semibold uppercase tracking-wide text-label", children: title })), jsx("div", { className: cn('grid grid-cols-1
|
|
77180
|
+
function FilterSurfaceGroup({ title, masonry = false, className, children, }) {
|
|
77181
|
+
return (jsxs("section", { className: "flex flex-col gap-3", children: [title && (jsx("h2", { className: "text-xs font-semibold uppercase tracking-wide text-label", children: title })), jsx("div", { className: cn('grid grid-cols-1 items-start lg:grid-cols-6', masonry ? MASONRY_GRID_CLASS_NAME : 'gap-3', className), children: masonry
|
|
77182
|
+
? Children.map(children, (child) => child ? jsx(MasonryItem, { children: child }) : null)
|
|
77183
|
+
: children })] }));
|
|
77099
77184
|
}
|
|
77100
77185
|
/** Collapsed "More filters" divider that reveals extra cards. */
|
|
77101
77186
|
function FilterSurfaceMore({ label, defaultExpanded, children, }) {
|
|
@@ -79676,5 +79761,5 @@ var RichTextAreaImpl = /*#__PURE__*/Object.freeze({
|
|
|
79676
79761
|
RichTextarea: RichTextarea
|
|
79677
79762
|
});
|
|
79678
79763
|
|
|
79679
|
-
export { ACTIVITY_CATEGORIES, ACTIVITY_CATEGORY_META, ActionBadge, ActionButtonWithTooltip, ActiveFilterChips, ActivityFeed, AdvancedSearchDrawer, Alert, ApiComboBox, AppSidebar, AuditEntryCard, AuditHistoryPanel, AuditTimeline, AuditTimelineShimmer, Badge, BadgeType, BusinessContactStatusFilter, Button, CUSTOMER_AUDIT_CONFIG, CertificateRowList, ChangePill, Collapsible, CollapsibleCard, CollapsibleContent, CollapsibleTrigger, CompanyCard, ConfirmDialog, ContactTypeFilter, ContainsFilter, CopyButton, CountrySelect, CreateFormPageHeader, CreateFormPageShell, CurrencyDisplayTabs, CurrencyInput, CurrencyToggle, CustomCombobox, CustomFieldInput, CustomFieldValue, CustomTable, CustomizeColumnsDrawer, DATE_PRESET_OPTIONS, DIAMOND_AUDIT_CONFIG, DashboardShell, DataEditableTable, DataTable, DateInput, DateRangeFilter, DetailChip, DetailPageLayout, DetailPageSkeleton, DetailSection, DocumentDeleteDialog, DocumentDetailSkeleton, DocumentHero, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyValue, EntityHeader, FILTER_COUNT_BADGE_CLASS_NAME, Field, FieldSkeleton, FileUploader, FilterCardSkeleton, FilterCellPopover, FilterEmptyState, FilterField, FilterFieldCard, FilterFieldCell, FilterFieldControl, FilterMultiSelect, FilterPanel, FilterSelect, FilterSurface, FilterSurfaceGroup, FilterSurfaceMore, FormSectionCard, FormSectionCardSkeleton, GEM_AUDIT_CONFIG, HistoryEntryCard, HistoryTimeline, HistoryTimelineShimmer, INVOICE_AUDIT_CONFIG, IconButton, IdHyperlinkText, IdListInput, InAppNotificationCard, InAppNotificationsPanel, InfoBanner, InventoryComboBox, InventoryItemCard, InventoryItemRow, InvoiceComboBox, KYCStatusFilter, KeyValueGrid, LAYOUT_AUDIT_CONFIG, LabeledToggle, LineItemsCard, LineProductCell, LineProductThumb, LineProductTypeChip, LineRowActionsMenu, LineTable, LineTableEmpty, MEMO_IN_AUDIT_CONFIG, MEMO_OUT_AUDIT_CONFIG, MediaGallery, MemoInComboBox, MetricCard, MetricRow, MetricTile, Modal, MoreInfoPanel, MoreInfoTrigger, NOT_CAPTURED, Navbar, NavbarActions, NavbarAvatar, NavbarIconButton, NavbarSearch, NavbarTitle, NumberExpressionFilter, NumberInput, NumberRangeInputs, NumberedStepCard, OtpInput, OwnershipFilter, OwnershipSplitCard, PROFORMA_AUDIT_CONFIG, PageHeader, PageTabs, Pagination, PaginationSelect, PartnerShareCard, PickListFilter, PriceInput, Protected, PurchaseOrderComboBox, QUOTATION_AUDIT_CONFIG, RadioCardGroup, ReadMoreText, RefreshButton, RichTextarea$1 as RichTextarea, SAVED_SEARCH_NAME_MAX_LENGTH, SEARCHABLE_SELECT_PAGE_SIZE, SafeImage, SaveSearchDialog, SavedSearchChipList, SavedSearchesMenu, ScaleRangeFilter, ScrollFade, SearchBox, SearchInput, SearchableSelect, SectionCard, SectionIcon, SegmentedControl, ShareDialog, ShareMenu, ShimmerTable, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SlideOut, SlideOutClose, SlideOutContent, SlideOutDescription, SlideOutHeader, SlideOutOverlay, SlideOutPortal, SlideOutTitle, SlideOutTrigger, Slider, SpecList, Spinner, SquareSkeleton, StateSelect, StatusBadge$1 as StatusBadge, StatusFilter, Stepper, StockTypeFilter, TablePagination, TableRowsSkeleton, TableSkeleton, Tabs, TabsContent, TabsList, TabsTrigger, TagsInput, TextHyperlink, TimePicker, ToggleSwitch, TriStateFilter, TruncateTooltip, TruncatedLabel, WarehouseComboBox, WarehouseFilter, ZoomSplitViewer, asList, asText, badgeVariants, buildBulkShareMessage, buildBulkShareTitle, buildInventoryPublicUrl, buildInventoryRowProps, buildInventoryShareTitle, buildNumberRuleHelp, buildWhatsappUrl, buttonVariants, cn, countries, countryByCode, createVideoMediaItem, describeFilterValue, dropdownMenuItemDestructiveClass, expandScaleRange, fieldTypeIcon, filterSavedSearches, findRegion, flagEmoji, formatAddressLocation, formatDateRange, formatFileSize, formatNumberRange, formatScaleRange, formatSelectedCount, generateActionDescription, generateTitle, getBadgeTypeForStatus, getChangePills, getCountryName, getInitials, getStatesOfCountry, iconButtonVariants, inAppNotificationCardA11y, inAppNotificationsPanelA11y, isBlank, isDuplicateSearchName, isFilterValueActive, isIndiaCountry, isKnownRegion, isPathActive, isValidAlpha2CountryCode, isValidSearchName, isValueEmpty, notify, parseNumberExpression, releaseStaleOverlayBodyLock, renderBadge, resolveCountryCode, resolveDatePreset, resolveDateRange, splitIdList, toAddressFormValues, toBoolValue, toContainsValue, toDateValue, toIdsValue, toListValue, toNumberValue, toScaleValue, useAuditShowMore, useBadge, useButton, useDropdownMenu, useInAppNotificationsPanel, useNavbar, useSidebar, valueOfType, videoThumbnailSrc, watchOverlayBodyLock };
|
|
79764
|
+
export { ACTIVITY_CATEGORIES, ACTIVITY_CATEGORY_META, ActionBadge, ActionButtonWithTooltip, ActiveFilterChips, ActivityFeed, AdvancedSearchDrawer, Alert, ApiComboBox, AppSidebar, AuditEntryCard, AuditHistoryPanel, AuditTimeline, AuditTimelineShimmer, Badge, BadgeType, BusinessContactStatusFilter, Button, CUSTOMER_AUDIT_CONFIG, CertificateRowList, ChangePill, Collapsible, CollapsibleCard, CollapsibleContent, CollapsibleTrigger, CompanyCard, ConfirmDialog, ContactTypeFilter, ContainsFilter, CopyButton, CountrySelect, CreateFormPageHeader, CreateFormPageShell, CurrencyDisplayTabs, CurrencyInput, CurrencyToggle, CustomCombobox, CustomFieldInput, CustomFieldValue, CustomTable, CustomizeColumnsDrawer, DATE_PRESET_OPTIONS, DIAMOND_AUDIT_CONFIG, DashboardShell, DataEditableTable, DataTable, DateInput, DateRangeFilter, DetailChip, DetailPageLayout, DetailPageSkeleton, DetailSection, DocumentDeleteDialog, DocumentDetailSkeleton, DocumentHero, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyValue, EntityHeader, FILTER_COUNT_BADGE_CLASS_NAME, Field, FieldSkeleton, FileUploader, FilterCardSkeleton, FilterCellPopover, FilterEmptyState, FilterField, FilterFieldCard, FilterFieldCell, FilterFieldControl, FilterMultiSelect, FilterPanel, FilterSelect, FilterSurface, FilterSurfaceGroup, FilterSurfaceMore, FormSectionCard, FormSectionCardSkeleton, GEM_AUDIT_CONFIG, HistoryEntryCard, HistoryTimeline, HistoryTimelineShimmer, INVOICE_AUDIT_CONFIG, IconButton, IdHyperlinkText, IdListInput, InAppNotificationCard, InAppNotificationsPanel, InfoBanner, InventoryComboBox, InventoryItemCard, InventoryItemRow, InvoiceComboBox, KYCStatusFilter, KeyValueGrid, LAYOUT_AUDIT_CONFIG, LabeledToggle, LineItemsCard, LineProductCell, LineProductThumb, LineProductTypeChip, LineRowActionsMenu, LineTable, LineTableEmpty, MEMO_IN_AUDIT_CONFIG, MEMO_OUT_AUDIT_CONFIG, MediaGallery, MemoInComboBox, MetricCard, MetricRow, MetricTile, Modal, MoreInfoPanel, MoreInfoTrigger, NOT_CAPTURED, Navbar, NavbarActions, NavbarAvatar, NavbarIconButton, NavbarSearch, NavbarTitle, NumberExpressionFilter, NumberInput, NumberRangeInputs, NumberedStepCard, OtpInput, OwnershipFilter, OwnershipSplitCard, PROFORMA_AUDIT_CONFIG, PageHeader, PageTabs, Pagination, PaginationSelect, PartnerShareCard, PickListDropdown, PickListFilter, PriceInput, Protected, PurchaseOrderComboBox, QUOTATION_AUDIT_CONFIG, RadioCardGroup, ReadMoreText, RefreshButton, RichTextarea$1 as RichTextarea, SAVED_SEARCH_NAME_MAX_LENGTH, SEARCHABLE_SELECT_PAGE_SIZE, SafeImage, SaveSearchDialog, SavedSearchChipList, SavedSearchDetail, SavedSearchesMenu, ScaleRangeFilter, ScrollFade, SearchBox, SearchInput, SearchableSelect, SectionCard, SectionIcon, SegmentedControl, ShareDialog, ShareMenu, ShimmerTable, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SlideOut, SlideOutClose, SlideOutContent, SlideOutDescription, SlideOutHeader, SlideOutOverlay, SlideOutPortal, SlideOutTitle, SlideOutTrigger, Slider, SpecList, Spinner, SquareSkeleton, StateSelect, StatusBadge$1 as StatusBadge, StatusFilter, Stepper, StockTypeFilter, TablePagination, TableRowsSkeleton, TableSkeleton, Tabs, TabsContent, TabsList, TabsTrigger, TagsInput, TextHyperlink, TimePicker, ToggleSwitch, TriStateFilter, TruncateTooltip, TruncatedLabel, WarehouseComboBox, WarehouseFilter, ZoomSplitViewer, asList, asText, badgeVariants, buildBulkShareMessage, buildBulkShareTitle, buildInventoryPublicUrl, buildInventoryRowProps, buildInventoryShareTitle, buildNumberRuleHelp, buildWhatsappUrl, buttonVariants, cn, countries, countryByCode, createVideoMediaItem, describeFilterValue, dropdownMenuItemDestructiveClass, expandScaleRange, fieldTypeIcon, filterSavedSearches, findRegion, flagEmoji, formatAddressLocation, formatDateRange, formatFileSize, formatNumberRange, formatScaleRange, formatSelectedCount, generateActionDescription, generateTitle, getBadgeTypeForStatus, getChangePills, getCountryName, getInitials, getStatesOfCountry, iconButtonVariants, inAppNotificationCardA11y, inAppNotificationsPanelA11y, isBlank, isDuplicateSearchName, isFilterValueActive, isIndiaCountry, isKnownRegion, isPathActive, isValidAlpha2CountryCode, isValidSearchName, isValueEmpty, notify, parseNumberExpression, releaseStaleOverlayBodyLock, renderBadge, resolveCountryCode, resolveDatePreset, resolveDateRange, splitIdList, toAddressFormValues, toBoolValue, toContainsValue, toDateValue, toIdsValue, toListValue, toNumberValue, toScaleValue, useAuditShowMore, useBadge, useButton, useDropdownMenu, useInAppNotificationsPanel, useNavbar, useSidebar, valueOfType, videoThumbnailSrc, watchOverlayBodyLock };
|
|
79680
79765
|
//# sourceMappingURL=index.esm.js.map
|