@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 +595 -269
- package/dist/index.d.cts +130 -28
- package/dist/index.d.ts +130 -28
- package/dist/index.js +593 -271
- package/package.json +4 -4
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
|
|
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
|
|
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
|
|
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
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
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
|
-
|
|
3072
|
-
|
|
3073
|
-
className: "
|
|
3074
|
-
children:
|
|
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
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
return
|
|
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
|
-
|
|
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
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
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
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
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
|
-
|
|
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:
|
|
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 =
|
|
6600
|
-
const
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
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
|
-
|
|
6698
|
-
|
|
6699
|
-
className: "
|
|
6700
|
-
children:
|
|
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;
|