@wordrhyme/auto-crud 1.1.6 → 1.1.8

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
 
@@ -1945,7 +1943,7 @@ const DEBOUNCE_MS$1 = 300;
1945
1943
  const THROTTLE_MS$1 = 50;
1946
1944
  const FILTER_SHORTCUT_KEY = "f";
1947
1945
  const REMOVE_FILTER_SHORTCUTS = ["backspace", "delete"];
1948
- function getOptionCommandValue$2(option) {
1946
+ function getOptionCommandValue(option) {
1949
1947
  return [
1950
1948
  option.value,
1951
1949
  option.label,
@@ -2231,7 +2229,7 @@ function FilterValueSelector({ column, value, onSelect }) {
2231
2229
  })] });
2232
2230
  case "select":
2233
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, {
2234
- value: getOptionCommandValue$2(option),
2232
+ value: getOptionCommandValue(option),
2235
2233
  onSelect: () => onSelect(option.value),
2236
2234
  children: [
2237
2235
  option.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(option.icon, {}),
@@ -2358,7 +2356,7 @@ function onFilterInputRender({ filter, column, inputId, onFilterUpdate, showValu
2358
2356
  align: "start",
2359
2357
  className: "w-48 p-0",
2360
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, {
2361
- value: getOptionCommandValue$2(option),
2359
+ value: getOptionCommandValue(option),
2362
2360
  onSelect: () => {
2363
2361
  const value = filter.variant === "multiSelect" ? selectedValues.includes(option.value) ? selectedValues.filter((v) => v !== option.value) : [...selectedValues, option.value] : option.value;
2364
2362
  onFilterUpdate(filter.filterId, { value });
@@ -2715,6 +2713,205 @@ function DataTableViewOptions({ table, disabled,...props }) {
2715
2713
  })] });
2716
2714
  }
2717
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
+
2718
2915
  //#endregion
2719
2916
  //#region src/components/auto-crud/auto-table-simple-filters.tsx
2720
2917
  function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange, leading }) {
@@ -2962,120 +3159,60 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
2962
3159
  ]
2963
3160
  });
2964
3161
  }
2965
- function getOptionCommandValue$1(option) {
2966
- return [
2967
- option.value,
2968
- option.label,
2969
- option.searchText
2970
- ].filter(Boolean).join(" ");
2971
- }
2972
3162
  function SimpleFacetedFilter({ title, options, multiple, value, onChange }) {
2973
- const [open, setOpen] = react.useState(false);
2974
- const selectedValues = react.useMemo(() => new Set(value), [value]);
2975
- const onItemSelect = (option, isSelected) => {
2976
- if (multiple) {
2977
- const newValues = new Set(selectedValues);
2978
- if (isSelected) newValues.delete(option.value);
2979
- else newValues.add(option.value);
2980
- onChange(Array.from(newValues));
2981
- } else {
2982
- onChange(isSelected ? [] : [option.value]);
2983
- setOpen(false);
2984
- }
2985
- };
2986
- const onReset = (e) => {
2987
- e?.stopPropagation();
2988
- onChange([]);
2989
- };
2990
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Popover, {
2991
- open,
2992
- onOpenChange: setOpen,
2993
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.PopoverTrigger, {
2994
- asChild: true,
2995
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Button, {
2996
- variant: "outline",
2997
- size: "sm",
2998
- className: "border-dashed font-normal",
2999
- children: [
3000
- selectedValues.size > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3001
- role: "button",
3002
- tabIndex: 0,
3003
- className: "rounded-sm opacity-70 hover:opacity-100",
3004
- onClick: onReset,
3005
- onKeyDown: (e) => {
3006
- if (e.key === "Enter" || e.key === " ") {
3007
- e.preventDefault();
3008
- onReset(e);
3009
- }
3010
- },
3011
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
3012
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PlusCircle, {}),
3013
- title,
3014
- selectedValues.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3015
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Separator, {
3016
- orientation: "vertical",
3017
- className: "mx-0.5 h-4"
3018
- }),
3019
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
3020
- variant: "secondary",
3021
- className: "rounded-sm px-1 font-normal lg:hidden",
3022
- children: selectedValues.size
3023
- }),
3024
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3025
- className: "hidden items-center gap-1 lg:flex",
3026
- children: selectedValues.size > 2 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
3027
- variant: "secondary",
3028
- className: "rounded-sm px-1 font-normal",
3029
- children: [selectedValues.size, " selected"]
3030
- }) : options.filter((o) => selectedValues.has(o.value)).map((o) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
3031
- variant: "secondary",
3032
- className: "rounded-sm px-1 font-normal",
3033
- children: o.label
3034
- }, o.value))
3035
- })
3036
- ] })
3037
- ]
3038
- })
3039
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.PopoverContent, {
3040
- className: "w-50 p-0",
3041
- align: "start",
3042
- 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, {
3043
- className: "max-h-full",
3044
- children: [
3045
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandEmpty, { children: "No results found." }),
3046
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandGroup, {
3047
- className: "max-h-[300px] overflow-y-auto",
3048
- children: options.map((option) => {
3049
- const isSelected = selectedValues.has(option.value);
3050
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandItem, {
3051
- value: getOptionCommandValue$1(option),
3052
- onSelect: () => onItemSelect(option, isSelected),
3053
- children: [
3054
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3055
- className: cn("flex size-4 items-center justify-center rounded-sm border border-primary", isSelected ? "bg-primary" : "opacity-50 [&_svg]:invisible"),
3056
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {})
3057
- }),
3058
- option.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(option.icon, {}),
3059
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3060
- className: "truncate",
3061
- children: option.label
3062
- }),
3063
- option.count && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3064
- className: "ml-auto font-mono text-xs",
3065
- children: option.count
3066
- })
3067
- ]
3068
- }, option.value);
3069
- })
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"
3070
3195
  }),
