@wordrhyme/auto-crud 1.1.5 → 1.1.7

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/index.cjs CHANGED
@@ -59,8 +59,6 @@ let __pixpilot_formily_shadcn = require("@pixpilot/formily-shadcn");
59
59
  __pixpilot_formily_shadcn = __toESM(__pixpilot_formily_shadcn);
60
60
  let __formily_react = require("@formily/react");
61
61
  __formily_react = __toESM(__formily_react);
62
- let __pixpilot_shadcn_ui = require("@pixpilot/shadcn-ui");
63
- __pixpilot_shadcn_ui = __toESM(__pixpilot_shadcn_ui);
64
62
  let sonner = require("sonner");
65
63
  sonner = __toESM(sonner);
66
64
 
@@ -1459,6 +1457,7 @@ function DataTableFilterList({ table, debounceMs = DEBOUNCE_MS$2, throttleMs = T
1459
1457
  const descriptionId = react.useId();
1460
1458
  const [open, setOpen] = react.useState(false);
1461
1459
  const addButtonRef = react.useRef(null);
1460
+ const columnDefs = table.options.columns;
1462
1461
  const columns = react.useMemo(() => {
1463
1462
  return table.getAllColumns().filter((column) => {
1464
1463
  if (!column.getCanFilter()) return false;
@@ -1466,7 +1465,7 @@ function DataTableFilterList({ table, debounceMs = DEBOUNCE_MS$2, throttleMs = T
1466
1465
  if (modes && !modes.includes("advanced")) return false;
1467
1466
  return true;
1468
1467
  });
1469
- }, [table]);
1468
+ }, [table, columnDefs]);
1470
1469
  const [filters, setFilters] = useReadableFilters(columns, { debounceMs });
1471
1470
  const debouncedSetFilters = useDebouncedCallback(setFilters, debounceMs);
1472
1471
  const [joinOperator, setJoinOperator] = useQueryState(table.options.meta?.queryKeys?.joinOperator ?? "", parseAsStringEnum(["and", "or"]).withDefault("and"));
@@ -1944,7 +1943,7 @@ const DEBOUNCE_MS$1 = 300;
1944
1943
  const THROTTLE_MS$1 = 50;
1945
1944
  const FILTER_SHORTCUT_KEY = "f";
1946
1945
  const REMOVE_FILTER_SHORTCUTS = ["backspace", "delete"];
1947
- function getOptionCommandValue$2(option) {
1946
+ function getOptionCommandValue(option) {
1948
1947
  return [
1949
1948
  option.value,
1950
1949
  option.label,
@@ -1953,6 +1952,7 @@ function getOptionCommandValue$2(option) {
1953
1952
  }
1954
1953
  function DataTableFilterMenu({ table, debounceMs = DEBOUNCE_MS$1, throttleMs = THROTTLE_MS$1, shallow = true, disabled,...props }) {
1955
1954
  const id = react.useId();
1955
+ const columnDefs = table.options.columns;
1956
1956
  const columns = react.useMemo(() => {
1957
1957
  return table.getAllColumns().filter((column) => {
1958
1958
  if (!column.columnDef.enableColumnFilter) return false;
@@ -1960,7 +1960,7 @@ function DataTableFilterMenu({ table, debounceMs = DEBOUNCE_MS$1, throttleMs = T
1960
1960
  if (modes && !modes.includes("command")) return false;
1961
1961
  return true;
1962
1962
  });
1963
- }, [table]);
1963
+ }, [table, columnDefs]);
1964
1964
  const [open, setOpen] = react.useState(false);
1965
1965
  const [selectedColumn, setSelectedColumn] = react.useState(null);
1966
1966
  const [inputValue, setInputValue] = react.useState("");
@@ -2229,7 +2229,7 @@ function FilterValueSelector({ column, value, onSelect }) {
2229
2229
  })] });
2230
2230
  case "select":
2231
2231
  case "multiSelect": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandGroup, { children: column.columnDef.meta?.options?.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandItem, {
2232
- value: getOptionCommandValue$2(option),
2232
+ value: getOptionCommandValue(option),
2233
2233
  onSelect: () => onSelect(option.value),
2234
2234
  children: [
2235
2235
  option.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(option.icon, {}),
@@ -2356,7 +2356,7 @@ function onFilterInputRender({ filter, column, inputId, onFilterUpdate, showValu
2356
2356
  align: "start",
2357
2357
  className: "w-48 p-0",
2358
2358
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Command, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandInput, { placeholder: "Search options..." }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandList, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandEmpty, { children: "No options found." }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandGroup, { children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandItem, {
2359
- value: getOptionCommandValue$2(option),
2359
+ value: getOptionCommandValue(option),
2360
2360
  onSelect: () => {
2361
2361
  const value = filter.variant === "multiSelect" ? selectedValues.includes(option.value) ? selectedValues.filter((v) => v !== option.value) : [...selectedValues, option.value] : option.value;
2362
2362
  onFilterUpdate(filter.filterId, { value });
@@ -2713,9 +2713,209 @@ function DataTableViewOptions({ table, disabled,...props }) {
2713
2713
  })] });
2714
2714
  }
2715
2715
 
2716
+ //#endregion
2717
+ //#region src/components/ui/multi-combobox.tsx
2718
+ const DEFAULT_OPTIONS = [];
2719
+ const SCROLLABLE_COMMAND_GROUP_STYLE = {
2720
+ maxHeight: "300px",
2721
+ overscrollBehavior: "contain",
2722
+ overflowX: "hidden",
2723
+ overflowY: "auto"
2724
+ };
2725
+ function getWheelDeltaY(event) {
2726
+ if (event.deltaMode === 1) return event.deltaY * 16;
2727
+ if (event.deltaMode === 2) return event.deltaY * event.currentTarget.clientHeight;
2728
+ return event.deltaY;
2729
+ }
2730
+ function handleScrollableCommandGroupWheel(event) {
2731
+ const target = event.currentTarget;
2732
+ if (target.scrollHeight <= target.clientHeight) return;
2733
+ const maxScrollTop = target.scrollHeight - target.clientHeight;
2734
+ const nextScrollTop = Math.max(0, Math.min(target.scrollTop + getWheelDeltaY(event), maxScrollTop));
2735
+ if (nextScrollTop === target.scrollTop) return;
2736
+ event.preventDefault();
2737
+ event.stopPropagation();
2738
+ target.scrollTop = nextScrollTop;
2739
+ }
2740
+ function collectSearchText(...parts) {
2741
+ const texts = [];
2742
+ for (const part of parts) {
2743
+ if (!part) continue;
2744
+ if (Array.isArray(part)) texts.push(...part.map(String));
2745
+ else if (typeof part === "object") texts.push(...Object.values(part).filter(Boolean).map(String));
2746
+ else texts.push(String(part));
2747
+ }
2748
+ return texts.join(" ");
2749
+ }
2750
+ function getOptionKeywords(option) {
2751
+ return collectSearchText(typeof option.label === "string" || typeof option.label === "number" ? option.label : void 0, option.searchText, option.keywords).split(/\s+/).filter(Boolean);
2752
+ }
2753
+ function getOptionText(option) {
2754
+ if (typeof option.label === "string" || typeof option.label === "number") return String(option.label);
2755
+ return option.value;
2756
+ }
2757
+ function createNextValue(currentValue, optionValue, isSelected, selectionMode) {
2758
+ if (selectionMode === "single") return isSelected ? [] : [optionValue];
2759
+ return isSelected ? currentValue.filter((value) => value !== optionValue) : [...currentValue, optionValue];
2760
+ }
2761
+ function DefaultMultiComboboxTrigger({ open, selectedOptions, selectedValues, clearSelection, disabled, readOnly, placeholder, selectedText, className,...buttonProps }) {
2762
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Button, {
2763
+ ...buttonProps,
2764
+ type: "button",
2765
+ variant: "outline",
2766
+ role: "combobox",
2767
+ "aria-expanded": open,
2768
+ disabled,
2769
+ className: (0, __pixpilot_shadcn.cn)("w-full justify-between", className),
2770
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2771
+ className: "flex min-w-0 flex-1 items-center gap-1 overflow-hidden",
2772
+ children: selectedValues.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2773
+ className: "truncate text-muted-foreground",
2774
+ children: placeholder
2775
+ }) : selectedOptions.length <= 2 ? selectedOptions.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
2776
+ variant: "secondary",
2777
+ className: "max-w-[8rem] rounded-sm px-1 font-normal",
2778
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2779
+ className: "truncate",
2780
+ children: option.label
2781
+ })
2782
+ }, option.value)) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
2783
+ variant: "secondary",
2784
+ className: "rounded-sm px-1 font-normal",
2785
+ children: [
2786
+ selectedValues.length,
2787
+ " ",
2788
+ selectedText
2789
+ ]
2790
+ })
2791
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2792
+ className: "ml-2 flex shrink-0 items-center gap-1",
2793
+ children: [selectedValues.length > 0 && !readOnly && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2794
+ role: "button",
2795
+ "aria-label": "Clear selection",
2796
+ tabIndex: 0,
2797
+ className: "rounded-sm px-1 text-muted-foreground opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
2798
+ onClick: clearSelection,
2799
+ onKeyDown: (event) => {
2800
+ if (event.key === "Enter" || event.key === " ") {
2801
+ event.preventDefault();
2802
+ clearSelection(event);
2803
+ }
2804
+ },
2805
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "size-3" })
2806
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronsUpDown, { className: "h-4 w-4 opacity-50" })]
2807
+ })]
2808
+ });
2809
+ }
2810
+ const MultiCombobox = ({ value, defaultValue, onChange, options = DEFAULT_OPTIONS, placeholder = "Select options...", searchPlaceholder = "Search...", emptyText = "No options found.", clearText = "Clear selection", selectedText = "selected", selectionMode = "multiple", disabled, readOnly, className, contentClassName, triggerClassName, matchTriggerWidth = true, renderTrigger, filter,...commandProps }) => {
2811
+ const [open, setOpen] = react.useState(false);
2812
+ const [internalValue, setInternalValue] = react.useState(defaultValue ?? []);
2813
+ const selectedValues = value ?? internalValue;
2814
+ const selectedValueSet = react.useMemo(() => new Set(selectedValues), [selectedValues]);
2815
+ const selectedOptions = react.useMemo(() => options.filter((option) => selectedValueSet.has(option.value)), [options, selectedValueSet]);
2816
+ const commitChange = react.useCallback((nextValue) => {
2817
+ if (value === void 0) setInternalValue(nextValue);
2818
+ onChange?.(nextValue);
2819
+ }, [onChange, value]);
2820
+ const clearSelection = react.useCallback((event) => {
2821
+ event?.preventDefault();
2822
+ event?.stopPropagation();
2823
+ if (readOnly) return;
2824
+ commitChange([]);
2825
+ }, [commitChange, readOnly]);
2826
+ const onItemSelect = react.useCallback((option) => {
2827
+ if (option.disabled || readOnly) return;
2828
+ const isSelected = selectedValueSet.has(option.value);
2829
+ commitChange(createNextValue(selectedValues, option.value, isSelected, selectionMode));
2830
+ if (selectionMode === "single") setOpen(false);
2831
+ }, [
2832
+ commitChange,
2833
+ readOnly,
2834
+ selectedValues,
2835
+ selectedValueSet,
2836
+ selectionMode
2837
+ ]);
2838
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Popover, {
2839
+ open,
2840
+ onOpenChange: setOpen,
2841
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.PopoverTrigger, {
2842
+ asChild: true,
2843
+ children: renderTrigger ? renderTrigger({
2844
+ open,
2845
+ selectedOptions,
2846
+ selectedValues,
2847
+ clearSelection,
2848
+ disabled,
2849
+ readOnly
2850
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultMultiComboboxTrigger, {
2851
+ open,
2852
+ selectedOptions,
2853
+ selectedValues,
2854
+ clearSelection,
2855
+ disabled,
2856
+ readOnly,
2857
+ placeholder,
2858
+ selectedText,
2859
+ className: triggerClassName
2860
+ })
2861
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.PopoverContent, {
2862
+ className: (0, __pixpilot_shadcn.cn)("w-full p-0", contentClassName),
2863
+ style: matchTriggerWidth ? { width: "var(--radix-popover-trigger-width)" } : void 0,
2864
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Command, {
2865
+ className,
2866
+ filter,
2867
+ ...commandProps,
2868
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandInput, { placeholder: searchPlaceholder }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandList, {
2869
+ className: "max-h-full",
2870
+ children: [
2871
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandEmpty, { children: emptyText }),
2872
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandGroup, {
2873
+ className: "max-h-[300px] scroll-py-1 overflow-y-auto overflow-x-hidden",
2874
+ onWheelCapture: handleScrollableCommandGroupWheel,
2875
+ style: SCROLLABLE_COMMAND_GROUP_STYLE,
2876
+ children: options.map((option) => {
2877
+ const isSelected = selectedValueSet.has(option.value);
2878
+ const Icon = option.icon;
2879
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandItem, {
2880
+ value: option.value,
2881
+ keywords: getOptionKeywords(option),
2882
+ disabled: option.disabled,
2883
+ onSelect: () => onItemSelect(option),
2884
+ children: [
2885
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2886
+ className: (0, __pixpilot_shadcn.cn)("flex size-4 items-center justify-center rounded-sm border border-primary", isSelected ? "bg-primary text-primary-foreground" : "opacity-50 [&_svg]:invisible"),
2887
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {})
2888
+ }),
2889
+ Icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { className: "size-4" }),
2890
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2891
+ className: "truncate",
2892
+ children: getOptionText(option)
2893
+ }),
2894
+ option.count !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2895
+ className: "ml-auto font-mono text-xs",
2896
+ children: option.count
2897
+ })
2898
+ ]
2899
+ }, option.value);
2900
+ })
2901
+ }),
2902
+ selectedValues.length > 0 && !readOnly && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandSeparator, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandGroup, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandItem, {
2903
+ onSelect: () => commitChange([]),
2904
+ className: "justify-center text-center",
2905
+ children: clearText
2906
+ }) })] })
2907
+ ]
2908
+ })]
2909
+ })
2910
+ })]
2911
+ });
2912
+ };
2913
+ MultiCombobox.displayName = "MultiCombobox";
2914
+
2716
2915
  //#endregion