3071
- 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, {
3072
- onSelect: () => onReset(),
3073
- className: "justify-center text-center",
3074
- children: "Clear filters"
3075
- }) })] })
3076
- ]
3077
- })] })
3078
- })]
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
+ })
3079
3216
  });
3080
3217
  }
3081
3218
  function SimpleSliderFilter({ title, range, unit, value, onChange }) {
@@ -3986,11 +4123,32 @@ function parseZodField(schema) {
3986
4123
  /**
3987
4124
  * 渲染单元格内容
3988
4125
  */
3989
- function renderCell(value, type) {
4126
+ function renderCell(value, type, options) {
3990
4127
  if (value === null || value === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3991
4128
  className: "text-muted-foreground",
3992
4129
  children: "-"
3993
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
+ }
3994
4152
  switch (type) {
3995
4153
  case "boolean": return value ? "✓" : "✗";
3996
4154
  case "date": return formatDate(value);
@@ -4063,7 +4221,7 @@ function createTableSchema(schema, options) {
4063
4221
  column,
4064
4222
  label
4065
4223
  }),
4066
- cell: ({ row }) => renderCell(row.getValue(key), parsed.type),
4224
+ cell: ({ row }) => renderCell(row.getValue(key), parsed.type, meta.options),
4067
4225
  enableColumnFilter: true,
4068
4226
  enableSorting: true,
4069
4227
  meta,
@@ -5074,6 +5232,73 @@ function createEditFormSchema(schema, options) {
5074
5232
  });
5075
5233
  }
5076
5234
 
5235
+ //#endregion
5236
+ //#region src/lib/registries.ts
5237
+ function createRegistry(label) {
5238
+ const items = /* @__PURE__ */ new Map();
5239
+ const listeners = /* @__PURE__ */ new Set();
5240
+ let notifyQueued = false;
5241
+ const notify = () => {
5242
+ if (notifyQueued) return;
5243
+ notifyQueued = true;
5244
+ queueMicrotask(() => {
5245
+ notifyQueued = false;
5246
+ listeners.forEach((listener) => listener());
5247
+ });
5248
+ };
5249
+ return {
5250
+ register(name, value) {
5251
+ const current = items.get(name);
5252
+ if (current === value) return;
5253
+ if (current !== void 0) {
5254
+ console.warn(`[AutoCrud] ${label} "${name}" is already registered.`);
5255
+ return;
5256
+ }
5257
+ items.set(name, value);
5258
+ notify();
5259
+ },
5260
+ unregister(name) {
5261
+ if (!items.delete(name)) return;
5262
+ notify();
5263
+ },
5264
+ get(name) {
5265
+ return items.get(name);
5266
+ },
5267
+ all() {
5268
+ return Object.fromEntries(items);
5269
+ },
5270
+ subscribe(listener) {
5271
+ listeners.add(listener);
5272
+ return () => {
5273
+ listeners.delete(listener);
5274
+ };
5275
+ }
5276
+ };
5277
+ }
5278
+ const formComponents = createRegistry("form component");
5279
+ const dataSources = createRegistry("data source");
5280
+ function normalizeDataSourceConfig(config) {
5281
+ if (!config) return void 0;
5282
+ if (typeof config === "string") return {
5283
+ key: config,
5284
+ dependsOn: [],
5285
+ resetOnChange: false
5286
+ };
5287
+ return {
5288
+ key: config.key,
5289
+ dependsOn: config.dependsOn ?? [],
5290
+ resetOnChange: config.resetOnChange ?? false
5291
+ };
5292
+ }
5293
+ function normalizeOptions(result) {
5294
+ const options = Array.isArray(result) ? result : result?.options;
5295
+ if (!options) return [];
5296
+ return options.map((option) => ({
5297
+ ...option,
5298
+ value: String(option.value)
5299
+ }));
5300
+ }
5301
+
5077
5302
  //#endregion
5078
5303
  //#region src/components/auto-crud/auto-form.tsx
5079
5304
  const COMBOBOX_LIST_CLASS = "[&_[cmdk-list]]:max-h-[360px] [&_[cmdk-list]]:overflow-x-hidden [&_[cmdk-list]]:overflow-y-auto";
@@ -5082,37 +5307,129 @@ const FormilySwitch = (0, __formily_react.connect)(__pixpilot_shadcn.Switch, (0,
5082
5307
  onInput: "onCheckedChange"
5083
5308
  }));
5084
5309
  const FormilyCombobox = (0, __formily_react.connect)(AutoCrudCombobox, (0, __formily_react.mapProps)({ dataSource: "options" }));
5085
- function AutoCrudCombobox({ options = [], filter, className,...props }) {
5086
- const resolvedFilter = (0, react.useMemo)(() => {
5087
- if (typeof filter === "function") return filter;
5088
- return createComboboxFilter(options);
5089
- }, [filter, options]);
5090
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn_ui.Combobox, {
5310
+ const FormilyMultiCombobox = (0, __formily_react.connect)(AutoCrudMultiCombobox, (0, __formily_react.mapProps)({
5311
+ dataSource: "options",
5312
+ onInput: "onChange"
5313
+ }, (props, field) => {
5314
+ const fieldValue = field.value;
5315
+ return {
5316
+ ...props,
5317
+ value: Array.isArray(fieldValue) ? fieldValue.map(String) : []
5318
+ };
5319
+ }));
5320
+ function AutoCrudCombobox({ options = [], className, value, onChange,...props }) {
5321
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiCombobox, {
5091
5322
  ...props,
5323
+ value: value ? [value] : [],
5092
5324
  options,
5093
- filter: resolvedFilter,
5325
+ selectionMode: "single",
5326
+ onChange: (nextValues) => onChange?.(nextValues[0] ?? ""),
5094
5327
  className: (0, __pixpilot_shadcn.cn)(COMBOBOX_LIST_CLASS, className)
5095
5328
  });
5096
5329
  }
5097
- function createComboboxFilter(options) {
5098
- const searchTextByValue = new Map(options.map((option) => [option.value, collectSearchText(option.value, option.label, option.searchText, option.keywords).toLocaleLowerCase()]));
5099
- return (value, search, keywords) => {
5100
- const query = search.trim().toLocaleLowerCase();
5101
- if (!query) return 1;
5102
- const searchText = collectSearchText(searchTextByValue.get(value), keywords).toLocaleLowerCase();
5103
- if (searchText === query) return 2;
5104
- return searchText.includes(query) ? 1 : 0;
5105
- };
5330
+ function AutoCrudMultiCombobox({ options = [],...props }) {
5331
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiCombobox, {
5332
+ ...props,
5333
+ options
5334
+ });
5106
5335
  }
5107
- function collectSearchText(...parts) {
5108
- const texts = [];
5109
- for (const part of parts) {
5110
- if (!part) continue;
5111
- if (Array.isArray(part)) texts.push(...part.map(String));
5112
- else if (typeof part === "object") texts.push(...Object.values(part).filter(Boolean).map(String));
5113
- else texts.push(String(part));
5336
+ const defaultFieldComponents = {
5337
+ Combobox: {
5338
+ component: FormilyCombobox,
5339
+ decorator: "FormItem"
5340
+ },
5341
+ MultiCombobox: {
5342
+ component: FormilyMultiCombobox,
5343
+ decorator: "FormItem"
5344
+ },
5345
+ Switch: {
5346
+ component: FormilySwitch,
5347
+ decorator: "FormItem"
5114
5348
  }
5115
- return texts.join(" ");
5349
+ };
5350
+ function useRegistryVersion(subscribe) {
5351
+ const [version, setVersion] = (0, react.useState)(0);
5352
+ (0, react.useEffect)(() => subscribe(() => setVersion((current) => current + 1)), [subscribe]);
5353
+ return version;
5354
+ }
5355
+ function isDataSourceConfig(value) {
5356
+ if (typeof value === "string") return value.length > 0;
5357
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
5358
+ return typeof value.key === "string";
5359
+ }
5360
+ function collectDataSourceConfigs(schema, result = {}) {
5361
+ const properties = schema.properties;
5362
+ if (!properties) return result;
5363
+ for (const [key, property] of Object.entries(properties)) {
5364
+ const customDataSource = property["x-data-source"];
5365
+ const schemaDataSource = property.dataSource;
5366
+ const dataSourceConfig = isDataSourceConfig(customDataSource) ? customDataSource : isDataSourceConfig(schemaDataSource) ? schemaDataSource : void 0;
5367
+ if (dataSourceConfig) result[key] = dataSourceConfig;
5368
+ collectDataSourceConfigs(property, result);
5369
+ }
5370
+ return result;
5371
+ }
5372
+ function useFormDataSources(form, schema) {
5373
+ (0, react.useEffect)(() => {
5374
+ const sourceEntries = Object.entries(collectDataSourceConfigs(schema)).map(([fieldName, config]) => {
5375
+ const normalized = normalizeDataSourceConfig(config);
5376
+ return normalized ? [fieldName, normalized] : void 0;
5377
+ }).filter(Boolean);
5378
+ if (sourceEntries.length === 0) return;
5379
+ let active = true;
5380
+ const effectId = Symbol("autoCrudDataSources");
5381
+ const loadVersions = /* @__PURE__ */ new Map();
5382
+ const controller = typeof AbortController === "undefined" ? void 0 : new AbortController();
5383
+ const loadFieldOptions = async (fieldName, source) => {
5384
+ const version = (loadVersions.get(fieldName) ?? 0) + 1;
5385
+ loadVersions.set(fieldName, version);
5386
+ const loader = dataSources.get(source.key);
5387
+ if (!loader) {
5388
+ form.setFieldState(fieldName, (state) => {
5389
+ state.dataSource = [];
5390
+ });
5391
+ return;
5392
+ }
5393
+ try {
5394
+ const options = normalizeOptions(await loader({
5395
+ field: fieldName,
5396
+ values: Object.fromEntries(source.dependsOn.map((dependency) => [dependency, form.getValuesIn(dependency)])),
5397
+ signal: controller?.signal
5398
+ }));
5399
+ if (!active || loadVersions.get(fieldName) !== version) return;
5400
+ form.setFieldState(fieldName, (state) => {
5401
+ state.dataSource = options;
5402
+ });
5403
+ } catch (error) {
5404
+ if (!active || controller?.signal.aborted) return;
5405
+ console.warn(`[AutoCrud] Failed to load data source "${source.key}".`, error);
5406
+ form.setFieldState(fieldName, (state) => {
5407
+ state.dataSource = [];
5408
+ });
5409
+ }
5410
+ };
5411
+ for (const [fieldName, source] of sourceEntries) loadFieldOptions(fieldName, source);
5412
+ form.addEffects(effectId, () => {
5413
+ new Set(sourceEntries.flatMap(([, source]) => source.dependsOn)).forEach((dependency) => {
5414
+ (0, __formily_core.onFieldValueChange)(dependency, () => {
5415
+ sourceEntries.forEach(([fieldName, source]) => {
5416
+ if (!source.dependsOn.includes(dependency)) return;
5417
+ if (source.resetOnChange) form.setValuesIn(fieldName, void 0);
5418
+ loadFieldOptions(fieldName, source);
5419
+ });
5420
+ });
5421
+ });
5422
+ });
5423
+ return () => {
5424
+ active = false;
5425
+ controller?.abort();
5426
+ form.removeEffects(effectId);
5427
+ };
5428
+ }, [
5429
+ useRegistryVersion(dataSources.subscribe),
5430
+ form,
5431
+ schema
5432
+ ]);
5116
5433
  }
5117
5434
  function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides, mode = "create", loading = false, gridColumns = 1, labelAlign = "top", labelWidth, showSubmitButton = true }, ref) {
5118
5435
  const form = (0, react.useMemo)(() => (0, __formily_core.createForm)({ initialValues }), [JSON.stringify(initialValues)]);
@@ -5129,6 +5446,11 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
5129
5446
  labelAlign,
5130
5447
  labelWidth
5131
5448
  ]);
5449
+ const fieldComponents = (0, react.useMemo)(() => ({ fields: {
5450
+ ...defaultFieldComponents,
5451
+ ...formComponents.all()
5452
+ } }), [useRegistryVersion(formComponents.subscribe)]);
5453
+ useFormDataSources(form, formSchema);
5132
5454
  const handleSubmit = async () => {
5133
5455
  await form.validate();
5134
5456
  if (form.valid) await onSubmit(form.values);
@@ -5138,16 +5460,7 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
5138
5460
  form,
5139
5461
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_formily_shadcn.JsonSchemaField, {
5140
5462
  schema: formSchema,
5141
- components: { fields: {
5142
- Combobox: {
5143
- component: FormilyCombobox,
5144
- decorator: "FormItem"
5145
- },
5146
- Switch: {
5147
- component: FormilySwitch,
5148
- decorator: "FormItem"
5149
- }
5150
- } }
5463
+ components: fieldComponents
5151
5464
  }), showSubmitButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5152
5465
  className: "flex justify-end gap-2 mt-6",
5153
5466
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Button, {
@@ -5960,6 +6273,8 @@ function mergeFieldConfig(base, override) {
5960
6273
  return {
5961
6274
  ...base,
5962
6275
  ...override,
6276
+ enum: override?.enum ?? base?.enum,
6277
+ dataSource: override?.dataSource ?? base?.dataSource,
5963
6278
  table: mergeFieldPart(base?.table, override?.table),
5964
6279
  filter: mergeFieldPart(base?.filter, override?.filter),
5965
6280
  form: mergeFieldPart(base?.form, override?.form)
@@ -5970,6 +6285,32 @@ function mergeFields(base, override) {
5970
6285
  const keys = new Set([...Object.keys(base ?? {}), ...Object.keys(override ?? {})]);
5971
6286
  return Object.fromEntries(Array.from(keys).map((key) => [key, mergeFieldConfig(base?.[key], override?.[key])]));
5972
6287
  }
6288
+ function normalizeFieldOptions(options) {
6289
+ if (!options || options.length === 0) return void 0;
6290
+ return options.map((option) => ({
6291
+ ...option,
6292
+ value: String(option.value)
6293
+ }));
6294
+ }
6295
+ function toTableOptions(options) {
6296
+ return options?.map(({ label, value, searchText, count, icon }) => ({
6297
+ label,
6298
+ value,
6299
+ searchText: Array.isArray(searchText) ? searchText.join(" ") : searchText,
6300
+ count,
6301
+ icon
6302
+ }));
6303
+ }
6304
+ function toBatchOptions(options) {
6305
+ return options?.map(({ label, value }) => ({
6306
+ label,
6307
+ value
6308
+ }));
6309
+ }
6310
+ function getOptionLabel(value, options) {
6311
+ const stringValue = String(value);
6312
+ return options?.find((option) => option.value === stringValue)?.label ?? stringValue;
6313
+ }
5973
6314
  /**
5974
6315
  * 从统一配置生成表格 overrides
5975
6316
  * 当 filter 配置存在时,合并到 meta 中(不影响列隐藏)
@@ -5977,7 +6318,12 @@ function mergeFields(base, override) {
5977
6318
  function buildTableOverrides(fields, legacyOverrides) {
5978
6319
  const result = { ...legacyOverrides };
5979
6320
  if (fields) for (const [key, config] of Object.entries(fields)) {
6321
+ const tableOptions = toTableOptions(normalizeFieldOptions(config.enum));
5980
6322
  const tableMeta = config.table !== false && typeof config.table === "object" ? config.table.meta : void 0;
6323
+ const fieldEnumMeta = tableOptions ? {
6324
+ options: tableOptions,
6325
+ variant: "multiSelect"
6326
+ } : void 0;
5981
6327
  let filterMeta;
5982
6328
  if (config.filter && typeof config.filter === "object") {
5983
6329
  if (config.filter.enabled !== false && config.filter.hidden !== true) {
@@ -5995,10 +6341,11 @@ function buildTableOverrides(fields, legacyOverrides) {
5995
6341
  enableColumnFilter: false
5996
6342
  };
5997
6343
  }
5998
- if (tableMeta || filterMeta) result[key] = {
6344
+ if (fieldEnumMeta || tableMeta || filterMeta) result[key] = {
5999
6345
  ...result[key],
6000
6346
  meta: {
6001
6347
  ...result[key]?.meta ?? {},
6348
+ ...fieldEnumMeta ?? {},
6002
6349
  ...tableMeta ?? {},
6003
6350
  ...filterMeta ?? {}
6004
6351
  }
@@ -6036,6 +6383,8 @@ function buildFormOverrides(fields, legacyOverrides, denyFields) {
6036
6383
  "x-hidden": true
6037
6384
  };
6038
6385
  if (fields) for (const [key, config] of Object.entries(fields)) {
6386
+ const fieldOptions = normalizeFieldOptions((config.form && typeof config.form === "object" ? config.form : void 0)?.enum) ?? normalizeFieldOptions(config.enum);
6387
+ const fieldDataSource = fieldOptions || !config.dataSource ? void 0 : normalizeDataSourceConfig(config.dataSource);
6039
6388
  if (config.label) result[key] = {
6040
6389
  ...result[key],
6041
6390
  title: config.label
@@ -6044,6 +6393,14 @@ function buildFormOverrides(fields, legacyOverrides, denyFields) {
6044
6393
  ...result[key],
6045
6394
  "x-hidden": true
6046
6395
  };
6396
+ if (fieldOptions) result[key] = {
6397
+ ...result[key],
6398
+ enum: fieldOptions
6399
+ };
6400
+ if (fieldDataSource) result[key] = {
6401
+ ...result[key],
6402
+ "x-data-source": config.dataSource
6403
+ };
6047
6404
  if (config.form === false) result[key] = {
6048
6405
  ...result[key],
6049
6406
  "x-hidden": true
@@ -6086,6 +6443,12 @@ function buildBatchUpdateFields(schema, batchFields, fields) {
6086
6443
  }
6087
6444
  const parsed = parseZodField(fieldSchema);
6088
6445
  const label = fields?.[field]?.label ?? humanize(field);
6446
+ const batchOptions = toBatchOptions(normalizeFieldOptions(fields?.[field]?.enum));
6447
+ if (batchOptions) return {
6448
+ field,
6449
+ label,
6450
+ options: batchOptions
6451
+ };
6089
6452
  if (parsed.type === "enum" && parsed.enumValues) return {
6090
6453
  field,
6091
6454
  label,
@@ -6105,11 +6468,28 @@ function buildBatchUpdateFields(schema, batchFields, fields) {
6105
6468
  /**
6106
6469
  * 渲染字段值
6107
6470
  */
6108
- function renderFieldValue(value, type, booleanLocale) {
6471
+ function renderFieldValue(value, type, booleanLocale, options) {
6109
6472
  if (value === null || value === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6110
6473
  className: "text-muted-foreground",
6111
6474
  children: "-"
6112
6475
  });
6476
+ if (options && options.length > 0) {
6477
+ if (Array.isArray(value)) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6478
+ className: "flex gap-1 flex-wrap",
6479
+ children: [value.slice(0, 5).map((v, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
6480
+ variant: "secondary",
6481
+ children: getOptionLabel(v, options)
6482
+ }, i)), value.length > 5 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
6483
+ variant: "outline",
6484
+ children: ["+", value.length - 5]
6485
+ })]
6486
+ });
6487
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
6488
+ variant: "outline",
6489
+ className: "capitalize",
6490
+ children: getOptionLabel(value, options)
6491
+ });
6492
+ }
6113
6493
  switch (type) {
6114
6494
  case "boolean": return value ? booleanLocale.true : booleanLocale.false;
6115
6495
  case "date": return formatDate(value);
@@ -6147,6 +6527,7 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
6147
6527
  const parsed = parseZodField(fieldSchema);
6148
6528
  const label = fieldConfig?.[key]?.label ?? humanize(key);
6149
6529
  const value = data[key];
6530
+ const options = normalizeFieldOptions(fieldConfig?.[key]?.enum);
6150
6531
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6151
6532
  className: "grid grid-cols-3 items-start gap-4",
6152
6533
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
@@ -6154,7 +6535,7 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
6154
6535
  children: label
6155
6536
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
6156
6537
  className: "col-span-2 text-sm",
6157
- children: renderFieldValue(value, parsed.type, locale.boolean)
6538
+ children: renderFieldValue(value, parsed.type, locale.boolean, options)
6158
6539
  })]