2717
2916
  //#region src/components/auto-crud/auto-table-simple-filters.tsx
2718
2917
  function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange, leading }) {
2918
+ const columnDefs = table.options.columns;
2719
2919
  const columns = react.useMemo(() => table.getAllColumns().filter((col) => col.getCanFilter()).sort((a, b) => {
2720
2920
  const readOrder = (column) => {
2721
2921
  const metaIndex = column.columnDef.meta?.index;
@@ -2725,7 +2925,7 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
2725
2925
  const orderDiff = readOrder(a) - readOrder(b);
2726
2926
  if (orderDiff !== 0) return orderDiff;
2727
2927
  return a.getIndex() - b.getIndex();
2728
- }), [table]);
2928
+ }), [table, columnDefs]);
2729
2929
  const queryStateOptions = table.options.meta?.queryStateOptions;
2730
2930
  const [queryFilters, setQueryFilters] = useReadableFilters(columns, {
2731
2931
  ...queryStateOptions,
@@ -2959,120 +3159,60 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
2959
3159
  ]
2960
3160
  });
2961
3161
  }
2962
- function getOptionCommandValue$1(option) {
2963
- return [
2964
- option.value,
2965
- option.label,
2966
- option.searchText
2967
- ].filter(Boolean).join(" ");
2968
- }
2969
3162
  function SimpleFacetedFilter({ title, options, multiple, value, onChange }) {
2970
- const [open, setOpen] = react.useState(false);
2971
- const selectedValues = react.useMemo(() => new Set(value), [value]);
2972
- const onItemSelect = (option, isSelected) => {
2973
- if (multiple) {
2974
- const newValues = new Set(selectedValues);
2975
- if (isSelected) newValues.delete(option.value);
2976
- else newValues.add(option.value);
2977
- onChange(Array.from(newValues));
2978
- } else {
2979
- onChange(isSelected ? [] : [option.value]);
2980
- setOpen(false);
2981
- }
2982
- };
2983
- const onReset = (e) => {
2984
- e?.stopPropagation();
2985
- onChange([]);
2986
- };
2987
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Popover, {
2988
- open,
2989
- onOpenChange: setOpen,
2990
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.PopoverTrigger, {
2991
- asChild: true,
2992
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Button, {
2993
- variant: "outline",
2994
- size: "sm",
2995
- className: "border-dashed font-normal",
2996
- children: [
2997
- selectedValues.size > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2998
- role: "button",
2999
- tabIndex: 0,
3000
- className: "rounded-sm opacity-70 hover:opacity-100",
3001
- onClick: onReset,
3002
- onKeyDown: (e) => {
3003
- if (e.key === "Enter" || e.key === " ") {
3004
- e.preventDefault();
3005
- onReset(e);
3006
- }
3007
- },
3008
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
3009
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PlusCircle, {}),
3010
- title,
3011
- selectedValues.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3012
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Separator, {
3013
- orientation: "vertical",
3014
- className: "mx-0.5 h-4"
3015
- }),
3016
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
3017
- variant: "secondary",
3018
- className: "rounded-sm px-1 font-normal lg:hidden",
3019
- children: selectedValues.size
3020
- }),
3021
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3022
- className: "hidden items-center gap-1 lg:flex",
3023
- children: selectedValues.size > 2 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
3024
- variant: "secondary",
3025
- className: "rounded-sm px-1 font-normal",
3026
- children: [selectedValues.size, " selected"]
3027
- }) : options.filter((o) => selectedValues.has(o.value)).map((o) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
3028
- variant: "secondary",
3029
- className: "rounded-sm px-1 font-normal",
3030
- children: o.label
3031
- }, o.value))
3032
- })
3033
- ] })
3034
- ]
3035
- })
3036
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.PopoverContent, {
3037
- className: "w-50 p-0",
3038
- align: "start",
3039
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Command, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandInput, { placeholder: title }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandList, {
3040
- className: "max-h-full",
3041
- children: [
3042
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandEmpty, { children: "No results found." }),
3043
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandGroup, {
3044
- className: "max-h-[300px] overflow-y-auto",
3045
- children: options.map((option) => {
3046
- const isSelected = selectedValues.has(option.value);
3047
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandItem, {
3048
- value: getOptionCommandValue$1(option),
3049
- onSelect: () => onItemSelect(option, isSelected),
3050
- children: [
3051
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3052
- className: cn("flex size-4 items-center justify-center rounded-sm border border-primary", isSelected ? "bg-primary" : "opacity-50 [&_svg]:invisible"),
3053
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {})
3054
- }),
3055
- option.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(option.icon, {}),
3056
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3057
- className: "truncate",
3058
- children: option.label
3059
- }),
3060
- option.count && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3061
- className: "ml-auto font-mono text-xs",
3062
- children: option.count
3063
- })
3064
- ]
3065
- }, option.value);
3066
- })
3163
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiCombobox, {
3164
+ value,
3165
+ onChange,
3166
+ options,
3167
+ selectionMode: multiple ? "multiple" : "single",
3168
+ searchPlaceholder: title,
3169
+ contentClassName: "w-50",
3170
+ matchTriggerWidth: false,
3171
+ clearText: "Clear filters",
3172
+ renderTrigger: ({ selectedOptions, selectedValues, clearSelection }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Button, {
3173
+ variant: "outline",
3174
+ size: "sm",
3175
+ className: "border-dashed font-normal",
3176
+ children: [
3177
+ selectedValues.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3178
+ role: "button",
3179
+ tabIndex: 0,
3180
+ className: "rounded-sm opacity-70 hover:opacity-100",
3181
+ onClick: clearSelection,
3182
+ onKeyDown: (e) => {
3183
+ if (e.key === "Enter" || e.key === " ") {
3184
+ e.preventDefault();
3185
+ clearSelection(e);
3186
+ }
3187
+ },
3188
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
3189
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PlusCircle, {}),
3190
+ title,
3191
+ selectedValues.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3192
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Separator, {
3193
+ orientation: "vertical",
3194
+ className: "mx-0.5 h-4"
3067
3195
  }),
3068
- selectedValues.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandSeparator, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandGroup, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandItem, {
3069
- onSelect: () => onReset(),
3070
- className: "justify-center text-center",
3071
- children: "Clear filters"
3072
- }) })] })
3073
- ]
3074
- })] })
3075
- })]
3196
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
3197
+ variant: "secondary",
3198
+ className: "rounded-sm px-1 font-normal lg:hidden",
3199
+ children: selectedValues.length
3200
+ }),
3201
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3202
+ className: "hidden items-center gap-1 lg:flex",
3203
+ children: selectedValues.length > 2 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
3204
+ variant: "secondary",
3205
+ className: "rounded-sm px-1 font-normal",
3206
+ children: [selectedValues.length, " selected"]
3207
+ }) : selectedOptions.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
3208
+ variant: "secondary",
3209
+ className: "rounded-sm px-1 font-normal",
3210
+ children: option.label
3211
+ }, option.value))
3212
+ })
3213
+ ] })
3214
+ ]
3215
+ })
3076
3216
  });
3077
3217
  }
3078
3218
  function SimpleSliderFilter({ title, range, unit, value, onChange }) {
@@ -3983,11 +4123,32 @@ function parseZodField(schema) {
3983
4123
  /**
3984
4124
  * 渲染单元格内容
3985
4125
  */
3986
- function renderCell(value, type) {
4126
+ function renderCell(value, type, options) {
3987
4127
  if (value === null || value === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3988
4128
  className: "text-muted-foreground",
3989
4129
  children: "-"
3990
4130
  });
4131
+ const formatOptionValue = (optionValue) => {
4132
+ const stringValue = String(optionValue);
4133
+ return options?.find((option) => option.value === stringValue)?.label ?? stringValue;
4134
+ };
4135
+ if (options && options.length > 0) {
4136
+ if (Array.isArray(value)) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4137
+ className: "flex gap-1 flex-wrap",
4138
+ children: [value.slice(0, 3).map((v, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
4139
+ variant: "secondary",
4140
+ children: formatOptionValue(v)
4141
+ }, i)), value.length > 3 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
4142
+ variant: "outline",
4143
+ children: ["+", value.length - 3]
4144
+ })]
4145
+ });
4146
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
4147
+ variant: "outline",
4148
+ className: "capitalize",
4149
+ children: formatOptionValue(value)
4150
+ });
4151
+ }
3991
4152
  switch (type) {
3992
4153
  case "boolean": return value ? "✓" : "✗";
3993
4154
  case "date": return formatDate(value);
@@ -4060,7 +4221,7 @@ function createTableSchema(schema, options) {
4060
4221
  column,
4061
4222
  label
4062
4223
  }),