6159
6540
  }, key);
6160
6541
  })
@@ -6586,122 +6967,62 @@ function DataTableAdvancedToolbar({ table, children, className,...props }) {
6586
6967
 
6587
6968
  //#endregion
6588
6969
  //#region src/components/data-table/data-table-faceted-filter.tsx
6589
- function getOptionCommandValue(option) {
6590
- return [
6591
- option.value,
6592
- option.label,
6593
- option.searchText
6594
- ].filter(Boolean).join(" ");
6595
- }
6596
6970
  function DataTableFacetedFilter({ column, title, options, multiple }) {
6597
- const [open, setOpen] = react.useState(false);
6598
6971
  const columnFilterValue = column?.getFilterValue();
6599
- const selectedValues = new Set(Array.isArray(columnFilterValue) ? columnFilterValue : []);
6600
- const onItemSelect = react.useCallback((option, isSelected) => {
6601
- if (!column) return;
6602
- if (multiple) {
6603
- const newSelectedValues = new Set(selectedValues);
6604
- if (isSelected) newSelectedValues.delete(option.value);
6605
- else newSelectedValues.add(option.value);
6606
- const filterValues = Array.from(newSelectedValues);
6607
- column.setFilterValue(filterValues.length ? filterValues : void 0);
6608
- } else {
6609
- column.setFilterValue(isSelected ? void 0 : [option.value]);
6610
- setOpen(false);
6611
- }
6612
- }, [
6613
- column,
6614
- multiple,
6615
- selectedValues
6616
- ]);
6617
- const onReset = react.useCallback((event) => {
6618
- event?.stopPropagation();
6619
- column?.setFilterValue(void 0);
6620
- }, [column]);
6621
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Popover, {
6622
- open,
6623
- onOpenChange: setOpen,
6624
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.PopoverTrigger, {
6625
- asChild: true,
6626
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Button, {
6627
- variant: "outline",
6628
- size: "sm",
6629
- className: "border-dashed font-normal",
6630
- children: [
6631
- selectedValues?.size > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6632
- role: "button",
6633
- "aria-label": `Clear ${title} filter`,
6634
- tabIndex: 0,
6635
- className: "rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
6636
- onClick: onReset,
6637
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
6638
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PlusCircle, {}),
6639
- title,
6640
- selectedValues?.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
6641
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Separator, {
6642
- orientation: "vertical",
6643
- className: "mx-0.5 data-[orientation=vertical]:h-4"
6644
- }),
6645
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
6646
- variant: "secondary",
6647
- className: "rounded-sm px-1 font-normal lg:hidden",
6648
- children: selectedValues.size
6649
- }),
6650
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6651
- className: "hidden items-center gap-1 lg:flex",
6652
- children: selectedValues.size > 2 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
6653
- variant: "secondary",
6654
- className: "rounded-sm px-1 font-normal",
6655
- children: [selectedValues.size, " selected"]
6656
- }) : options.filter((option) => selectedValues.has(option.value)).map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
6657
- variant: "secondary",
6658
- className: "rounded-sm px-1 font-normal",
6659
- children: option.label
6660
- }, option.value))
6661
- })
6662
- ] })
6663
- ]
6664
- })
6665
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.PopoverContent, {
6666
- className: "w-50 p-0",
6667
- align: "start",
6668
- 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, {
6669
- className: "max-h-full",
6670
- children: [
6671
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandEmpty, { children: "No results found." }),
6672
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.CommandGroup, {
6673
- className: "max-h-[300px] scroll-py-1 overflow-y-auto overflow-x-hidden",
6674
- children: options.map((option) => {
6675
- const isSelected = selectedValues.has(option.value);
6676
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.CommandItem, {
6677
- value: getOptionCommandValue(option),
6678
- onSelect: () => onItemSelect(option, isSelected),
6679
- children: [
6680
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6681
- className: cn("flex size-4 items-center justify-center rounded-sm border border-primary", isSelected ? "bg-primary" : "opacity-50 [&_svg]:invisible"),
6682
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {})
6683
- }),
6684
- option.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(option.icon, {}),
6685
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6686
- className: "truncate",
6687
- children: option.label
6688
- }),
6689
- option.count && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6690
- className: "ml-auto font-mono text-xs",
6691
- children: option.count
6692
- })
6693
- ]
6694
- }, option.value);
6695
- })
6972
+ const selectedValues = Array.isArray(columnFilterValue) ? columnFilterValue.filter((value) => typeof value === "string") : [];
6973
+ const onChange = (nextValues) => {
6974
+ column?.setFilterValue(nextValues.length ? nextValues : void 0);
6975
+ };
6976
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiCombobox, {
6977
+ value: selectedValues,
6978
+ onChange,
6979
+ options,
6980
+ selectionMode: multiple ? "multiple" : "single",
6981
+ searchPlaceholder: title,
6982
+ contentClassName: "w-50",
6983
+ matchTriggerWidth: false,
6984
+ clearText: "Clear filters",
6985
+ renderTrigger: ({ selectedOptions, selectedValues: selectedValues$1, clearSelection }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Button, {
6986
+ type: "button",
6987
+ variant: "outline",
6988
+ size: "sm",
6989
+ className: "border-dashed font-normal",
6990
+ disabled: !column,
6991
+ children: [
6992
+ selectedValues$1.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6993
+ role: "button",
6994
+ "aria-label": `Clear ${title} filter`,
6995
+ tabIndex: 0,
6996
+ className: "rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
6997
+ onClick: clearSelection,
6998
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
6999
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PlusCircle, {}),
7000
+ title,
7001
+ selectedValues$1.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
7002
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Separator, {
7003
+ orientation: "vertical",
7004
+ className: "mx-0.5 data-[orientation=vertical]:h-4"
6696
7005
  }),
6697
- 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, {
6698
- onSelect: () => onReset(),
6699
- className: "justify-center text-center",
6700
- children: "Clear filters"
6701
- }) })] })
6702
- ]
6703
- })] })
6704
- })]
7006
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
7007
+ variant: "secondary",
7008
+ className: "rounded-sm px-1 font-normal lg:hidden",
7009
+ children: selectedValues$1.length
7010
+ }),
7011
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7012
+ className: "hidden items-center gap-1 lg:flex",
7013
+ children: selectedValues$1.length > 2 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Badge, {
7014
+ variant: "secondary",
7015
+ className: "rounded-sm px-1 font-normal",
7016
+ children: [selectedValues$1.length, " selected"]
7017
+ }) : selectedOptions.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Badge, {
7018
+ variant: "secondary",
7019
+ className: "rounded-sm px-1 font-normal",
7020
+ children: option.label
7021
+ }, option.value))
7022
+ })
7023
+ ] })
7024
+ ]
7025
+ })
6705
7026
  });