4063
- cell: ({ row }) => renderCell(row.getValue(key), parsed.type),
4224
+ cell: ({ row }) => renderCell(row.getValue(key), parsed.type, meta.options),
4064
4225
  enableColumnFilter: true,
4065
4226
  enableSorting: true,
4066
4227
  meta,
@@ -4757,25 +4918,27 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
4757
4918
  clearOnDefault: true
4758
4919
  });
4759
4920
  const searchConfig = search && typeof search === "object" ? search : {};
4921
+ const enableAdvancedFilter = true;
4922
+ const columns = (0, react.useMemo)(() => {
4923
+ const dataColumns = createTableSchema(schema, {
4924
+ overrides,
4925
+ exclude
4926
+ });
4927
+ const result = enableRowSelection ? [createSelectColumn(), ...dataColumns] : dataColumns;
4928
+ if (actions) result.push(createActionsColumn(actions));
4929
+ return result;
4930
+ }, [
4931
+ schema,
4932
+ overrides,
4933
+ enableRowSelection,
4934
+ exclude,
4935
+ actions
4936
+ ]);
4760
4937
  const { table, shallow, debounceMs, throttleMs } = useDataTable({
4761
4938
  data,
4762
- columns: (0, react.useMemo)(() => {
4763
- const dataColumns = createTableSchema(schema, {
4764
- overrides,
4765
- exclude
4766
- });
4767
- const result = enableRowSelection ? [createSelectColumn(), ...dataColumns] : dataColumns;
4768
- if (actions) result.push(createActionsColumn(actions));
4769
- return result;
4770
- }, [
4771
- schema,
4772
- overrides,
4773
- enableRowSelection,
4774
- exclude,
4775
- actions
4776
- ]),
4939
+ columns,
4777
4940
  pageCount,
4778
- enableAdvancedFilter: true,
4941
+ enableAdvancedFilter,
4779
4942
  initialState: (0, react.useMemo)(() => ({
4780
4943
  sorting: initialSorting,
4781
4944
  columnPinning: pinnedColumns ?? {
@@ -4861,6 +5024,7 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
4861
5024
  }, [
4862
5025
  currentMode,
4863
5026
  table,
5027
+ columns,
4864
5028
  shallow,
4865
5029
  searchInput,
4866
5030
  debounceMs,
@@ -5076,37 +5240,31 @@ const FormilySwitch = (0, __formily_react.connect)(__pixpilot_shadcn.Switch, (0,
5076
5240
  onInput: "onCheckedChange"
5077
5241
  }));
5078
5242
  const FormilyCombobox = (0, __formily_react.connect)(AutoCrudCombobox, (0, __formily_react.mapProps)({ dataSource: "options" }));
5079
- function AutoCrudCombobox({ options = [], filter, className,...props }) {
5080
- const resolvedFilter = (0, react.useMemo)(() => {
5081
- if (typeof filter === "function") return filter;
5082
- return createComboboxFilter(options);
5083
- }, [filter, options]);
5084
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn_ui.Combobox, {
5243
+ const FormilyMultiCombobox = (0, __formily_react.connect)(AutoCrudMultiCombobox, (0, __formily_react.mapProps)({
5244
+ dataSource: "options",
5245
+ onInput: "onChange"
5246
+ }, (props, field) => {
5247
+ const fieldValue = field.value;
5248
+ return {
5249
+ ...props,
5250
+ value: Array.isArray(fieldValue) ? fieldValue.map(String) : []
5251
+ };
5252
+ }));
5253
+ function AutoCrudCombobox({ options = [], className, value, onChange,...props }) {
5254
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiCombobox, {
5085
5255
  ...props,
5256
+ value: value ? [value] : [],
5086
5257
  options,
5087
- filter: resolvedFilter,
5258
+ selectionMode: "single",
5259
+ onChange: (nextValues) => onChange?.(nextValues[0] ?? ""),
5088
5260
  className: (0, __pixpilot_shadcn.cn)(COMBOBOX_LIST_CLASS, className)
5089
5261
  });
5090
5262
  }
5091
- function createComboboxFilter(options) {
5092
- const searchTextByValue = new Map(options.map((option) => [option.value, collectSearchText(option.value, option.label, option.searchText, option.keywords).toLocaleLowerCase()]));
5093
- return (value, search, keywords) => {
5094
- const query = search.trim().toLocaleLowerCase();
5095
- if (!query) return 1;
5096
- const searchText = collectSearchText(searchTextByValue.get(value), keywords).toLocaleLowerCase();
5097
- if (searchText === query) return 2;
5098
- return searchText.includes(query) ? 1 : 0;
5099
- };
5100
- }
5101
- function collectSearchText(...parts) {
5102
- const texts = [];
5103
- for (const part of parts) {
5104
- if (!part) continue;
5105
- if (Array.isArray(part)) texts.push(...part.map(String));
5106
- else if (typeof part === "object") texts.push(...Object.values(part).filter(Boolean).map(String));
5107
- else texts.push(String(part));
5108
- }
5109
- return texts.join(" ");
5263
+ function AutoCrudMultiCombobox({ options = [],...props }) {
5264
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiCombobox, {
5265
+ ...props,
5266
+ options
5267
+ });
5110
5268
  }
5111
5269
  function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides, mode = "create", loading = false, gridColumns = 1, labelAlign = "top", labelWidth, showSubmitButton = true }, ref) {
5112
5270
  const form = (0, react.useMemo)(() => (0, __formily_core.createForm)({ initialValues }), [JSON.stringify(initialValues)]);
@@ -5137,6 +5295,10 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
5137
5295
  component: FormilyCombobox,
5138
5296
  decorator: "FormItem"
5139
5297
  },
5298
+ MultiCombobox: {
5299
+ component: FormilyMultiCombobox,
5300
+ decorator: "FormItem"
5301
+ },
5140
5302
  Switch: {
5141
5303
  component: FormilySwitch,
5142
5304
  decorator: "FormItem"
@@ -5954,6 +6116,7 @@ function mergeFieldConfig(base, override) {
5954
6116
  return {
5955
6117
  ...base,
5956
6118
  ...override,
6119
+ enum: override?.enum ?? base?.enum,
5957
6120
  table: mergeFieldPart(base?.table, override?.table),
5958
6121
  filter: mergeFieldPart(base?.filter, override?.filter),
5959
6122
  form: mergeFieldPart(base?.form, override?.form)
@@ -5964,6 +6127,32 @@ function mergeFields(base, override) {
5964
6127
  const keys = new Set([...Object.keys(base ?? {}), ...Object.keys(override ?? {})]);
5965
6128
  return Object.fromEntries(Array.from(keys).map((key) => [key, mergeFieldConfig(base?.[key], override?.[key])]));
5966
6129
  }
6130
+ function normalizeFieldOptions(options) {
6131
+ if (!options || options.length === 0) return void 0;
6132
+ return options.map((option) => ({
6133
+ ...option,
6134
+ value: String(option.value)
6135
+ }));
6136
+ }
6137
+ function toTableOptions(options) {
6138
+ return options?.map(({ label, value, searchText, count, icon }) => ({
6139
+ label,
6140
+ value,
6141
+ searchText: Array.isArray(searchText) ? searchText.join(" ") : searchText,
6142
+ count,
6143
+ icon
6144
+ }));
6145
+ }
6146
+ function toBatchOptions(options) {
6147
+ return options?.map(({ label, value }) => ({
6148
+ label,
6149
+ value
6150
+ }));
6151
+ }
6152
+ function getOptionLabel(value, options) {
6153
+ const stringValue = String(value);
6154
+ return options?.find((option) => option.value === stringValue)?.label ?? stringValue;
6155
+ }
5967
6156
  /**
5968
6157
  * 从统一配置生成表格 overrides
5969
6158
  * 当 filter 配置存在时,合并到 meta 中(不影响列隐藏)
@@ -5971,7 +6160,12 @@ function mergeFields(base, override) {
5971
6160
  function buildTableOverrides(fields, legacyOverrides) {
5972
6161
  const result = { ...legacyOverrides };
5973
6162
  if (fields) for (const [key, config] of Object.entries(fields)) {
6163
+ const tableOptions = toTableOptions(normalizeFieldOptions(config.enum));
5974
6164
  const tableMeta = config.table !== false && typeof config.table === "object" ? config.table.meta : void 0;
6165
+ const fieldEnumMeta = tableOptions ? {
6166
+ options: tableOptions,
6167
+ variant: "multiSelect"
6168
+ } : void 0;
5975
6169
  let filterMeta;
5976
6170
  if (config.filter && typeof config.filter === "object") {
5977
6171
  if (config.filter.enabled !== false && config.filter.hidden !== true) {
@@ -5989,10 +6183,11 @@ function buildTableOverrides(fields, legacyOverrides) {
5989
6183
  enableColumnFilter: false
5990
6184
  };
5991
6185
  }
5992
- if (tableMeta || filterMeta) result[key] = {
6186
+ if (fieldEnumMeta || tableMeta || filterMeta) result[key] = {
5993
6187
  ...result[key],
5994
6188
  meta: {
5995
6189
  ...result[key]?.meta ?? {},
6190
+ ...fieldEnumMeta ?? {},
5996
6191
  ...tableMeta ?? {},
5997
6192
  ...filterMeta ?? {}
5998
6193
  }
@@ -6030,6 +6225,7 @@ function buildFormOverrides(fields, legacyOverrides, denyFields) {
6030
6225
  "x-hidden": true
6031
6226
  };
6032
6227
  if (fields) for (const [key, config] of Object.entries(fields)) {
6228
+ const fieldOptions = normalizeFieldOptions(config.enum);
6033
6229
  if (config.label) result[key] = {
6034
6230
  ...result[key],
6035
6231
  title: config.label
@@ -6038,6 +6234,10 @@ function buildFormOverrides(fields, legacyOverrides, denyFields) {
6038
6234
  ...result[key],
6039
6235
  "x-hidden": true
6040
6236
  };
6237
+ if (fieldOptions) result[key] = {
6238
+ ...result[key],
6239
+ enum: fieldOptions
6240
+ };
6041
6241
  if (config.form === false) result[key] = {
6042
6242
  ...result[key],
6043
6243
  "x-hidden": true
@@ -6080,6 +6280,12 @@ function buildBatchUpdateFields(schema, batchFields, fields) {
6080
6280
  }
6081
6281
  const parsed = parseZodField(fieldSchema);
6082
6282
  const label = fields?.[field]?.label ?? humanize(field);
6283
+ const batchOptions = toBatchOptions(normalizeFieldOptions(fields?.[field]?.enum));
6284
+ if (batchOptions) return {
6285
+ field,
6286
+ label,
6287
+ options: batchOptions
6288
+ };
6083
6289
  if (parsed.type === "enum" && parsed.enumValues) return {
6084
6290
  field,
6085
6291
  label,
@@ -6099,11 +6305,28 @@ function buildBatchUpdateFields(schema, batchFields, fields) {
6099
6305
  /**
6100
6306
  * 渲染字段值
6101
6307
  */
6102
- function renderFieldValue(value, type, booleanLocale) {
6308
+ function renderFieldValue(value, type, booleanLocale, options) {
6103
6309
  if (value === null || value === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6104
6310
  className: "text-muted-foreground",
6105
6311
  children: "-"
6106
6312
  });
6313
+ if (options && options.length > 0) {
6314
+ if (Array.isArray(value)) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6315
+ className: "flex gap-1 flex-wrap",
6316
+ children: [value.slice(0, 5).map((v, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
6317
+ variant: "secondary",
6318
+ children: getOptionLabel(v, options)
6319
+ }, i)), value.length > 5 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
6320
+ variant: "outline",
6321
+ children: ["+", value.length - 5]
6322
+ })]
6323
+ });
6324
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
6325
+ variant: "outline",
6326
+ className: "capitalize",
6327
+ children: getOptionLabel(value, options)
6328
+ });
6329
+ }
6107
6330
  switch (type) {
6108
6331
  case "boolean": return value ? booleanLocale.true : booleanLocale.false;
6109
6332
  case "date": return formatDate(value);
@@ -6141,6 +6364,7 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
6141
6364
  const parsed = parseZodField(fieldSchema);
6142
6365
  const label = fieldConfig?.[key]?.label ?? humanize(key);
6143
6366
  const value = data[key];
6367
+ const options = normalizeFieldOptions(fieldConfig?.[key]?.enum);
6144
6368
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6145
6369
  className: "grid grid-cols-3 items-start gap-4",
6146
6370
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
@@ -6148,7 +6372,7 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
6148
6372
  children: label
6149
6373
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
6150
6374
  className: "col-span-2 text-sm",
6151
- children: renderFieldValue(value, parsed.type, locale.boolean)
6375
+ children: renderFieldValue(value, parsed.type, locale.boolean, options)
6152
6376
  })]
6153
6377
  }, key);
6154
6378
  })
@@ -6580,122 +6804,62 @@ function DataTableAdvancedToolbar({ table, children, className,...props }) {
6580
6804
 
6581
6805
  //#endregion
6582
6806
  //#region src/components/data-table/data-table-faceted-filter.tsx
6583
- function getOptionCommandValue(option) {
6584
- return [
6585
- option.value,
6586
- option.label,
6587
- option.searchText
6588
- ].filter(Boolean).join(" ");
6589
- }
6590
6807
  function DataTableFacetedFilter({ column, title, options, multiple }) {
6591
- const [open, setOpen] = react.useState(false);
6592
6808
  const columnFilterValue = column?.getFilterValue();
6593
- const selectedValues = new Set(Array.isArray(columnFilterValue) ? columnFilterValue : []);
6594
- const onItemSelect = react.useCallback((option, isSelected) => {
6595
- if (!column) return;
6596
- if (multiple) {
6597
- const newSelectedValues = new Set(selectedValues);
6598
- if (isSelected) newSelectedValues.delete(option.value);
6599
- else newSelectedValues.add(option.value);
6600
- const filterValues = Array.from(newSelectedValues);
6601
- column.setFilterValue(filterValues.length ? filterValues : void 0);
6602
- } else {
6603
- column.setFilterValue(isSelected ? void 0 : [option.value]);
6604
- setOpen(false);
6605
- }
6606
- }, [
6607
- column,
6608
- multiple,
6609
- selectedValues
6610
- ]);
6611
- const onReset = react.useCallback((event) => {
6612
- event?.stopPropagation();
6613
- column?.setFilterValue(void 0);
6614
- }, [column]);
6615
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Popover, {
6616
- open,
6617
- onOpenChange: setOpen,
6618
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.PopoverTrigger, {
6619
- asChild: true,
6620
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Button, {
6621
- variant: "outline",
6622
- size: "sm",
6623
- className: "border-dashed font-normal",
6624
- children: [
6625
- selectedValues?.size > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6626
- role: "button",
6627
- "aria-label": `Clear ${title} filter`,
6628
- tabIndex: 0,
6629
- className: "rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
6630
- onClick: onReset,
6631
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
6632
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PlusCircle, {}),
6633
- title,
6634
- selectedValues?.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
6635
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Separator, {
6636
- orientation: "vertical",
6637
- className: "mx-0.5 data-[orientation=vertical]:h-4"
6638
- }),
6639
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
6640
- variant: "secondary",
6641
- className: "rounded-sm px-1 font-normal lg:hidden",
6642
- children: selectedValues.size
6643
- }),
6644
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6645
- className: "hidden items-center gap-1 lg:flex",
6646
- children: selectedValues.size > 2 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
6647
- variant: "secondary",
6648
- className: "rounded-sm px-1 font-normal",
6649
- children: [selectedValues.size, " selected"]
6650
- }) : options.filter((option) => selectedValues.has(option.value)).map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
6651
- variant: "secondary",
6652
- className: "rounded-sm px-1 font-normal",
6653
- children: option.label
6654
- }, option.value))
6655
- })
6656
- ] })
6657
- ]
6658
- })
6659
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.PopoverContent, {
6660
- className: "w-50 p-0",
6661
- align: "start",
6662
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Command, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandInput, { placeholder: title }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandList, {
6663
- className: "max-h-full",
6664
- children: [
6665
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandEmpty, { children: "No results found." }),
6666
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandGroup, {
6667
- className: "max-h-[300px] scroll-py-1 overflow-y-auto overflow-x-hidden",
6668
- children: options.map((option) => {
6669
- const isSelected = selectedValues.has(option.value);
6670
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandItem, {
6671
- value: getOptionCommandValue(option),
6672
- onSelect: () => onItemSelect(option, isSelected),
6673
- children: [
6674
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6675
- className: cn("flex size-4 items-center justify-center rounded-sm border border-primary", isSelected ? "bg-primary" : "opacity-50 [&_svg]:invisible"),
6676
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {})
6677
- }),
6678
- option.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(option.icon, {}),
6679
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6680
- className: "truncate",
6681
- children: option.label
6682
- }),
6683
- option.count && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6684
- className: "ml-auto font-mono text-xs",
6685
- children: option.count
6686
- })
6687
- ]
6688
- }, option.value);
6689
- })
6809
+ const selectedValues = Array.isArray(columnFilterValue) ? columnFilterValue.filter((value) => typeof value === "string") : [];
6810
+ const onChange = (nextValues) => {
6811
+ column?.setFilterValue(nextValues.length ? nextValues : void 0);
6812
+ };
6813
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiCombobox, {
6814
+ value: selectedValues,
6815
+ onChange,
6816
+ options,
6817
+ selectionMode: multiple ? "multiple" : "single",
6818
+ searchPlaceholder: title,
6819
+ contentClassName: "w-50",
6820
+ matchTriggerWidth: false,
6821
+ clearText: "Clear filters",
6822
+ renderTrigger: ({ selectedOptions, selectedValues: selectedValues$1, clearSelection }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Button, {
6823
+ type: "button",
6824
+ variant: "outline",
6825
+ size: "sm",
6826
+ className: "border-dashed font-normal",
6827
+ disabled: !column,
6828
+ children: [
6829
+ selectedValues$1.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6830
+ role: "button",
6831
+ "aria-label": `Clear ${title} filter`,
6832
+ tabIndex: 0,
6833
+ className: "rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
6834
+ onClick: clearSelection,
6835
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
6836
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PlusCircle, {}),
6837
+ title,
6838
+ selectedValues$1.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
6839
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Separator, {
6840
+ orientation: "vertical",
6841
+ className: "mx-0.5 data-[orientation=vertical]:h-4"
6690
6842
  }),
6691
- selectedValues.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandSeparator, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandGroup, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandItem, {
6692
- onSelect: () => onReset(),
6693
- className: "justify-center text-center",
6694
- children: "Clear filters"
6695
- }) })] })
6696
- ]
6697
- })] })
6698
- })]
6843
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
6844
+ variant: "secondary",
6845
+ className: "rounded-sm px-1 font-normal lg:hidden",
6846
+ children: selectedValues$1.length
6847
+ }),
6848
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6849
+ className: "hidden items-center gap-1 lg:flex",
6850
+ children: selectedValues$1.length > 2 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
6851
+ variant: "secondary",
6852
+ className: "rounded-sm px-1 font-normal",
6853
+ children: [selectedValues$1.length, " selected"]
6854
+ }) : selectedOptions.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
6855
+ variant: "secondary",
6856
+ className: "rounded-sm px-1 font-normal",
6857
+ children: option.label
6858
+ }, option.value))
6859
+ })
6860
+ ] })
6861
+ ]
6862
+ })
6699
6863
  });
6700
6864
  }
6701
6865
 
@@ -7929,6 +8093,7 @@ exports.DataTableToolbar = DataTableToolbar;
7929
8093
  exports.DataTableViewOptions = DataTableViewOptions;
7930
8094
  exports.ExportDialog = ExportDialog;
7931
8095
  exports.ImportDialog = ImportDialog;
8096
+ exports.MultiCombobox = MultiCombobox;
7932
8097
  exports.SchemaAdapter = SchemaAdapter;
7933
8098
  exports.cn = cn;
7934
8099
  exports.coerceRowValues = coerceRowValues;