6706
7027
  }
6707
7028
 
@@ -7935,6 +8256,7 @@ exports.DataTableToolbar = DataTableToolbar;
7935
8256
  exports.DataTableViewOptions = DataTableViewOptions;
7936
8257
  exports.ExportDialog = ExportDialog;
7937
8258
  exports.ImportDialog = ImportDialog;
8259
+ exports.MultiCombobox = MultiCombobox;
7938
8260
  exports.SchemaAdapter = SchemaAdapter;
7939
8261
  exports.cn = cn;
7940
8262
  exports.coerceRowValues = coerceRowValues;
@@ -7945,6 +8267,7 @@ exports.createMemoryDataSource = createMemoryDataSource;
7945
8267
  exports.createSelectColumn = createSelectColumn;
7946
8268
  exports.createTRPCDataSource = createTRPCDataSource;
7947
8269
  exports.createTableSchema = createTableSchema;
8270
+ exports.dataSources = dataSources;
7948
8271
  exports.dataToCSV = dataToCSV;
7949
8272
  exports.deDE = deDE;
7950
8273
  exports.downloadCSVTemplate = downloadCSVTemplate;
@@ -7952,6 +8275,7 @@ exports.enUS = enUS;
7952
8275
  exports.esES = esES;
7953
8276
  exports.exportAllToCSV = exportAllToCSV;
7954
8277
  exports.exportTableToCSV = exportTableToCSV;
8278
+ exports.formComponents = formComponents;
7955
8279
  exports.formatDate = formatDate;
7956
8280
  exports.frFR = frFR;
7957
8281
  exports.generateCSVTemplate = generateCSVTemplate;
@@ -7960,6 +8284,8 @@ exports.humanize = humanize;
7960
8284
  exports.jaJP = jaJP;
7961
8285
  exports.koKR = koKR;
7962
8286
  exports.noopToastAdapter = noopToastAdapter;
8287
+ exports.normalizeDataSourceConfig = normalizeDataSourceConfig;
8288
+ exports.normalizeOptions = normalizeOptions;
7963
8289
  exports.parseAsArrayOf = parseAsArrayOf;
7964
8290
  exports.parseAsInteger = parseAsInteger;
7965
8291
  exports.parseAsString = parseAsString;