@wordrhyme/auto-crud 1.2.5 → 1.3.1
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/README.md +116 -46
- package/dist/index.cjs +1475 -1723
- package/dist/index.d.cts +184 -97
- package/dist/index.d.ts +168 -81
- package/dist/index.js +1256 -1504
- package/package.json +5 -9
package/dist/index.js
CHANGED
|
@@ -2,22 +2,16 @@ import * as React from "react";
|
|
|
2
2
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useReducer, useRef, useState } from "react";
|
|
3
3
|
import { ArrowDownUp, BadgeCheck, CalendarIcon, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, ChevronsLeft, ChevronsRight, ChevronsUpDown, CommandIcon, Download, Ellipsis, EyeOff, FileDown, FileSpreadsheetIcon, GripVertical, ListFilter, ListFilterIcon, Loader2, PlusCircle, Settings2, Text, Trash2, Upload, X, XCircle } from "lucide-react";
|
|
4
4
|
import { flexRender, getCoreRowModel, getFacetedMinMaxValues, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
|
|
5
|
-
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Calendar, Checkbox, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList,
|
|
5
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Calendar, Checkbox, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, Input, Label, Popover, PopoverContent, PopoverTrigger, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, Slider, Switch, Table as Table$1, TableBody, TableCell, TableHead, TableHeader, TableRow, cn as cn$1 } from "@wordrhyme/shadcn";
|
|
6
6
|
import { clsx } from "clsx";
|
|
7
7
|
import { twMerge } from "tailwind-merge";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { customAlphabet } from "nanoid";
|
|
10
|
-
import {
|
|
11
|
-
import { restrictToHorizontalAxis, restrictToParentElement, restrictToVerticalAxis } from "@dnd-kit/modifiers";
|
|
12
|
-
import { SortableContext, arrayMove, horizontalListSortingStrategy, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
13
|
-
import { CSS } from "@dnd-kit/utilities";
|
|
14
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
15
|
-
import * as ReactDOM from "react-dom";
|
|
16
|
-
import { useDirection } from "@radix-ui/react-direction";
|
|
10
|
+
import { ActionBar, ActionBarClose, ActionBarGroup, ActionBarItem, ActionBarSelection, ActionBarSeparator, Faceted, FacetedBadgeList, FacetedContent, FacetedEmpty, FacetedGroup, FacetedInput, FacetedItem, FacetedList, FacetedTrigger, MultiCombobox, MultiCombobox as MultiCombobox$1, Sortable, SortableContent, SortableItem, SortableItemHandle, SortableOverlay } from "@wordrhyme/shadcn-ui";
|
|
17
11
|
import { z } from "zod";
|
|
18
|
-
import { createForm
|
|
19
|
-
import {
|
|
20
|
-
import { connect, mapProps } from "@formily
|
|
12
|
+
import { createForm } from "@formily/core";
|
|
13
|
+
import { action, observable } from "@formily/reactive";
|
|
14
|
+
import { Form, JsonSchemaField, connect, mapProps } from "@wordrhyme/formily-shadcn";
|
|
21
15
|
import { toast } from "sonner";
|
|
22
16
|
import { keepPreviousData } from "@tanstack/react-query";
|
|
23
17
|
|
|
@@ -128,55 +122,6 @@ function DataTablePagination({ table, pageSizeOptions = [
|
|
|
128
122
|
});
|
|
129
123
|
}
|
|
130
124
|
|
|
131
|
-
//#endregion
|
|
132
|
-
//#region src/components/ui/table.tsx
|
|
133
|
-
function Table$1({ className,...props }) {
|
|
134
|
-
return /* @__PURE__ */ jsx("div", {
|
|
135
|
-
"data-slot": "table-container",
|
|
136
|
-
className: "relative w-full overflow-x-auto",
|
|
137
|
-
children: /* @__PURE__ */ jsx("table", {
|
|
138
|
-
"data-slot": "table",
|
|
139
|
-
className: cn("w-full caption-bottom text-sm", className),
|
|
140
|
-
...props
|
|
141
|
-
})
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
function TableHeader({ className,...props }) {
|
|
145
|
-
return /* @__PURE__ */ jsx("thead", {
|
|
146
|
-
"data-slot": "table-header",
|
|
147
|
-
className: cn("[&_tr]:border-b", className),
|
|
148
|
-
...props
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
function TableBody({ className,...props }) {
|
|
152
|
-
return /* @__PURE__ */ jsx("tbody", {
|
|
153
|
-
"data-slot": "table-body",
|
|
154
|
-
className: cn("[&_tr:last-child]:border-0", className),
|
|
155
|
-
...props
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
function TableRow({ className,...props }) {
|
|
159
|
-
return /* @__PURE__ */ jsx("tr", {
|
|
160
|
-
"data-slot": "table-row",
|
|
161
|
-
className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className),
|
|
162
|
-
...props
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
function TableHead({ className,...props }) {
|
|
166
|
-
return /* @__PURE__ */ jsx("th", {
|
|
167
|
-
"data-slot": "table-head",
|
|
168
|
-
className: cn("h-10 whitespace-nowrap px-2 text-left align-middle font-medium text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className),
|
|
169
|
-
...props
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
function TableCell({ className,...props }) {
|
|
173
|
-
return /* @__PURE__ */ jsx("td", {
|
|
174
|
-
"data-slot": "table-cell",
|
|
175
|
-
className: cn("whitespace-nowrap p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className),
|
|
176
|
-
...props
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
125
|
//#endregion
|
|
181
126
|
//#region src/config/data-table.ts
|
|
182
127
|
const dataTableConfig = {
|
|
@@ -461,10 +406,10 @@ function getColumnVariant(column) {
|
|
|
461
406
|
function splitKey(key) {
|
|
462
407
|
const match = key.match(/^(.*?)(?:__(\d+))?$/);
|
|
463
408
|
if (!match) return { base: key };
|
|
464
|
-
const index = match[2] ? Number(match[2]) : void 0;
|
|
409
|
+
const index$1 = match[2] ? Number(match[2]) : void 0;
|
|
465
410
|
return {
|
|
466
411
|
base: match[1] ?? key,
|
|
467
|
-
index
|
|
412
|
+
index: index$1
|
|
468
413
|
};
|
|
469
414
|
}
|
|
470
415
|
function isFilterKey(key, columnIds) {
|
|
@@ -911,454 +856,6 @@ function DataTableRangeFilter({ filter, column, inputId, onFilterUpdate, classNa
|
|
|
911
856
|
});
|
|
912
857
|
}
|
|
913
858
|
|
|
914
|
-
//#endregion
|
|
915
|
-
//#region src/components/ui/faceted.tsx
|
|
916
|
-
const FacetedContext = React.createContext(null);
|
|
917
|
-
function useFacetedContext(name) {
|
|
918
|
-
const context = React.useContext(FacetedContext);
|
|
919
|
-
if (!context) throw new Error(`\`${name}\` must be within Faceted`);
|
|
920
|
-
return context;
|
|
921
|
-
}
|
|
922
|
-
function Faceted(props) {
|
|
923
|
-
const { open: openProp, onOpenChange: onOpenChangeProp, value, onValueChange, children, multiple = false,...facetedProps } = props;
|
|
924
|
-
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
|
|
925
|
-
const isControlled = openProp !== void 0;
|
|
926
|
-
const open = isControlled ? openProp : uncontrolledOpen;
|
|
927
|
-
const onOpenChange = React.useCallback((newOpen) => {
|
|
928
|
-
if (!isControlled) setUncontrolledOpen(newOpen);
|
|
929
|
-
onOpenChangeProp?.(newOpen);
|
|
930
|
-
}, [isControlled, onOpenChangeProp]);
|
|
931
|
-
const onItemSelect = React.useCallback((selectedValue) => {
|
|
932
|
-
if (!onValueChange) return;
|
|
933
|
-
if (multiple) {
|
|
934
|
-
const currentValue = Array.isArray(value) ? value : [];
|
|
935
|
-
onValueChange(currentValue.includes(selectedValue) ? currentValue.filter((v) => v !== selectedValue) : [...currentValue, selectedValue]);
|
|
936
|
-
} else {
|
|
937
|
-
if (value === selectedValue) onValueChange(void 0);
|
|
938
|
-
else onValueChange(selectedValue);
|
|
939
|
-
requestAnimationFrame(() => onOpenChange(false));
|
|
940
|
-
}
|
|
941
|
-
}, [
|
|
942
|
-
multiple,
|
|
943
|
-
value,
|
|
944
|
-
onValueChange,
|
|
945
|
-
onOpenChange
|
|
946
|
-
]);
|
|
947
|
-
const contextValue = React.useMemo(() => ({
|
|
948
|
-
value,
|
|
949
|
-
onItemSelect,
|
|
950
|
-
multiple
|
|
951
|
-
}), [
|
|
952
|
-
value,
|
|
953
|
-
onItemSelect,
|
|
954
|
-
multiple
|
|
955
|
-
]);
|
|
956
|
-
return /* @__PURE__ */ jsx(FacetedContext.Provider, {
|
|
957
|
-
value: contextValue,
|
|
958
|
-
children: /* @__PURE__ */ jsx(Popover, {
|
|
959
|
-
open,
|
|
960
|
-
onOpenChange,
|
|
961
|
-
...facetedProps,
|
|
962
|
-
children
|
|
963
|
-
})
|
|
964
|
-
});
|
|
965
|
-
}
|
|
966
|
-
function FacetedTrigger(props) {
|
|
967
|
-
const { className, children,...triggerProps } = props;
|
|
968
|
-
return /* @__PURE__ */ jsx(PopoverTrigger, {
|
|
969
|
-
...triggerProps,
|
|
970
|
-
className: cn("justify-between text-left", className),
|
|
971
|
-
children
|
|
972
|
-
});
|
|
973
|
-
}
|
|
974
|
-
function FacetedBadgeList(props) {
|
|
975
|
-
const { options = [], max = 2, placeholder = "Select options...", className, badgeClassName,...badgeListProps } = props;
|
|
976
|
-
const context = useFacetedContext("FacetedBadgeList");
|
|
977
|
-
const values = Array.isArray(context.value) ? context.value : [context.value].filter(Boolean);
|
|
978
|
-
const getLabel = React.useCallback((value) => {
|
|
979
|
-
return options.find((opt) => opt.value === value)?.label ?? value;
|
|
980
|
-
}, [options]);
|
|
981
|
-
if (!values || values.length === 0) return /* @__PURE__ */ jsxs("div", {
|
|
982
|
-
...badgeListProps,
|
|
983
|
-
className: "flex w-full items-center gap-1 text-muted-foreground",
|
|
984
|
-
children: [placeholder, /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ml-auto size-4 shrink-0 opacity-50" })]
|
|
985
|
-
});
|
|
986
|
-
return /* @__PURE__ */ jsx("div", {
|
|
987
|
-
...badgeListProps,
|
|
988
|
-
className: cn("flex flex-wrap items-center gap-1", className),
|
|
989
|
-
children: values.length > max ? /* @__PURE__ */ jsxs(Badge, {
|
|
990
|
-
variant: "secondary",
|
|
991
|
-
className: cn("rounded-sm px-1 font-normal", badgeClassName),
|
|
992
|
-
children: [values.length, " selected"]
|
|
993
|
-
}) : values.map((value) => /* @__PURE__ */ jsx(Badge, {
|
|
994
|
-
variant: "secondary",
|
|
995
|
-
className: cn("rounded-sm px-1 font-normal", badgeClassName),
|
|
996
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
997
|
-
className: "truncate",
|
|
998
|
-
children: getLabel(value)
|
|
999
|
-
})
|
|
1000
|
-
}, value))
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
function FacetedContent(props) {
|
|
1004
|
-
const { className, children,...contentProps } = props;
|
|
1005
|
-
return /* @__PURE__ */ jsx(PopoverContent, {
|
|
1006
|
-
...contentProps,
|
|
1007
|
-
align: "start",
|
|
1008
|
-
className: cn("w-[200px] origin-(--radix-popover-content-transform-origin) p-0", className),
|
|
1009
|
-
children: /* @__PURE__ */ jsx(Command, { children })
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
const FacetedInput = CommandInput;
|
|
1013
|
-
const FacetedList = CommandList;
|
|
1014
|
-
const FacetedEmpty = CommandEmpty;
|
|
1015
|
-
const FacetedGroup = CommandGroup;
|
|
1016
|
-
function FacetedItem(props) {
|
|
1017
|
-
const { value, onSelect, className, children,...itemProps } = props;
|
|
1018
|
-
const context = useFacetedContext("FacetedItem");
|
|
1019
|
-
const isSelected = context.multiple ? Array.isArray(context.value) && context.value.includes(value) : context.value === value;
|
|
1020
|
-
const onItemSelect = React.useCallback((currentValue) => {
|
|
1021
|
-
if (onSelect) onSelect(currentValue);
|
|
1022
|
-
else if (context.onItemSelect) context.onItemSelect(currentValue);
|
|
1023
|
-
}, [onSelect, context]);
|
|
1024
|
-
return /* @__PURE__ */ jsxs(CommandItem, {
|
|
1025
|
-
value,
|
|
1026
|
-
"aria-selected": isSelected,
|
|
1027
|
-
"data-selected": isSelected,
|
|
1028
|
-
className: cn("gap-2", className),
|
|
1029
|
-
onSelect: () => onItemSelect(value),
|
|
1030
|
-
...itemProps,
|
|
1031
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1032
|
-
className: cn("flex size-4 items-center justify-center rounded-sm border border-primary", isSelected ? "bg-primary text-primary-foreground" : "opacity-50 [&_svg]:invisible"),
|
|
1033
|
-
children: /* @__PURE__ */ jsx(Check, { className: "size-4" })
|
|
1034
|
-
}), children]
|
|
1035
|
-
});
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
//#endregion
|
|
1039
|
-
//#region src/lib/compose-refs.ts
|
|
1040
|
-
/**
|
|
1041
|
-
* Set a given ref to a given value
|
|
1042
|
-
* This utility takes care of different types of refs: callback refs and RefObject(s)
|
|
1043
|
-
*/
|
|
1044
|
-
function setRef(ref, value) {
|
|
1045
|
-
if (typeof ref === "function") return ref(value);
|
|
1046
|
-
if (ref !== null && ref !== void 0) ref.current = value;
|
|
1047
|
-
}
|
|
1048
|
-
/**
|
|
1049
|
-
* A utility to compose multiple refs together
|
|
1050
|
-
* Accepts callback refs and RefObject(s)
|
|
1051
|
-
*/
|
|
1052
|
-
function composeRefs(...refs) {
|
|
1053
|
-
return (node) => {
|
|
1054
|
-
let hasCleanup = false;
|
|
1055
|
-
const cleanups = refs.map((ref) => {
|
|
1056
|
-
const cleanup = setRef(ref, node);
|
|
1057
|
-
if (!hasCleanup && typeof cleanup === "function") hasCleanup = true;
|
|
1058
|
-
return cleanup;
|
|
1059
|
-
});
|
|
1060
|
-
if (hasCleanup) return () => {
|
|
1061
|
-
for (let i = 0; i < cleanups.length; i++) {
|
|
1062
|
-
const cleanup = cleanups[i];
|
|
1063
|
-
if (typeof cleanup === "function") cleanup();
|
|
1064
|
-
else setRef(refs[i], null);
|
|
1065
|
-
}
|
|
1066
|
-
};
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
/**
|
|
1070
|
-
* A custom hook that composes multiple refs
|
|
1071
|
-
* Accepts callback refs and RefObject(s)
|
|
1072
|
-
*/
|
|
1073
|
-
function useComposedRefs(...refs) {
|
|
1074
|
-
return React.useCallback(composeRefs(...refs), refs);
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
//#endregion
|
|
1078
|
-
//#region src/components/ui/sortable.tsx
|
|
1079
|
-
const orientationConfig = {
|
|
1080
|
-
vertical: {
|
|
1081
|
-
modifiers: [restrictToVerticalAxis, restrictToParentElement],
|
|
1082
|
-
strategy: verticalListSortingStrategy,
|
|
1083
|
-
collisionDetection: closestCenter
|
|
1084
|
-
},
|
|
1085
|
-
horizontal: {
|
|
1086
|
-
modifiers: [restrictToHorizontalAxis, restrictToParentElement],
|
|
1087
|
-
strategy: horizontalListSortingStrategy,
|
|
1088
|
-
collisionDetection: closestCenter
|
|
1089
|
-
},
|
|
1090
|
-
mixed: {
|
|
1091
|
-
modifiers: [restrictToParentElement],
|
|
1092
|
-
strategy: void 0,
|
|
1093
|
-
collisionDetection: closestCorners
|
|
1094
|
-
}
|
|
1095
|
-
};
|
|
1096
|
-
const ROOT_NAME$1 = "Sortable";
|
|
1097
|
-
const CONTENT_NAME = "SortableContent";
|
|
1098
|
-
const ITEM_NAME$1 = "SortableItem";
|
|
1099
|
-
const ITEM_HANDLE_NAME = "SortableItemHandle";
|
|
1100
|
-
const OVERLAY_NAME = "SortableOverlay";
|
|
1101
|
-
const SortableRootContext = React.createContext(null);
|
|
1102
|
-
function useSortableContext(consumerName) {
|
|
1103
|
-
const context = React.useContext(SortableRootContext);
|
|
1104
|
-
if (!context) throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME$1}\``);
|
|
1105
|
-
return context;
|
|
1106
|
-
}
|
|
1107
|
-
function SortableRoot(props) {
|
|
1108
|
-
const { value, onValueChange, collisionDetection, modifiers, strategy, onMove, orientation = "vertical", flatCursor = false, getItemValue: getItemValueProp, accessibility, onDragStart: onDragStartProp, onDragEnd: onDragEndProp, onDragCancel: onDragCancelProp,...sortableProps } = props;
|
|
1109
|
-
const id = React.useId();
|
|
1110
|
-
const [activeId, setActiveId] = React.useState(null);
|
|
1111
|
-
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
1112
|
-
const config = React.useMemo(() => orientationConfig[orientation], [orientation]);
|
|
1113
|
-
const getItemValue = React.useCallback((item) => {
|
|
1114
|
-
if (typeof item === "object" && !getItemValueProp) throw new Error("getItemValue is required when using array of objects");
|
|
1115
|
-
return getItemValueProp ? getItemValueProp(item) : item;
|
|
1116
|
-
}, [getItemValueProp]);
|
|
1117
|
-
const items = React.useMemo(() => {
|
|
1118
|
-
return value.map((item) => getItemValue(item));
|
|
1119
|
-
}, [value, getItemValue]);
|
|
1120
|
-
const onDragStart = React.useCallback((event) => {
|
|
1121
|
-
onDragStartProp?.(event);
|
|
1122
|
-
if (event.activatorEvent.defaultPrevented) return;
|
|
1123
|
-
setActiveId(event.active.id);
|
|
1124
|
-
}, [onDragStartProp]);
|
|
1125
|
-
const onDragEnd = React.useCallback((event) => {
|
|
1126
|
-
onDragEndProp?.(event);
|
|
1127
|
-
if (event.activatorEvent.defaultPrevented) return;
|
|
1128
|
-
const { active, over } = event;
|
|
1129
|
-
if (over && active.id !== over?.id) {
|
|
1130
|
-
const activeIndex = value.findIndex((item) => getItemValue(item) === active.id);
|
|
1131
|
-
const overIndex = value.findIndex((item) => getItemValue(item) === over.id);
|
|
1132
|
-
if (onMove) onMove({
|
|
1133
|
-
...event,
|
|
1134
|
-
activeIndex,
|
|
1135
|
-
overIndex
|
|
1136
|
-
});
|
|
1137
|
-
else onValueChange?.(arrayMove(value, activeIndex, overIndex));
|
|
1138
|
-
}
|
|
1139
|
-
setActiveId(null);
|
|
1140
|
-
}, [
|
|
1141
|
-
value,
|
|
1142
|
-
onValueChange,
|
|
1143
|
-
onMove,
|
|
1144
|
-
getItemValue,
|
|
1145
|
-
onDragEndProp
|
|
1146
|
-
]);
|
|
1147
|
-
const onDragCancel = React.useCallback((event) => {
|
|
1148
|
-
onDragCancelProp?.(event);
|
|
1149
|
-
if (event.activatorEvent.defaultPrevented) return;
|
|
1150
|
-
setActiveId(null);
|
|
1151
|
-
}, [onDragCancelProp]);
|
|
1152
|
-
const announcements = React.useMemo(() => ({
|
|
1153
|
-
onDragStart({ active }) {
|
|
1154
|
-
return `Grabbed sortable item "${active.id.toString()}". Current position is ${active.data.current?.sortable.index + 1} of ${value.length}. Use arrow keys to move, space to drop.`;
|
|
1155
|
-
},
|
|
1156
|
-
onDragOver({ active, over }) {
|
|
1157
|
-
if (over) {
|
|
1158
|
-
const overIndex = over.data.current?.sortable.index ?? 0;
|
|
1159
|
-
const moveDirection = overIndex > (active.data.current?.sortable.index ?? 0) ? "down" : "up";
|
|
1160
|
-
return `Sortable item "${active.id.toString()}" moved ${moveDirection} to position ${overIndex + 1} of ${value.length}.`;
|
|
1161
|
-
}
|
|
1162
|
-
return "Sortable item is no longer over a droppable area. Press escape to cancel.";
|
|
1163
|
-
},
|
|
1164
|
-
onDragEnd({ active, over }) {
|
|
1165
|
-
const activeValue = active.id.toString();
|
|
1166
|
-
if (over) return `Sortable item "${activeValue}" dropped at position ${(over.data.current?.sortable.index ?? 0) + 1} of ${value.length}.`;
|
|
1167
|
-
return `Sortable item "${activeValue}" dropped. No changes were made.`;
|
|
1168
|
-
},
|
|
1169
|
-
onDragCancel({ active }) {
|
|
1170
|
-
const activeIndex = active.data.current?.sortable.index ?? 0;
|
|
1171
|
-
return `Sorting cancelled. Sortable item "${active.id.toString()}" returned to position ${activeIndex + 1} of ${value.length}.`;
|
|
1172
|
-
},
|
|
1173
|
-
onDragMove({ active, over }) {
|
|
1174
|
-
if (over) {
|
|
1175
|
-
const overIndex = over.data.current?.sortable.index ?? 0;
|
|
1176
|
-
const moveDirection = overIndex > (active.data.current?.sortable.index ?? 0) ? "down" : "up";
|
|
1177
|
-
return `Sortable item "${active.id.toString()}" is moving ${moveDirection} to position ${overIndex + 1} of ${value.length}.`;
|
|
1178
|
-
}
|
|
1179
|
-
return "Sortable item is no longer over a droppable area. Press escape to cancel.";
|
|
1180
|
-
}
|
|
1181
|
-
}), [value]);
|
|
1182
|
-
const screenReaderInstructions = React.useMemo(() => ({ draggable: `
|
|
1183
|
-
To pick up a sortable item, press space or enter.
|
|
1184
|
-
While dragging, use the ${orientation === "vertical" ? "up and down" : orientation === "horizontal" ? "left and right" : "arrow"} keys to move the item.
|
|
1185
|
-
Press space or enter again to drop the item in its new position, or press escape to cancel.
|
|
1186
|
-
` }), [orientation]);
|
|
1187
|
-
const contextValue = React.useMemo(() => ({
|
|
1188
|
-
id,
|
|
1189
|
-
items,
|
|
1190
|
-
modifiers: modifiers ?? config.modifiers,
|
|
1191
|
-
strategy: strategy ?? config.strategy,
|
|
1192
|
-
activeId,
|
|
1193
|
-
setActiveId,
|
|
1194
|
-
getItemValue,
|
|
1195
|
-
flatCursor
|
|
1196
|
-
}), [
|
|
1197
|
-
id,
|
|
1198
|
-
items,
|
|
1199
|
-
modifiers,
|
|
1200
|
-
strategy,
|
|
1201
|
-
config.modifiers,
|
|
1202
|
-
config.strategy,
|
|
1203
|
-
activeId,
|
|
1204
|
-
getItemValue,
|
|
1205
|
-
flatCursor
|
|
1206
|
-
]);
|
|
1207
|
-
return /* @__PURE__ */ jsx(SortableRootContext.Provider, {
|
|
1208
|
-
value: contextValue,
|
|
1209
|
-
children: /* @__PURE__ */ jsx(DndContext, {
|
|
1210
|
-
collisionDetection: collisionDetection ?? config.collisionDetection,
|
|
1211
|
-
modifiers: modifiers ?? config.modifiers,
|
|
1212
|
-
sensors,
|
|
1213
|
-
...sortableProps,
|
|
1214
|
-
id,
|
|
1215
|
-
onDragStart,
|
|
1216
|
-
onDragEnd,
|
|
1217
|
-
onDragCancel,
|
|
1218
|
-
accessibility: {
|
|
1219
|
-
announcements,
|
|
1220
|
-
screenReaderInstructions,
|
|
1221
|
-
...accessibility
|
|
1222
|
-
}
|
|
1223
|
-
})
|
|
1224
|
-
});
|
|
1225
|
-
}
|
|
1226
|
-
const SortableContentContext = React.createContext(false);
|
|
1227
|
-
function SortableContent(props) {
|
|
1228
|
-
const { strategy: strategyProp, asChild, withoutSlot, children, ref,...contentProps } = props;
|
|
1229
|
-
const context = useSortableContext(CONTENT_NAME);
|
|
1230
|
-
const ContentPrimitive = asChild ? Slot : "div";
|
|
1231
|
-
return /* @__PURE__ */ jsx(SortableContentContext.Provider, {
|
|
1232
|
-
value: true,
|
|
1233
|
-
children: /* @__PURE__ */ jsx(SortableContext, {
|
|
1234
|
-
items: context.items,
|
|
1235
|
-
strategy: strategyProp ?? context.strategy,
|
|
1236
|
-
children: withoutSlot ? children : /* @__PURE__ */ jsx(ContentPrimitive, {
|
|
1237
|
-
"data-slot": "sortable-content",
|
|
1238
|
-
...contentProps,
|
|
1239
|
-
ref,
|
|
1240
|
-
children
|
|
1241
|
-
})
|
|
1242
|
-
})
|
|
1243
|
-
});
|
|
1244
|
-
}
|
|
1245
|
-
const SortableItemContext = React.createContext(null);
|
|
1246
|
-
function useSortableItemContext(consumerName) {
|
|
1247
|
-
const context = React.useContext(SortableItemContext);
|
|
1248
|
-
if (!context) throw new Error(`\`${consumerName}\` must be used within \`${ITEM_NAME$1}\``);
|
|
1249
|
-
return context;
|
|
1250
|
-
}
|
|
1251
|
-
function SortableItem(props) {
|
|
1252
|
-
const { value, style, asHandle, asChild, disabled, className, ref,...itemProps } = props;
|
|
1253
|
-
const inSortableContent = React.useContext(SortableContentContext);
|
|
1254
|
-
const inSortableOverlay = React.useContext(SortableOverlayContext);
|
|
1255
|
-
if (!inSortableContent && !inSortableOverlay) throw new Error(`\`${ITEM_NAME$1}\` must be used within \`${CONTENT_NAME}\` or \`${OVERLAY_NAME}\``);
|
|
1256
|
-
if (value === "") throw new Error(`\`${ITEM_NAME$1}\` value cannot be an empty string`);
|
|
1257
|
-
const context = useSortableContext(ITEM_NAME$1);
|
|
1258
|
-
const id = React.useId();
|
|
1259
|
-
const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
|
|
1260
|
-
id: value,
|
|
1261
|
-
disabled
|
|
1262
|
-
});
|
|
1263
|
-
const composedRef = useComposedRefs(ref, (node) => {
|
|
1264
|
-
if (disabled) return;
|
|
1265
|
-
setNodeRef(node);
|
|
1266
|
-
if (asHandle) setActivatorNodeRef(node);
|
|
1267
|
-
});
|
|
1268
|
-
const composedStyle = React.useMemo(() => {
|
|
1269
|
-
return {
|
|
1270
|
-
transform: CSS.Translate.toString(transform),
|
|
1271
|
-
transition,
|
|
1272
|
-
...style
|
|
1273
|
-
};
|
|
1274
|
-
}, [
|
|
1275
|
-
transform,
|
|
1276
|
-
transition,
|
|
1277
|
-
style
|
|
1278
|
-
]);
|
|
1279
|
-
const itemContext = React.useMemo(() => ({
|
|
1280
|
-
id,
|
|
1281
|
-
attributes,
|
|
1282
|
-
listeners,
|
|
1283
|
-
setActivatorNodeRef,
|
|
1284
|
-
isDragging,
|
|
1285
|
-
disabled
|
|
1286
|
-
}), [
|
|
1287
|
-
id,
|
|
1288
|
-
attributes,
|
|
1289
|
-
listeners,
|
|
1290
|
-
setActivatorNodeRef,
|
|
1291
|
-
isDragging,
|
|
1292
|
-
disabled
|
|
1293
|
-
]);
|
|
1294
|
-
const ItemPrimitive = asChild ? Slot : "div";
|
|
1295
|
-
return /* @__PURE__ */ jsx(SortableItemContext.Provider, {
|
|
1296
|
-
value: itemContext,
|
|
1297
|
-
children: /* @__PURE__ */ jsx(ItemPrimitive, {
|
|
1298
|
-
id,
|
|
1299
|
-
"data-disabled": disabled,
|
|
1300
|
-
"data-dragging": isDragging ? "" : void 0,
|
|
1301
|
-
"data-slot": "sortable-item",
|
|
1302
|
-
...itemProps,
|
|
1303
|
-
...asHandle && !disabled ? attributes : {},
|
|
1304
|
-
...asHandle && !disabled ? listeners : {},
|
|
1305
|
-
ref: composedRef,
|
|
1306
|
-
style: composedStyle,
|
|
1307
|
-
className: cn("focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1", {
|
|
1308
|
-
"touch-none select-none": asHandle,
|
|
1309
|
-
"cursor-default": context.flatCursor,
|
|
1310
|
-
"data-dragging:cursor-grabbing": !context.flatCursor,
|
|
1311
|
-
"cursor-grab": !isDragging && asHandle && !context.flatCursor,
|
|
1312
|
-
"opacity-50": isDragging,
|
|
1313
|
-
"pointer-events-none opacity-50": disabled
|
|
1314
|
-
}, className)
|
|
1315
|
-
})
|
|
1316
|
-
});
|
|
1317
|
-
}
|
|
1318
|
-
function SortableItemHandle(props) {
|
|
1319
|
-
const { asChild, disabled, className, ref,...itemHandleProps } = props;
|
|
1320
|
-
const context = useSortableContext(ITEM_HANDLE_NAME);
|
|
1321
|
-
const itemContext = useSortableItemContext(ITEM_HANDLE_NAME);
|
|
1322
|
-
const isDisabled = disabled ?? itemContext.disabled;
|
|
1323
|
-
const composedRef = useComposedRefs(ref, (node) => {
|
|
1324
|
-
if (!isDisabled) return;
|
|
1325
|
-
itemContext.setActivatorNodeRef(node);
|
|
1326
|
-
});
|
|
1327
|
-
return /* @__PURE__ */ jsx(asChild ? Slot : "button", {
|
|
1328
|
-
type: "button",
|
|
1329
|
-
"aria-controls": itemContext.id,
|
|
1330
|
-
"data-disabled": isDisabled,
|
|
1331
|
-
"data-dragging": itemContext.isDragging ? "" : void 0,
|
|
1332
|
-
"data-slot": "sortable-item-handle",
|
|
1333
|
-
...itemHandleProps,
|
|
1334
|
-
...isDisabled ? {} : itemContext.attributes,
|
|
1335
|
-
...isDisabled ? {} : itemContext.listeners,
|
|
1336
|
-
ref: composedRef,
|
|
1337
|
-
className: cn("select-none disabled:pointer-events-none disabled:opacity-50", context.flatCursor ? "cursor-default" : "cursor-grab data-dragging:cursor-grabbing", className),
|
|
1338
|
-
disabled: isDisabled
|
|
1339
|
-
});
|
|
1340
|
-
}
|
|
1341
|
-
const SortableOverlayContext = React.createContext(false);
|
|
1342
|
-
const dropAnimation = { sideEffects: defaultDropAnimationSideEffects({ styles: { active: { opacity: "0.4" } } }) };
|
|
1343
|
-
function SortableOverlay(props) {
|
|
1344
|
-
const { container: containerProp, children,...overlayProps } = props;
|
|
1345
|
-
const context = useSortableContext(OVERLAY_NAME);
|
|
1346
|
-
const [mounted, setMounted] = React.useState(false);
|
|
1347
|
-
React.useLayoutEffect(() => setMounted(true), []);
|
|
1348
|
-
const container = containerProp ?? (mounted ? globalThis.document?.body : null);
|
|
1349
|
-
if (!container) return null;
|
|
1350
|
-
return ReactDOM.createPortal(/* @__PURE__ */ jsx(DragOverlay, {
|
|
1351
|
-
dropAnimation,
|
|
1352
|
-
modifiers: context.modifiers,
|
|
1353
|
-
className: cn(!context.flatCursor && "cursor-grabbing"),
|
|
1354
|
-
...overlayProps,
|
|
1355
|
-
children: /* @__PURE__ */ jsx(SortableOverlayContext.Provider, {
|
|
1356
|
-
value: true,
|
|
1357
|
-
children: context.activeId ? typeof children === "function" ? children({ value: context.activeId }) : children : null
|
|
1358
|
-
})
|
|
1359
|
-
}), container);
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
859
|
//#endregion
|
|
1363
860
|
//#region src/hooks/use-callback-ref.ts
|
|
1364
861
|
/**
|
|
@@ -1475,7 +972,7 @@ function DataTableFilterList({ table, debounceMs = DEBOUNCE_MS$2, throttleMs = T
|
|
|
1475
972
|
window.addEventListener("keydown", onKeyDown);
|
|
1476
973
|
return () => window.removeEventListener("keydown", onKeyDown);
|
|
1477
974
|
}, []);
|
|
1478
|
-
return /* @__PURE__ */ jsxs(
|
|
975
|
+
return /* @__PURE__ */ jsxs(Sortable, {
|
|
1479
976
|
value: filters,
|
|
1480
977
|
onValueChange: setFilters,
|
|
1481
978
|
getItemValue: (item) => item.filterId,
|
|
@@ -1528,9 +1025,9 @@ function DataTableFilterList({ table, debounceMs = DEBOUNCE_MS$2, throttleMs = T
|
|
|
1528
1025
|
children: /* @__PURE__ */ jsx("div", {
|
|
1529
1026
|
role: "list",
|
|
1530
1027
|
className: "flex max-h-[300px] flex-col gap-2 overflow-y-auto p-1",
|
|
1531
|
-
children: filters.map((filter, index) => /* @__PURE__ */ jsx(DataTableFilterItem$1, {
|
|
1028
|
+
children: filters.map((filter, index$1) => /* @__PURE__ */ jsx(DataTableFilterItem$1, {
|
|
1532
1029
|
filter,
|
|
1533
|
-
index,
|
|
1030
|
+
index: index$1,
|
|
1534
1031
|
filterItemId: `${id}-filter-${filter.filterId}`,
|
|
1535
1032
|
joinOperator,
|
|
1536
1033
|
setJoinOperator,
|
|
@@ -1571,7 +1068,7 @@ function DataTableFilterList({ table, debounceMs = DEBOUNCE_MS$2, throttleMs = T
|
|
|
1571
1068
|
}) })]
|
|
1572
1069
|
});
|
|
1573
1070
|
}
|
|
1574
|
-
function DataTableFilterItem$1({ filter, index, filterItemId, joinOperator, setJoinOperator, columns, onFilterUpdate, onFilterRemove }) {
|
|
1071
|
+
function DataTableFilterItem$1({ filter, index: index$1, filterItemId, joinOperator, setJoinOperator, columns, onFilterUpdate, onFilterRemove }) {
|
|
1575
1072
|
const [showFieldSelector, setShowFieldSelector] = React.useState(false);
|
|
1576
1073
|
const [showOperatorSelector, setShowOperatorSelector] = React.useState(false);
|
|
1577
1074
|
const [showValueSelector, setShowValueSelector] = React.useState(false);
|
|
@@ -1609,10 +1106,10 @@ function DataTableFilterItem$1({ filter, index, filterItemId, joinOperator, setJ
|
|
|
1609
1106
|
children: [
|
|
1610
1107
|
/* @__PURE__ */ jsx("div", {
|
|
1611
1108
|
className: "min-w-[72px] text-center",
|
|
1612
|
-
children: index === 0 ? /* @__PURE__ */ jsx("span", {
|
|
1109
|
+
children: index$1 === 0 ? /* @__PURE__ */ jsx("span", {
|
|
1613
1110
|
className: "text-muted-foreground text-sm",
|
|
1614
1111
|
children: "Where"
|
|
1615
|
-
}) : index === 1 ? /* @__PURE__ */ jsxs(Select, {
|
|
1112
|
+
}) : index$1 === 1 ? /* @__PURE__ */ jsxs(Select, {
|
|
1616
1113
|
value: joinOperator,
|
|
1617
1114
|
onValueChange: (value) => setJoinOperator(value),
|
|
1618
1115
|
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
@@ -1791,6 +1288,7 @@ function onFilterInputRender$1({ filter, inputId, column, columnMeta, onFilterUp
|
|
|
1791
1288
|
case "select":
|
|
1792
1289
|
case "multiSelect": {
|
|
1793
1290
|
const inputListboxId = `${inputId}-listbox`;
|
|
1291
|
+
const optionList = columnMeta?.autoCrudFilterOptions ?? columnMeta?.options;
|
|
1794
1292
|
const multiple = filter.variant === "multiSelect";
|
|
1795
1293
|
return /* @__PURE__ */ jsxs(Faceted, {
|
|
1796
1294
|
open: showValueSelector,
|
|
@@ -1817,20 +1315,41 @@ function onFilterInputRender$1({ filter, inputId, column, columnMeta, onFilterUp
|
|
|
1817
1315
|
}), /* @__PURE__ */ jsxs(FacetedContent, {
|
|
1818
1316
|
id: inputListboxId,
|
|
1819
1317
|
className: "w-[200px]",
|
|
1318
|
+
commandProps: { shouldFilter: columnMeta?.autoCrudFilterShouldFilter },
|
|
1820
1319
|
children: [/* @__PURE__ */ jsx(FacetedInput, {
|
|
1821
1320
|
"aria-label": `Search ${columnMeta?.label} options`,
|
|
1822
|
-
placeholder: columnMeta?.placeholder ?? "Search options..."
|
|
1823
|
-
|
|
1824
|
-
|
|
1321
|
+
placeholder: columnMeta?.placeholder ?? "Search options...",
|
|
1322
|
+
value: columnMeta?.autoCrudFilterSearchValue,
|
|
1323
|
+
onValueChange: columnMeta?.autoCrudFilterOnSearch
|
|
1324
|
+
}), /* @__PURE__ */ jsxs(FacetedList, {
|
|
1325
|
+
onScroll: columnMeta?.autoCrudFilterOnPopupScroll,
|
|
1825
1326
|
children: [
|
|
1826
|
-
|
|
1827
|
-
/* @__PURE__ */ jsx(
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1327
|
+
/* @__PURE__ */ jsx(FacetedEmpty, { children: "No options found." }),
|
|
1328
|
+
/* @__PURE__ */ jsx(FacetedGroup, { children: optionList?.map((option) => /* @__PURE__ */ jsxs(FacetedItem, {
|
|
1329
|
+
value: option.value,
|
|
1330
|
+
children: [
|
|
1331
|
+
option.icon && /* @__PURE__ */ jsx(option.icon, {}),
|
|
1332
|
+
/* @__PURE__ */ jsx("span", { children: option.label }),
|
|
1333
|
+
option.count && /* @__PURE__ */ jsx("span", {
|
|
1334
|
+
className: "ml-auto font-mono text-xs",
|
|
1335
|
+
children: option.count
|
|
1336
|
+
})
|
|
1337
|
+
]
|
|
1338
|
+
}, option.value)) }),
|
|
1339
|
+
columnMeta?.autoCrudFilterLoading && /* @__PURE__ */ jsx(FacetedGroup, { children: /* @__PURE__ */ jsx("div", {
|
|
1340
|
+
role: "status",
|
|
1341
|
+
"aria-live": "polite",
|
|
1342
|
+
className: "py-2 text-center text-sm text-muted-foreground",
|
|
1343
|
+
children: "Loading..."
|
|
1344
|
+
}) }),
|
|
1345
|
+
!columnMeta?.autoCrudFilterLoading && columnMeta?.autoCrudFilterHasMore && /* @__PURE__ */ jsx("div", {
|
|
1346
|
+
role: "status",
|
|
1347
|
+
"aria-live": "polite",
|
|
1348
|
+
className: "sr-only",
|
|
1349
|
+
children: "Load more"
|
|
1831
1350
|
})
|
|
1832
1351
|
]
|
|
1833
|
-
}
|
|
1352
|
+
})]
|
|
1834
1353
|
})]
|
|
1835
1354
|
});
|
|
1836
1355
|
}
|
|
@@ -1909,6 +1428,9 @@ function getOptionCommandValue(option) {
|
|
|
1909
1428
|
option.searchText
|
|
1910
1429
|
].filter(Boolean).join(" ");
|
|
1911
1430
|
}
|
|
1431
|
+
function getColumnFilterOptions(column) {
|
|
1432
|
+
return column.columnDef.meta?.autoCrudFilterOptions ?? column.columnDef.meta?.options;
|
|
1433
|
+
}
|
|
1912
1434
|
function DataTableFilterMenu({ table, debounceMs = DEBOUNCE_MS$1, throttleMs = THROTTLE_MS$1, shallow = true, disabled,...props }) {
|
|
1913
1435
|
const id = React.useId();
|
|
1914
1436
|
const columnDefs = table.options.columns;
|
|
@@ -1994,6 +1516,12 @@ function DataTableFilterMenu({ table, debounceMs = DEBOUNCE_MS$1, throttleMs = T
|
|
|
1994
1516
|
onFilterRemove(filters[filters.length - 1]?.filterId ?? "");
|
|
1995
1517
|
}
|
|
1996
1518
|
}, [filters, onFilterRemove]);
|
|
1519
|
+
const selectedColumnMeta = selectedColumn?.columnDef.meta;
|
|
1520
|
+
const commandInputValue = selectedColumnMeta?.autoCrudFilterOnSearch !== void 0 ? selectedColumnMeta.autoCrudFilterSearchValue ?? "" : inputValue;
|
|
1521
|
+
const onCommandInputValueChange = React.useCallback((value) => {
|
|
1522
|
+
setInputValue(value);
|
|
1523
|
+
selectedColumn?.columnDef.meta?.autoCrudFilterOnSearch?.(value);
|
|
1524
|
+
}, [selectedColumn]);
|
|
1997
1525
|
return /* @__PURE__ */ jsxs("div", {
|
|
1998
1526
|
role: "list",
|
|
1999
1527
|
className: "flex flex-wrap items-center gap-2",
|
|
@@ -2033,31 +1561,50 @@ function DataTableFilterMenu({ table, debounceMs = DEBOUNCE_MS$1, throttleMs = T
|
|
|
2033
1561
|
...props,
|
|
2034
1562
|
children: /* @__PURE__ */ jsxs(Command, {
|
|
2035
1563
|
loop: true,
|
|
1564
|
+
shouldFilter: selectedColumnMeta?.autoCrudFilterShouldFilter,
|
|
2036
1565
|
className: "[&_[cmdk-input-wrapper]_svg]:hidden",
|
|
2037
1566
|
children: [/* @__PURE__ */ jsx(CommandInput, {
|
|
2038
1567
|
ref: inputRef,
|
|
2039
1568
|
placeholder: selectedColumn ? selectedColumn.columnDef.meta?.label ?? selectedColumn.id : "Search fields...",
|
|
2040
|
-
value:
|
|
2041
|
-
onValueChange:
|
|
1569
|
+
value: commandInputValue,
|
|
1570
|
+
onValueChange: onCommandInputValueChange,
|
|
2042
1571
|
onKeyDown: onInputKeyDown
|
|
2043
|
-
}), /* @__PURE__ */ jsx(CommandList, {
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
1572
|
+
}), /* @__PURE__ */ jsx(CommandList, {
|
|
1573
|
+
onScroll: selectedColumnMeta?.autoCrudFilterOnPopupScroll,
|
|
1574
|
+
children: selectedColumn ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1575
|
+
selectedColumn.columnDef.meta?.options && /* @__PURE__ */ jsx(CommandEmpty, { children: "No options found." }),
|
|
1576
|
+
/* @__PURE__ */ jsx(FilterValueSelector, {
|
|
1577
|
+
column: selectedColumn,
|
|
1578
|
+
value: inputValue,
|
|
1579
|
+
onSelect: (value) => onFilterAdd(selectedColumn, value)
|
|
1580
|
+
}),
|
|
1581
|
+
selectedColumnMeta?.autoCrudFilterLoading && /* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsx("div", {
|
|
1582
|
+
role: "status",
|
|
1583
|
+
"aria-live": "polite",
|
|
1584
|
+
className: "py-2 text-center text-sm text-muted-foreground",
|
|
1585
|
+
children: "Loading..."
|
|
1586
|
+
}) }),
|
|
1587
|
+
!selectedColumnMeta?.autoCrudFilterLoading && selectedColumnMeta?.autoCrudFilterHasMore && /* @__PURE__ */ jsx("div", {
|
|
1588
|
+
role: "status",
|
|
1589
|
+
"aria-live": "polite",
|
|
1590
|
+
className: "sr-only",
|
|
1591
|
+
children: "Load more"
|
|
1592
|
+
})
|
|
1593
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(CommandEmpty, { children: "No fields found." }), /* @__PURE__ */ jsx(CommandGroup, { children: columns.map((column) => /* @__PURE__ */ jsxs(CommandItem, {
|
|
1594
|
+
value: column.id,
|
|
1595
|
+
onSelect: () => {
|
|
1596
|
+
setSelectedColumn(column);
|
|
1597
|
+
setInputValue("");
|
|
1598
|
+
requestAnimationFrame(() => {
|
|
1599
|
+
inputRef.current?.focus();
|
|
1600
|
+
});
|
|
1601
|
+
},
|
|
1602
|
+
children: [column.columnDef.meta?.icon && /* @__PURE__ */ jsx(column.columnDef.meta.icon, {}), /* @__PURE__ */ jsx("span", {
|
|
1603
|
+
className: "truncate",
|
|
1604
|
+
children: column.columnDef.meta?.label ?? column.id
|
|
1605
|
+
})]
|
|
1606
|
+
}, column.id)) })] })
|
|
1607
|
+
})]
|
|
2061
1608
|
})
|
|
2062
1609
|
})]
|
|
2063
1610
|
})
|
|
@@ -2187,7 +1734,7 @@ function FilterValueSelector({ column, value, onSelect }) {
|
|
|
2187
1734
|
children: "False"
|
|
2188
1735
|
})] });
|
|
2189
1736
|
case "select":
|
|
2190
|
-
case "multiSelect": return /* @__PURE__ */ jsx(CommandGroup, { children: column
|
|
1737
|
+
case "multiSelect": return /* @__PURE__ */ jsx(CommandGroup, { children: getColumnFilterOptions(column)?.map((option) => /* @__PURE__ */ jsxs(CommandItem, {
|
|
2191
1738
|
value: getOptionCommandValue(option),
|
|
2192
1739
|
onSelect: () => onSelect(option.value),
|
|
2193
1740
|
children: [
|
|
@@ -2286,6 +1833,7 @@ function onFilterInputRender({ filter, column, inputId, onFilterUpdate, showValu
|
|
|
2286
1833
|
case "multiSelect": {
|
|
2287
1834
|
const inputListboxId = `${inputId}-listbox`;
|
|
2288
1835
|
const options = column.columnDef.meta?.options ?? [];
|
|
1836
|
+
const optionList = getColumnFilterOptions(column) ?? [];
|
|
2289
1837
|
const selectedValues = Array.isArray(filter.value) ? filter.value : [filter.value];
|
|
2290
1838
|
const selectedOptions = options.filter((option) => selectedValues.includes(option.value));
|
|
2291
1839
|
return /* @__PURE__ */ jsxs(Popover, {
|
|
@@ -2314,21 +1862,46 @@ function onFilterInputRender({ filter, column, inputId, onFilterUpdate, showValu
|
|
|
2314
1862
|
id: inputListboxId,
|
|
2315
1863
|
align: "start",
|
|
2316
1864
|
className: "w-48 p-0",
|
|
2317
|
-
children: /* @__PURE__ */ jsxs(Command, {
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
children: option
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
1865
|
+
children: /* @__PURE__ */ jsxs(Command, {
|
|
1866
|
+
shouldFilter: column.columnDef.meta?.autoCrudFilterShouldFilter,
|
|
1867
|
+
children: [/* @__PURE__ */ jsx(CommandInput, {
|
|
1868
|
+
placeholder: "Search options...",
|
|
1869
|
+
value: column.columnDef.meta?.autoCrudFilterSearchValue,
|
|
1870
|
+
onValueChange: column.columnDef.meta?.autoCrudFilterOnSearch
|
|
1871
|
+
}), /* @__PURE__ */ jsxs(CommandList, {
|
|
1872
|
+
onScroll: column.columnDef.meta?.autoCrudFilterOnPopupScroll,
|
|
1873
|
+
children: [
|
|
1874
|
+
/* @__PURE__ */ jsx(CommandEmpty, { children: "No options found." }),
|
|
1875
|
+
/* @__PURE__ */ jsx(CommandGroup, { children: optionList.map((option) => /* @__PURE__ */ jsxs(CommandItem, {
|
|
1876
|
+
value: getOptionCommandValue(option),
|
|
1877
|
+
onSelect: () => {
|
|
1878
|
+
const value = filter.variant === "multiSelect" ? selectedValues.includes(option.value) ? selectedValues.filter((v) => v !== option.value) : [...selectedValues, option.value] : option.value;
|
|
1879
|
+
onFilterUpdate(filter.filterId, { value });
|
|
1880
|
+
},
|
|
1881
|
+
children: [
|
|
1882
|
+
option.icon && /* @__PURE__ */ jsx(option.icon, {}),
|
|
1883
|
+
/* @__PURE__ */ jsx("span", {
|
|
1884
|
+
className: "truncate",
|
|
1885
|
+
children: option.label
|
|
1886
|
+
}),
|
|
1887
|
+
filter.variant === "multiSelect" && /* @__PURE__ */ jsx(Check, { className: cn("ml-auto", selectedValues.includes(option.value) ? "opacity-100" : "opacity-0") })
|
|
1888
|
+
]
|
|
1889
|
+
}, option.value)) }),
|
|
1890
|
+
column.columnDef.meta?.autoCrudFilterLoading && /* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsx("div", {
|
|
1891
|
+
role: "status",
|
|
1892
|
+
"aria-live": "polite",
|
|
1893
|
+
className: "py-2 text-center text-sm text-muted-foreground",
|
|
1894
|
+
children: "Loading..."
|
|
1895
|
+
}) }),
|
|
1896
|
+
!column.columnDef.meta?.autoCrudFilterLoading && column.columnDef.meta?.autoCrudFilterHasMore && /* @__PURE__ */ jsx("div", {
|
|
1897
|
+
role: "status",
|
|
1898
|
+
"aria-live": "polite",
|
|
1899
|
+
className: "sr-only",
|
|
1900
|
+
children: "Load more"
|
|
1901
|
+
})
|
|
1902
|
+
]
|
|
1903
|
+
})]
|
|
1904
|
+
})
|
|
2332
1905
|
})]
|
|
2333
1906
|
});
|
|
2334
1907
|
}
|
|
@@ -2452,7 +2025,7 @@ function DataTableSortList({ table, disabled,...props }) {
|
|
|
2452
2025
|
window.addEventListener("keydown", onKeyDown);
|
|
2453
2026
|
return () => window.removeEventListener("keydown", onKeyDown);
|
|
2454
2027
|
}, []);
|
|
2455
|
-
return /* @__PURE__ */ jsxs(
|
|
2028
|
+
return /* @__PURE__ */ jsxs(Sortable, {
|
|
2456
2029
|
value: sorting,
|
|
2457
2030
|
onValueChange: onSortingChange,
|
|
2458
2031
|
getItemValue: (item) => item.id,
|
|
@@ -2672,207 +2245,12 @@ function DataTableViewOptions({ table, disabled,...props }) {
|
|
|
2672
2245
|
})] });
|
|
2673
2246
|
}
|
|
2674
2247
|
|
|
2675
|
-
//#endregion
|
|
2676
|
-
//#region src/components/ui/multi-combobox.tsx
|
|
2677
|
-
const DEFAULT_OPTIONS = [];
|
|
2678
|
-
const SCROLLABLE_COMMAND_GROUP_STYLE = {
|
|
2679
|
-
maxHeight: "300px",
|
|
2680
|
-
overscrollBehavior: "contain",
|
|
2681
|
-
overflowX: "hidden",
|
|
2682
|
-
overflowY: "auto"
|
|
2683
|
-
};
|
|
2684
|
-
function getWheelDeltaY(event) {
|
|
2685
|
-
if (event.deltaMode === 1) return event.deltaY * 16;
|
|
2686
|
-
if (event.deltaMode === 2) return event.deltaY * event.currentTarget.clientHeight;
|
|
2687
|
-
return event.deltaY;
|
|
2688
|
-
}
|
|
2689
|
-
function handleScrollableCommandGroupWheel(event) {
|
|
2690
|
-
const target = event.currentTarget;
|
|
2691
|
-
if (target.scrollHeight <= target.clientHeight) return;
|
|
2692
|
-
const maxScrollTop = target.scrollHeight - target.clientHeight;
|
|
2693
|
-
const nextScrollTop = Math.max(0, Math.min(target.scrollTop + getWheelDeltaY(event), maxScrollTop));
|
|
2694
|
-
if (nextScrollTop === target.scrollTop) return;
|
|
2695
|
-
event.preventDefault();
|
|
2696
|
-
event.stopPropagation();
|
|
2697
|
-
target.scrollTop = nextScrollTop;
|
|
2698
|
-
}
|
|
2699
|
-
function collectSearchText(...parts) {
|
|
2700
|
-
const texts = [];
|
|
2701
|
-
for (const part of parts) {
|
|
2702
|
-
if (!part) continue;
|
|
2703
|
-
if (Array.isArray(part)) texts.push(...part.map(String));
|
|
2704
|
-
else if (typeof part === "object") texts.push(...Object.values(part).filter(Boolean).map(String));
|
|
2705
|
-
else texts.push(String(part));
|
|
2706
|
-
}
|
|
2707
|
-
return texts.join(" ");
|
|
2708
|
-
}
|
|
2709
|
-
function getOptionKeywords(option) {
|
|
2710
|
-
return collectSearchText(typeof option.label === "string" || typeof option.label === "number" ? option.label : void 0, option.searchText, option.keywords).split(/\s+/).filter(Boolean);
|
|
2711
|
-
}
|
|
2712
|
-
function getOptionText(option) {
|
|
2713
|
-
if (typeof option.label === "string" || typeof option.label === "number") return String(option.label);
|
|
2714
|
-
return option.value;
|
|
2715
|
-
}
|
|
2716
|
-
function createNextValue(currentValue, optionValue, isSelected, selectionMode) {
|
|
2717
|
-
if (selectionMode === "single") return isSelected ? [] : [optionValue];
|
|
2718
|
-
return isSelected ? currentValue.filter((value) => value !== optionValue) : [...currentValue, optionValue];
|
|
2719
|
-
}
|
|
2720
|
-
function DefaultMultiComboboxTrigger({ open, selectedOptions, selectedValues, clearSelection, disabled, readOnly, placeholder, selectedText, className,...buttonProps }) {
|
|
2721
|
-
return /* @__PURE__ */ jsxs(Button, {
|
|
2722
|
-
...buttonProps,
|
|
2723
|
-
type: "button",
|
|
2724
|
-
variant: "outline",
|
|
2725
|
-
role: "combobox",
|
|
2726
|
-
"aria-expanded": open,
|
|
2727
|
-
disabled,
|
|
2728
|
-
className: cn$1("w-full justify-between", className),
|
|
2729
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
2730
|
-
className: "flex min-w-0 flex-1 items-center gap-1 overflow-hidden",
|
|
2731
|
-
children: selectedValues.length === 0 ? /* @__PURE__ */ jsx("span", {
|
|
2732
|
-
className: "truncate text-muted-foreground",
|
|
2733
|
-
children: placeholder
|
|
2734
|
-
}) : selectedOptions.length <= 2 ? selectedOptions.map((option) => /* @__PURE__ */ jsx(Badge, {
|
|
2735
|
-
variant: "secondary",
|
|
2736
|
-
className: "max-w-[8rem] rounded-sm px-1 font-normal",
|
|
2737
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
2738
|
-
className: "truncate",
|
|
2739
|
-
children: option.label
|
|
2740
|
-
})
|
|
2741
|
-
}, option.value)) : /* @__PURE__ */ jsxs(Badge, {
|
|
2742
|
-
variant: "secondary",
|
|
2743
|
-
className: "rounded-sm px-1 font-normal",
|
|
2744
|
-
children: [
|
|
2745
|
-
selectedValues.length,
|
|
2746
|
-
" ",
|
|
2747
|
-
selectedText
|
|
2748
|
-
]
|
|
2749
|
-
})
|
|
2750
|
-
}), /* @__PURE__ */ jsxs("span", {
|
|
2751
|
-
className: "ml-2 flex shrink-0 items-center gap-1",
|
|
2752
|
-
children: [selectedValues.length > 0 && !readOnly && /* @__PURE__ */ jsx("span", {
|
|
2753
|
-
role: "button",
|
|
2754
|
-
"aria-label": "Clear selection",
|
|
2755
|
-
tabIndex: 0,
|
|
2756
|
-
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",
|
|
2757
|
-
onClick: clearSelection,
|
|
2758
|
-
onKeyDown: (event) => {
|
|
2759
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
2760
|
-
event.preventDefault();
|
|
2761
|
-
clearSelection(event);
|
|
2762
|
-
}
|
|
2763
|
-
},
|
|
2764
|
-
children: /* @__PURE__ */ jsx(X, { className: "size-3" })
|
|
2765
|
-
}), /* @__PURE__ */ jsx(ChevronsUpDown, { className: "h-4 w-4 opacity-50" })]
|
|
2766
|
-
})]
|
|
2767
|
-
});
|
|
2768
|
-
}
|
|
2769
|
-
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 }) => {
|
|
2770
|
-
const [open, setOpen] = React.useState(false);
|
|
2771
|
-
const [internalValue, setInternalValue] = React.useState(defaultValue ?? []);
|
|
2772
|
-
const selectedValues = value ?? internalValue;
|
|
2773
|
-
const selectedValueSet = React.useMemo(() => new Set(selectedValues), [selectedValues]);
|
|
2774
|
-
const selectedOptions = React.useMemo(() => options.filter((option) => selectedValueSet.has(option.value)), [options, selectedValueSet]);
|
|
2775
|
-
const commitChange = React.useCallback((nextValue) => {
|
|
2776
|
-
if (value === void 0) setInternalValue(nextValue);
|
|
2777
|
-
onChange?.(nextValue);
|
|
2778
|
-
}, [onChange, value]);
|
|
2779
|
-
const clearSelection = React.useCallback((event) => {
|
|
2780
|
-
event?.preventDefault();
|
|
2781
|
-
event?.stopPropagation();
|
|
2782
|
-
if (readOnly) return;
|
|
2783
|
-
commitChange([]);
|
|
2784
|
-
}, [commitChange, readOnly]);
|
|
2785
|
-
const onItemSelect = React.useCallback((option) => {
|
|
2786
|
-
if (option.disabled || readOnly) return;
|
|
2787
|
-
const isSelected = selectedValueSet.has(option.value);
|
|
2788
|
-
commitChange(createNextValue(selectedValues, option.value, isSelected, selectionMode));
|
|
2789
|
-
if (selectionMode === "single") setOpen(false);
|
|
2790
|
-
}, [
|
|
2791
|
-
commitChange,
|
|
2792
|
-
readOnly,
|
|
2793
|
-
selectedValues,
|
|
2794
|
-
selectedValueSet,
|
|
2795
|
-
selectionMode
|
|
2796
|
-
]);
|
|
2797
|
-
return /* @__PURE__ */ jsxs(Popover, {
|
|
2798
|
-
open,
|
|
2799
|
-
onOpenChange: setOpen,
|
|
2800
|
-
children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
2801
|
-
asChild: true,
|
|
2802
|
-
children: renderTrigger ? renderTrigger({
|
|
2803
|
-
open,
|
|
2804
|
-
selectedOptions,
|
|
2805
|
-
selectedValues,
|
|
2806
|
-
clearSelection,
|
|
2807
|
-
disabled,
|
|
2808
|
-
readOnly
|
|
2809
|
-
}) : /* @__PURE__ */ jsx(DefaultMultiComboboxTrigger, {
|
|
2810
|
-
open,
|
|
2811
|
-
selectedOptions,
|
|
2812
|
-
selectedValues,
|
|
2813
|
-
clearSelection,
|
|
2814
|
-
disabled,
|
|
2815
|
-
readOnly,
|
|
2816
|
-
placeholder,
|
|
2817
|
-
selectedText,
|
|
2818
|
-
className: triggerClassName
|
|
2819
|
-
})
|
|
2820
|
-
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
2821
|
-
className: cn$1("w-full p-0", contentClassName),
|
|
2822
|
-
style: matchTriggerWidth ? { width: "var(--radix-popover-trigger-width)" } : void 0,
|
|
2823
|
-
children: /* @__PURE__ */ jsxs(Command, {
|
|
2824
|
-
className,
|
|
2825
|
-
filter,
|
|
2826
|
-
...commandProps,
|
|
2827
|
-
children: [/* @__PURE__ */ jsx(CommandInput, { placeholder: searchPlaceholder }), /* @__PURE__ */ jsxs(CommandList, {
|
|
2828
|
-
className: "max-h-full",
|
|
2829
|
-
children: [
|
|
2830
|
-
/* @__PURE__ */ jsx(CommandEmpty, { children: emptyText }),
|
|
2831
|
-
/* @__PURE__ */ jsx(CommandGroup, {
|
|
2832
|
-
className: "max-h-[300px] scroll-py-1 overflow-y-auto overflow-x-hidden",
|
|
2833
|
-
onWheelCapture: handleScrollableCommandGroupWheel,
|
|
2834
|
-
style: SCROLLABLE_COMMAND_GROUP_STYLE,
|
|
2835
|
-
children: options.map((option) => {
|
|
2836
|
-
const isSelected = selectedValueSet.has(option.value);
|
|
2837
|
-
const Icon = option.icon;
|
|
2838
|
-
return /* @__PURE__ */ jsxs(CommandItem, {
|
|
2839
|
-
value: option.value,
|
|
2840
|
-
keywords: getOptionKeywords(option),
|
|
2841
|
-
disabled: option.disabled,
|
|
2842
|
-
onSelect: () => onItemSelect(option),
|
|
2843
|
-
children: [
|
|
2844
|
-
/* @__PURE__ */ jsx("div", {
|
|
2845
|
-
className: cn$1("flex size-4 items-center justify-center rounded-sm border border-primary", isSelected ? "bg-primary text-primary-foreground" : "opacity-50 [&_svg]:invisible"),
|
|
2846
|
-
children: /* @__PURE__ */ jsx(Check, {})
|
|
2847
|
-
}),
|
|
2848
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4" }),
|
|
2849
|
-
/* @__PURE__ */ jsx("span", {
|
|
2850
|
-
className: "truncate",
|
|
2851
|
-
children: getOptionText(option)
|
|
2852
|
-
}),
|
|
2853
|
-
option.count !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
2854
|
-
className: "ml-auto font-mono text-xs",
|
|
2855
|
-
children: option.count
|
|
2856
|
-
})
|
|
2857
|
-
]
|
|
2858
|
-
}, option.value);
|
|
2859
|
-
})
|
|
2860
|
-
}),
|
|
2861
|
-
selectedValues.length > 0 && !readOnly && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(CommandSeparator, {}), /* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsx(CommandItem, {
|
|
2862
|
-
onSelect: () => commitChange([]),
|
|
2863
|
-
className: "justify-center text-center",
|
|
2864
|
-
children: clearText
|
|
2865
|
-
}) })] })
|
|
2866
|
-
]
|
|
2867
|
-
})]
|
|
2868
|
-
})
|
|
2869
|
-
})]
|
|
2870
|
-
});
|
|
2871
|
-
};
|
|
2872
|
-
MultiCombobox.displayName = "MultiCombobox";
|
|
2873
|
-
|
|
2874
2248
|
//#endregion
|
|
2875
2249
|
//#region src/components/auto-crud/auto-table-simple-filters.tsx
|
|
2250
|
+
function mergeSelectedFilterOptions(currentOptions, labelOptions, selectedValues) {
|
|
2251
|
+
const currentValues = new Set(currentOptions.map((option) => option.value));
|
|
2252
|
+
return [...labelOptions.filter((option) => selectedValues.includes(option.value) && !currentValues.has(option.value)), ...currentOptions];
|
|
2253
|
+
}
|
|
2876
2254
|
function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange, leading }) {
|
|
2877
2255
|
const columnDefs = table.options.columns;
|
|
2878
2256
|
const columns = React.useMemo(() => table.getAllColumns().filter((col) => col.getCanFilter()).sort((a, b) => {
|
|
@@ -3045,10 +2423,10 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
|
|
|
3045
2423
|
"data-filter-leading": true,
|
|
3046
2424
|
children: leading
|
|
3047
2425
|
}) : null,
|
|
3048
|
-
filterColumns.map((column, index) => {
|
|
2426
|
+
filterColumns.map((column, index$1) => {
|
|
3049
2427
|
const meta = column.columnDef.meta;
|
|
3050
2428
|
const value = getFilterValue(column.id);
|
|
3051
|
-
const isHidden = !expanded && visibleCount !== null && index >= visibleCount;
|
|
2429
|
+
const isHidden = !expanded && visibleCount !== null && index$1 >= visibleCount;
|
|
3052
2430
|
switch (meta.variant) {
|
|
3053
2431
|
case "text": return /* @__PURE__ */ jsx("div", {
|
|
3054
2432
|
className: isHidden ? "hidden" : "",
|
|
@@ -3061,17 +2439,27 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
|
|
|
3061
2439
|
})
|
|
3062
2440
|
}, column.id);
|
|
3063
2441
|
case "select":
|
|
3064
|
-
case "multiSelect":
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
2442
|
+
case "multiSelect": {
|
|
2443
|
+
const selectedValues = Array.isArray(value) ? value : value ? [value] : [];
|
|
2444
|
+
const filterOptions = mergeSelectedFilterOptions(meta.autoCrudFilterOptions ?? meta.options ?? [], meta.options ?? [], selectedValues);
|
|
2445
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2446
|
+
className: isHidden ? "hidden" : "",
|
|
2447
|
+
"data-filter-item": true,
|
|
2448
|
+
children: /* @__PURE__ */ jsx(SimpleFacetedFilter, {
|
|
2449
|
+
title: meta.label ?? column.id,
|
|
2450
|
+
options: filterOptions,
|
|
2451
|
+
multiple: meta.variant === "multiSelect",
|
|
2452
|
+
value: selectedValues,
|
|
2453
|
+
hasMore: meta.autoCrudFilterHasMore,
|
|
2454
|
+
loading: meta.autoCrudFilterLoading,
|
|
2455
|
+
searchValue: meta.autoCrudFilterSearchValue,
|
|
2456
|
+
shouldFilter: meta.autoCrudFilterShouldFilter,
|
|
2457
|
+
onChange: (v) => updateFilter(column.id, v.length ? v : void 0),
|
|
2458
|
+
onPopupScroll: meta.autoCrudFilterOnPopupScroll,
|
|
2459
|
+
onSearch: meta.autoCrudFilterOnSearch
|
|
2460
|
+
})
|
|
2461
|
+
}, column.id);
|
|
2462
|
+
}
|
|
3075
2463
|
case "range": return /* @__PURE__ */ jsx("div", {
|
|
3076
2464
|
className: isHidden ? "hidden" : "",
|
|
3077
2465
|
"data-filter-item": true,
|
|
@@ -3118,13 +2506,19 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
|
|
|
3118
2506
|
]
|
|
3119
2507
|
});
|
|
3120
2508
|
}
|
|
3121
|
-
function SimpleFacetedFilter({ title, options, multiple, value, onChange }) {
|
|
3122
|
-
return /* @__PURE__ */ jsx(MultiCombobox, {
|
|
2509
|
+
function SimpleFacetedFilter({ title, options, multiple, value, hasMore, loading, searchValue, shouldFilter, onChange, onPopupScroll, onSearch }) {
|
|
2510
|
+
return /* @__PURE__ */ jsx(MultiCombobox$1, {
|
|
3123
2511
|
value,
|
|
3124
2512
|
onChange,
|
|
3125
2513
|
options,
|
|
3126
2514
|
selectionMode: multiple ? "multiple" : "single",
|
|
3127
2515
|
searchPlaceholder: title,
|
|
2516
|
+
hasMore,
|
|
2517
|
+
loading,
|
|
2518
|
+
searchValue,
|
|
2519
|
+
shouldFilter,
|
|
2520
|
+
onPopupScroll,
|
|
2521
|
+
onSearch,
|
|
3128
2522
|
contentClassName: "w-50",
|
|
3129
2523
|
matchTriggerWidth: false,
|
|
3130
2524
|
clearText: "Clear filters",
|
|
@@ -3423,438 +2817,58 @@ function SimpleDateFilter({ title, multiple, value, onChange }) {
|
|
|
3423
2817
|
}
|
|
3424
2818
|
|
|
3425
2819
|
//#endregion
|
|
3426
|
-
//#region src/
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
function useAsRef(props) {
|
|
3432
|
-
const ref = React.useRef(props);
|
|
3433
|
-
useIsomorphicLayoutEffect(() => {
|
|
3434
|
-
ref.current = props;
|
|
2820
|
+
//#region src/components/data-table/data-table-column-header.tsx
|
|
2821
|
+
function DataTableColumnHeader({ column, label, className,...props }) {
|
|
2822
|
+
if (!column.getCanSort() && !column.getCanHide()) return /* @__PURE__ */ jsx("div", {
|
|
2823
|
+
className: cn(className),
|
|
2824
|
+
children: label
|
|
3435
2825
|
});
|
|
3436
|
-
return
|
|
2826
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
|
|
2827
|
+
className: cn("-ml-1.5 flex h-8 items-center gap-1.5 rounded-md px-2 py-1.5 hover:bg-accent focus:outline-none focus:ring-1 focus:ring-ring data-[state=open]:bg-accent [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground", className),
|
|
2828
|
+
...props,
|
|
2829
|
+
children: [label, column.getCanSort() && (column.getIsSorted() === "desc" ? /* @__PURE__ */ jsx(ChevronDown, {}) : column.getIsSorted() === "asc" ? /* @__PURE__ */ jsx(ChevronUp, {}) : /* @__PURE__ */ jsx(ChevronsUpDown, {}))]
|
|
2830
|
+
}), /* @__PURE__ */ jsxs(DropdownMenuContent, {
|
|
2831
|
+
align: "start",
|
|
2832
|
+
className: "w-28",
|
|
2833
|
+
children: [column.getCanSort() && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2834
|
+
/* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
|
|
2835
|
+
className: "relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-muted-foreground",
|
|
2836
|
+
checked: column.getIsSorted() === "asc",
|
|
2837
|
+
onClick: () => column.toggleSorting(false),
|
|
2838
|
+
children: [/* @__PURE__ */ jsx(ChevronUp, {}), "Asc"]
|
|
2839
|
+
}),
|
|
2840
|
+
/* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
|
|
2841
|
+
className: "relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-muted-foreground",
|
|
2842
|
+
checked: column.getIsSorted() === "desc",
|
|
2843
|
+
onClick: () => column.toggleSorting(true),
|
|
2844
|
+
children: [/* @__PURE__ */ jsx(ChevronDown, {}), "Desc"]
|
|
2845
|
+
}),
|
|
2846
|
+
column.getIsSorted() && /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
2847
|
+
className: "pl-2 [&_svg]:text-muted-foreground",
|
|
2848
|
+
onClick: () => column.clearSorting(),
|
|
2849
|
+
children: [/* @__PURE__ */ jsx(X, {}), "Reset"]
|
|
2850
|
+
})
|
|
2851
|
+
] }), column.getCanHide() && /* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
|
|
2852
|
+
className: "relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-muted-foreground",
|
|
2853
|
+
checked: !column.getIsVisible(),
|
|
2854
|
+
onClick: () => column.toggleVisibility(false),
|
|
2855
|
+
children: [/* @__PURE__ */ jsx(EyeOff, {}), "Hide"]
|
|
2856
|
+
})]
|
|
2857
|
+
})] });
|
|
3437
2858
|
}
|
|
3438
2859
|
|
|
3439
2860
|
//#endregion
|
|
3440
|
-
//#region src/
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
};
|
|
3452
|
-
function focusFirst(candidates, preventScroll = false) {
|
|
3453
|
-
const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
|
|
3454
|
-
for (const candidateRef of candidates) {
|
|
3455
|
-
const candidate = candidateRef.current;
|
|
3456
|
-
if (!candidate) continue;
|
|
3457
|
-
if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
|
|
3458
|
-
candidate.focus({ preventScroll });
|
|
3459
|
-
if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
|
|
3460
|
-
}
|
|
3461
|
-
}
|
|
3462
|
-
function wrapArray(array, startIndex) {
|
|
3463
|
-
return array.map((_, index) => array[(startIndex + index) % array.length]);
|
|
3464
|
-
}
|
|
3465
|
-
function getDirectionAwareKey(key, dir) {
|
|
3466
|
-
if (dir !== "rtl") return key;
|
|
3467
|
-
return key === "ArrowLeft" ? "ArrowRight" : key === "ArrowRight" ? "ArrowLeft" : key;
|
|
3468
|
-
}
|
|
3469
|
-
const ActionBarContext = React.createContext(null);
|
|
3470
|
-
function useActionBarContext(consumerName) {
|
|
3471
|
-
const context = React.useContext(ActionBarContext);
|
|
3472
|
-
if (!context) throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
|
|
3473
|
-
return context;
|
|
3474
|
-
}
|
|
3475
|
-
const FocusContext = React.createContext(null);
|
|
3476
|
-
function useFocusContext(consumerName) {
|
|
3477
|
-
const context = React.useContext(FocusContext);
|
|
3478
|
-
if (!context) throw new Error(`\`${consumerName}\` must be used within \`FocusProvider\``);
|
|
3479
|
-
return context;
|
|
3480
|
-
}
|
|
3481
|
-
function ActionBar(props) {
|
|
3482
|
-
const { open = false, onOpenChange, onEscapeKeyDown, side = "bottom", alignOffset = 0, align = "center", sideOffset = 16, portalContainer: portalContainerProp, dir: dirProp, orientation = "horizontal", loop = true, className, style, ref, asChild,...rootProps } = props;
|
|
3483
|
-
const [mounted, setMounted] = React.useState(false);
|
|
3484
|
-
const rootRef = React.useRef(null);
|
|
3485
|
-
const composedRef = useComposedRefs(ref, rootRef);
|
|
3486
|
-
const propsRef = useAsRef({
|
|
3487
|
-
onEscapeKeyDown,
|
|
3488
|
-
onOpenChange
|
|
3489
|
-
});
|
|
3490
|
-
const dir = useDirection(dirProp);
|
|
3491
|
-
React.useLayoutEffect(() => {
|
|
3492
|
-
setMounted(true);
|
|
3493
|
-
}, []);
|
|
3494
|
-
React.useEffect(() => {
|
|
3495
|
-
if (!open) return;
|
|
3496
|
-
const ownerDocument = rootRef.current?.ownerDocument ?? document;
|
|
3497
|
-
function onKeyDown(event) {
|
|
3498
|
-
if (event.key === "Escape") {
|
|
3499
|
-
propsRef.current.onEscapeKeyDown?.(event);
|
|
3500
|
-
if (!event.defaultPrevented) propsRef.current.onOpenChange?.(false);
|
|
3501
|
-
}
|
|
3502
|
-
}
|
|
3503
|
-
ownerDocument.addEventListener("keydown", onKeyDown);
|
|
3504
|
-
return () => ownerDocument.removeEventListener("keydown", onKeyDown);
|
|
3505
|
-
}, [open, propsRef]);
|
|
3506
|
-
const contextValue = React.useMemo(() => ({
|
|
3507
|
-
onOpenChange,
|
|
3508
|
-
dir,
|
|
3509
|
-
orientation,
|
|
3510
|
-
loop
|
|
3511
|
-
}), [
|
|
3512
|
-
onOpenChange,
|
|
3513
|
-
dir,
|
|
3514
|
-
orientation,
|
|
3515
|
-
loop
|
|
3516
|
-
]);
|
|
3517
|
-
const portalContainer = portalContainerProp ?? (mounted ? globalThis.document?.body : null);
|
|
3518
|
-
if (!portalContainer || !open) return null;
|
|
3519
|
-
const RootPrimitive = asChild ? Slot : "div";
|
|
3520
|
-
return /* @__PURE__ */ jsx(ActionBarContext.Provider, {
|
|
3521
|
-
value: contextValue,
|
|
3522
|
-
children: ReactDOM.createPortal(/* @__PURE__ */ jsx(RootPrimitive, {
|
|
3523
|
-
role: "toolbar",
|
|
3524
|
-
"aria-orientation": orientation,
|
|
3525
|
-
"data-slot": "action-bar",
|
|
3526
|
-
"data-side": side,
|
|
3527
|
-
"data-align": align,
|
|
3528
|
-
"data-orientation": orientation,
|
|
3529
|
-
dir,
|
|
3530
|
-
...rootProps,
|
|
3531
|
-
ref: composedRef,
|
|
3532
|
-
className: cn("fixed z-50 rounded-lg border bg-card shadow-lg outline-none", "fade-in-0 zoom-in-95 animate-in duration-250 [animation-timing-function:cubic-bezier(0.16,1,0.3,1)]", "data-[side=bottom]:slide-in-from-bottom-4 data-[side=top]:slide-in-from-top-4", "motion-reduce:animate-none motion-reduce:transition-none", orientation === "horizontal" ? "flex flex-row items-center gap-2 px-2 py-1.5" : "flex flex-col items-start gap-2 px-1.5 py-2", className),
|
|
3533
|
-
style: {
|
|
3534
|
-
[side]: `${sideOffset}px`,
|
|
3535
|
-
...align === "center" && {
|
|
3536
|
-
left: "50%",
|
|
3537
|
-
translate: "-50% 0"
|
|
3538
|
-
},
|
|
3539
|
-
...align === "start" && { left: `${alignOffset}px` },
|
|
3540
|
-
...align === "end" && { right: `${alignOffset}px` },
|
|
3541
|
-
...style
|
|
3542
|
-
}
|
|
3543
|
-
}), portalContainer)
|
|
3544
|
-
});
|
|
3545
|
-
}
|
|
3546
|
-
function ActionBarSelection(props) {
|
|
3547
|
-
const { className, asChild,...selectionProps } = props;
|
|
3548
|
-
return /* @__PURE__ */ jsx(asChild ? Slot : "div", {
|
|
3549
|
-
"data-slot": "action-bar-selection",
|
|
3550
|
-
...selectionProps,
|
|
3551
|
-
className: cn("flex items-center gap-1 rounded-sm border px-2 py-1 font-medium text-sm tabular-nums", className)
|
|
3552
|
-
});
|
|
3553
|
-
}
|
|
3554
|
-
function ActionBarGroup(props) {
|
|
3555
|
-
const { onBlur: onBlurProp, onFocus: onFocusProp, onMouseDown: onMouseDownProp, className, asChild, ref,...groupProps } = props;
|
|
3556
|
-
const [tabStopId, setTabStopId] = React.useState(null);
|
|
3557
|
-
const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);
|
|
3558
|
-
const [focusableItemCount, setFocusableItemCount] = React.useState(0);
|
|
3559
|
-
const composedRef = useComposedRefs(ref, React.useRef(null));
|
|
3560
|
-
const isClickFocusRef = React.useRef(false);
|
|
3561
|
-
const itemsRef = React.useRef(/* @__PURE__ */ new Map());
|
|
3562
|
-
const { dir, orientation } = useActionBarContext(GROUP_NAME);
|
|
3563
|
-
const onItemFocus = React.useCallback((tabStopId$1) => {
|
|
3564
|
-
setTabStopId(tabStopId$1);
|
|
3565
|
-
}, []);
|
|
3566
|
-
const onItemShiftTab = React.useCallback(() => {
|
|
3567
|
-
setIsTabbingBackOut(true);
|
|
3568
|
-
}, []);
|
|
3569
|
-
const onFocusableItemAdd = React.useCallback(() => {
|
|
3570
|
-
setFocusableItemCount((prevCount) => prevCount + 1);
|
|
3571
|
-
}, []);
|
|
3572
|
-
const onFocusableItemRemove = React.useCallback(() => {
|
|
3573
|
-
setFocusableItemCount((prevCount) => prevCount - 1);
|
|
3574
|
-
}, []);
|
|
3575
|
-
const onItemRegister = React.useCallback((item) => {
|
|
3576
|
-
itemsRef.current.set(item.id, item);
|
|
3577
|
-
}, []);
|
|
3578
|
-
const onItemUnregister = React.useCallback((id) => {
|
|
3579
|
-
itemsRef.current.delete(id);
|
|
3580
|
-
}, []);
|
|
3581
|
-
const getItems = React.useCallback(() => {
|
|
3582
|
-
return Array.from(itemsRef.current.values()).filter((item) => item.ref.current).sort((a, b) => {
|
|
3583
|
-
const elementA = a.ref.current;
|
|
3584
|
-
const elementB = b.ref.current;
|
|
3585
|
-
if (!elementA || !elementB) return 0;
|
|
3586
|
-
const position = elementA.compareDocumentPosition(elementB);
|
|
3587
|
-
if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
|
|
3588
|
-
if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
|
|
3589
|
-
return 0;
|
|
3590
|
-
});
|
|
3591
|
-
}, []);
|
|
3592
|
-
const onBlur = React.useCallback((event) => {
|
|
3593
|
-
onBlurProp?.(event);
|
|
3594
|
-
if (event.defaultPrevented) return;
|
|
3595
|
-
setIsTabbingBackOut(false);
|
|
3596
|
-
}, [onBlurProp]);
|
|
3597
|
-
const onFocus = React.useCallback((event) => {
|
|
3598
|
-
onFocusProp?.(event);
|
|
3599
|
-
if (event.defaultPrevented) return;
|
|
3600
|
-
const isKeyboardFocus = !isClickFocusRef.current;
|
|
3601
|
-
if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {
|
|
3602
|
-
const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);
|
|
3603
|
-
event.currentTarget.dispatchEvent(entryFocusEvent);
|
|
3604
|
-
if (!entryFocusEvent.defaultPrevented) {
|
|
3605
|
-
const items = Array.from(itemsRef.current.values()).filter((item) => !item.disabled);
|
|
3606
|
-
focusFirst([items.find((item) => item.id === tabStopId), ...items].filter(Boolean).map((item) => item.ref), false);
|
|
3607
|
-
}
|
|
3608
|
-
}
|
|
3609
|
-
isClickFocusRef.current = false;
|
|
3610
|
-
}, [
|
|
3611
|
-
onFocusProp,
|
|
3612
|
-
isTabbingBackOut,
|
|
3613
|
-
tabStopId
|
|
3614
|
-
]);
|
|
3615
|
-
const onMouseDown = React.useCallback((event) => {
|
|
3616
|
-
onMouseDownProp?.(event);
|
|
3617
|
-
if (event.defaultPrevented) return;
|
|
3618
|
-
isClickFocusRef.current = true;
|
|
3619
|
-
}, [onMouseDownProp]);
|
|
3620
|
-
const focusContextValue = React.useMemo(() => ({
|
|
3621
|
-
tabStopId,
|
|
3622
|
-
onItemFocus,
|
|
3623
|
-
onItemShiftTab,
|
|
3624
|
-
onFocusableItemAdd,
|
|
3625
|
-
onFocusableItemRemove,
|
|
3626
|
-
onItemRegister,
|
|
3627
|
-
onItemUnregister,
|
|
3628
|
-
getItems
|
|
3629
|
-
}), [
|
|
3630
|
-
tabStopId,
|
|
3631
|
-
onItemFocus,
|
|
3632
|
-
onItemShiftTab,
|
|
3633
|
-
onFocusableItemAdd,
|
|
3634
|
-
onFocusableItemRemove,
|
|
3635
|
-
onItemRegister,
|
|
3636
|
-
onItemUnregister,
|
|
3637
|
-
getItems
|
|
3638
|
-
]);
|
|
3639
|
-
const GroupPrimitive = asChild ? Slot : "div";
|
|
3640
|
-
return /* @__PURE__ */ jsx(FocusContext.Provider, {
|
|
3641
|
-
value: focusContextValue,
|
|
3642
|
-
children: /* @__PURE__ */ jsx(GroupPrimitive, {
|
|
3643
|
-
role: "group",
|
|
3644
|
-
"data-slot": "action-bar-group",
|
|
3645
|
-
"data-orientation": orientation,
|
|
3646
|
-
dir,
|
|
3647
|
-
tabIndex: isTabbingBackOut || focusableItemCount === 0 ? -1 : 0,
|
|
3648
|
-
...groupProps,
|
|
3649
|
-
ref: composedRef,
|
|
3650
|
-
className: cn("flex gap-2 outline-none", orientation === "horizontal" ? "items-center" : "w-full flex-col items-start", className),
|
|
3651
|
-
onBlur,
|
|
3652
|
-
onFocus,
|
|
3653
|
-
onMouseDown
|
|
3654
|
-
})
|
|
3655
|
-
});
|
|
3656
|
-
}
|
|
3657
|
-
function ActionBarItem(props) {
|
|
3658
|
-
const { onSelect, onClick: onClickProp, onFocus: onFocusProp, onKeyDown: onKeyDownProp, onMouseDown: onMouseDownProp, className, disabled, ref,...itemProps } = props;
|
|
3659
|
-
const itemRef = React.useRef(null);
|
|
3660
|
-
const composedRef = useComposedRefs(ref, itemRef);
|
|
3661
|
-
const isMouseClickRef = React.useRef(false);
|
|
3662
|
-
const { onOpenChange, dir, orientation, loop } = useActionBarContext(ITEM_NAME);
|
|
3663
|
-
const focusContext = useFocusContext(ITEM_NAME);
|
|
3664
|
-
const itemId = React.useId();
|
|
3665
|
-
const isTabStop = focusContext.tabStopId === itemId;
|
|
3666
|
-
useIsomorphicLayoutEffect(() => {
|
|
3667
|
-
focusContext.onItemRegister({
|
|
3668
|
-
id: itemId,
|
|
3669
|
-
ref: itemRef,
|
|
3670
|
-
disabled: !!disabled
|
|
3671
|
-
});
|
|
3672
|
-
if (!disabled) focusContext.onFocusableItemAdd();
|
|
3673
|
-
return () => {
|
|
3674
|
-
focusContext.onItemUnregister(itemId);
|
|
3675
|
-
if (!disabled) focusContext.onFocusableItemRemove();
|
|
3676
|
-
};
|
|
3677
|
-
}, [
|
|
3678
|
-
focusContext,
|
|
3679
|
-
itemId,
|
|
3680
|
-
disabled
|
|
3681
|
-
]);
|
|
3682
|
-
const onClick = React.useCallback((event) => {
|
|
3683
|
-
onClickProp?.(event);
|
|
3684
|
-
if (event.defaultPrevented) return;
|
|
3685
|
-
const item = itemRef.current;
|
|
3686
|
-
if (!item) return;
|
|
3687
|
-
const itemSelectEvent = new CustomEvent(ITEM_SELECT, {
|
|
3688
|
-
bubbles: true,
|
|
3689
|
-
cancelable: true
|
|
3690
|
-
});
|
|
3691
|
-
item.addEventListener(ITEM_SELECT, (event$1) => onSelect?.(event$1), { once: true });
|
|
3692
|
-
item.dispatchEvent(itemSelectEvent);
|
|
3693
|
-
if (!itemSelectEvent.defaultPrevented) onOpenChange?.(false);
|
|
3694
|
-
}, [
|
|
3695
|
-
onClickProp,
|
|
3696
|
-
onOpenChange,
|
|
3697
|
-
onSelect
|
|
3698
|
-
]);
|
|
3699
|
-
const onFocus = React.useCallback((event) => {
|
|
3700
|
-
onFocusProp?.(event);
|
|
3701
|
-
if (event.defaultPrevented) return;
|
|
3702
|
-
focusContext.onItemFocus(itemId);
|
|
3703
|
-
isMouseClickRef.current = false;
|
|
3704
|
-
}, [
|
|
3705
|
-
onFocusProp,
|
|
3706
|
-
focusContext,
|
|
3707
|
-
itemId
|
|
3708
|
-
]);
|
|
3709
|
-
const onKeyDown = React.useCallback((event) => {
|
|
3710
|
-
onKeyDownProp?.(event);
|
|
3711
|
-
if (event.defaultPrevented) return;
|
|
3712
|
-
if (event.key === "Tab" && event.shiftKey) {
|
|
3713
|
-
focusContext.onItemShiftTab();
|
|
3714
|
-
return;
|
|
3715
|
-
}
|
|
3716
|
-
if (event.target !== event.currentTarget) return;
|
|
3717
|
-
const key = getDirectionAwareKey(event.key, dir);
|
|
3718
|
-
let focusIntent;
|
|
3719
|
-
if (orientation === "horizontal") {
|
|
3720
|
-
if (key === "ArrowLeft") focusIntent = "prev";
|
|
3721
|
-
else if (key === "ArrowRight") focusIntent = "next";
|
|
3722
|
-
else if (key === "Home") focusIntent = "first";
|
|
3723
|
-
else if (key === "End") focusIntent = "last";
|
|
3724
|
-
} else if (key === "ArrowUp") focusIntent = "prev";
|
|
3725
|
-
else if (key === "ArrowDown") focusIntent = "next";
|
|
3726
|
-
else if (key === "Home") focusIntent = "first";
|
|
3727
|
-
else if (key === "End") focusIntent = "last";
|
|
3728
|
-
if (focusIntent !== void 0) {
|
|
3729
|
-
if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
|
|
3730
|
-
event.preventDefault();
|
|
3731
|
-
let candidateRefs = focusContext.getItems().filter((item) => !item.disabled).map((item) => item.ref);
|
|
3732
|
-
if (focusIntent === "last") candidateRefs.reverse();
|
|
3733
|
-
else if (focusIntent === "prev" || focusIntent === "next") {
|
|
3734
|
-
if (focusIntent === "prev") candidateRefs.reverse();
|
|
3735
|
-
const currentIndex = candidateRefs.findIndex((ref$1) => ref$1.current === event.currentTarget);
|
|
3736
|
-
candidateRefs = loop ? wrapArray(candidateRefs, currentIndex + 1) : candidateRefs.slice(currentIndex + 1);
|
|
3737
|
-
}
|
|
3738
|
-
queueMicrotask(() => focusFirst(candidateRefs));
|
|
3739
|
-
}
|
|
3740
|
-
}, [
|
|
3741
|
-
onKeyDownProp,
|
|
3742
|
-
focusContext,
|
|
3743
|
-
dir,
|
|
3744
|
-
orientation,
|
|
3745
|
-
loop
|
|
3746
|
-
]);
|
|
3747
|
-
const onMouseDown = React.useCallback((event) => {
|
|
3748
|
-
onMouseDownProp?.(event);
|
|
3749
|
-
if (event.defaultPrevented) return;
|
|
3750
|
-
isMouseClickRef.current = true;
|
|
3751
|
-
if (disabled) event.preventDefault();
|
|
3752
|
-
else focusContext.onItemFocus(itemId);
|
|
3753
|
-
}, [
|
|
3754
|
-
onMouseDownProp,
|
|
3755
|
-
focusContext,
|
|
3756
|
-
itemId,
|
|
3757
|
-
disabled
|
|
3758
|
-
]);
|
|
3759
|
-
return /* @__PURE__ */ jsx(Button, {
|
|
3760
|
-
type: "button",
|
|
3761
|
-
"data-slot": "action-bar-item",
|
|
3762
|
-
variant: "secondary",
|
|
3763
|
-
size: "sm",
|
|
3764
|
-
disabled,
|
|
3765
|
-
tabIndex: isTabStop ? 0 : -1,
|
|
3766
|
-
...itemProps,
|
|
3767
|
-
className: cn(orientation === "vertical" && "w-full", className),
|
|
3768
|
-
ref: composedRef,
|
|
3769
|
-
onClick,
|
|
3770
|
-
onFocus,
|
|
3771
|
-
onKeyDown,
|
|
3772
|
-
onMouseDown
|
|
3773
|
-
});
|
|
3774
|
-
}
|
|
3775
|
-
function ActionBarClose(props) {
|
|
3776
|
-
const { asChild, className, onClick,...closeProps } = props;
|
|
3777
|
-
const { onOpenChange } = useActionBarContext(CLOSE_NAME);
|
|
3778
|
-
const onCloseClick = React.useCallback((event) => {
|
|
3779
|
-
onClick?.(event);
|
|
3780
|
-
if (event.defaultPrevented) return;
|
|
3781
|
-
onOpenChange?.(false);
|
|
3782
|
-
}, [onOpenChange, onClick]);
|
|
3783
|
-
return /* @__PURE__ */ jsx(asChild ? Slot : "button", {
|
|
3784
|
-
type: "button",
|
|
3785
|
-
"data-slot": "action-bar-close",
|
|
3786
|
-
...closeProps,
|
|
3787
|
-
className: cn("rounded-xs opacity-70 outline-none hover:opacity-100 focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none [&_svg:not([class*='size-'])]:size-3.5 [&_svg]:pointer-events-none [&_svg]:shrink-0", className),
|
|
3788
|
-
onClick: onCloseClick
|
|
3789
|
-
});
|
|
3790
|
-
}
|
|
3791
|
-
function ActionBarSeparator(props) {
|
|
3792
|
-
const { orientation: orientationProp, asChild, className,...separatorProps } = props;
|
|
3793
|
-
const context = useActionBarContext(SEPARATOR_NAME);
|
|
3794
|
-
const orientation = orientationProp ?? context.orientation;
|
|
3795
|
-
return /* @__PURE__ */ jsx(asChild ? Slot : "div", {
|
|
3796
|
-
role: "separator",
|
|
3797
|
-
"aria-orientation": orientation,
|
|
3798
|
-
"aria-hidden": "true",
|
|
3799
|
-
"data-slot": "action-bar-separator",
|
|
3800
|
-
...separatorProps,
|
|
3801
|
-
className: cn("in-data-[slot=action-bar-selection]:ml-0.5 in-data-[slot=action-bar-selection]:h-4 in-data-[slot=action-bar-selection]:w-px bg-border", orientation === "horizontal" ? "h-6 w-px" : "h-px w-full", className)
|
|
3802
|
-
});
|
|
3803
|
-
}
|
|
3804
|
-
|
|
3805
|
-
//#endregion
|
|
3806
|
-
//#region src/components/data-table/data-table-column-header.tsx
|
|
3807
|
-
function DataTableColumnHeader({ column, label, className,...props }) {
|
|
3808
|
-
if (!column.getCanSort() && !column.getCanHide()) return /* @__PURE__ */ jsx("div", {
|
|
3809
|
-
className: cn(className),
|
|
3810
|
-
children: label
|
|
3811
|
-
});
|
|
3812
|
-
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
|
|
3813
|
-
className: cn("-ml-1.5 flex h-8 items-center gap-1.5 rounded-md px-2 py-1.5 hover:bg-accent focus:outline-none focus:ring-1 focus:ring-ring data-[state=open]:bg-accent [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground", className),
|
|
3814
|
-
...props,
|
|
3815
|
-
children: [label, column.getCanSort() && (column.getIsSorted() === "desc" ? /* @__PURE__ */ jsx(ChevronDown, {}) : column.getIsSorted() === "asc" ? /* @__PURE__ */ jsx(ChevronUp, {}) : /* @__PURE__ */ jsx(ChevronsUpDown, {}))]
|
|
3816
|
-
}), /* @__PURE__ */ jsxs(DropdownMenuContent, {
|
|
3817
|
-
align: "start",
|
|
3818
|
-
className: "w-28",
|
|
3819
|
-
children: [column.getCanSort() && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3820
|
-
/* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
|
|
3821
|
-
className: "relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-muted-foreground",
|
|
3822
|
-
checked: column.getIsSorted() === "asc",
|
|
3823
|
-
onClick: () => column.toggleSorting(false),
|
|
3824
|
-
children: [/* @__PURE__ */ jsx(ChevronUp, {}), "Asc"]
|
|
3825
|
-
}),
|
|
3826
|
-
/* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
|
|
3827
|
-
className: "relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-muted-foreground",
|
|
3828
|
-
checked: column.getIsSorted() === "desc",
|
|
3829
|
-
onClick: () => column.toggleSorting(true),
|
|
3830
|
-
children: [/* @__PURE__ */ jsx(ChevronDown, {}), "Desc"]
|
|
3831
|
-
}),
|
|
3832
|
-
column.getIsSorted() && /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
3833
|
-
className: "pl-2 [&_svg]:text-muted-foreground",
|
|
3834
|
-
onClick: () => column.clearSorting(),
|
|
3835
|
-
children: [/* @__PURE__ */ jsx(X, {}), "Reset"]
|
|
3836
|
-
})
|
|
3837
|
-
] }), column.getCanHide() && /* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
|
|
3838
|
-
className: "relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-muted-foreground",
|
|
3839
|
-
checked: !column.getIsVisible(),
|
|
3840
|
-
onClick: () => column.toggleVisibility(false),
|
|
3841
|
-
children: [/* @__PURE__ */ jsx(EyeOff, {}), "Hide"]
|
|
3842
|
-
})]
|
|
3843
|
-
})] });
|
|
3844
|
-
}
|
|
3845
|
-
|
|
3846
|
-
//#endregion
|
|
3847
|
-
//#region src/lib/humanize.ts
|
|
3848
|
-
/**
|
|
3849
|
-
* 将字符串转为人类可读格式
|
|
3850
|
-
*
|
|
3851
|
-
* @example
|
|
3852
|
-
* humanize("createdAt") // "Created At"
|
|
3853
|
-
* humanize("user_name") // "User Name"
|
|
3854
|
-
* humanize("firstName") // "First Name"
|
|
3855
|
-
*/
|
|
3856
|
-
function humanize(str) {
|
|
3857
|
-
return str.replace(/([A-Z])/g, " $1").replace(/[_-]/g, " ").replace(/^./, (s) => s.toUpperCase()).trim();
|
|
2861
|
+
//#region src/lib/humanize.ts
|
|
2862
|
+
/**
|
|
2863
|
+
* 将字符串转为人类可读格式
|
|
2864
|
+
*
|
|
2865
|
+
* @example
|
|
2866
|
+
* humanize("createdAt") // "Created At"
|
|
2867
|
+
* humanize("user_name") // "User Name"
|
|
2868
|
+
* humanize("firstName") // "First Name"
|
|
2869
|
+
*/
|
|
2870
|
+
function humanize(str) {
|
|
2871
|
+
return str.replace(/([A-Z])/g, " $1").replace(/[_-]/g, " ").replace(/^./, (s) => s.toUpperCase()).trim();
|
|
3858
2872
|
}
|
|
3859
2873
|
|
|
3860
2874
|
//#endregion
|
|
@@ -4003,9 +3017,9 @@ function parseZodField(schema) {
|
|
|
4003
3017
|
if (def?.type === "enum") {
|
|
4004
3018
|
let enumValues$1;
|
|
4005
3019
|
if (def?.entries) {
|
|
4006
|
-
const entries = def.entries;
|
|
4007
|
-
if (Array.isArray(entries)) enumValues$1 = entries.map(String);
|
|
4008
|
-
else if (typeof entries === "object") enumValues$1 = Object.keys(entries);
|
|
3020
|
+
const entries$1 = def.entries;
|
|
3021
|
+
if (Array.isArray(entries$1)) enumValues$1 = entries$1.map(String);
|
|
3022
|
+
else if (typeof entries$1 === "object") enumValues$1 = Object.keys(entries$1);
|
|
4009
3023
|
} else if (def?.values) {
|
|
4010
3024
|
const values = def.values;
|
|
4011
3025
|
if (Array.isArray(values)) enumValues$1 = values.map(String);
|
|
@@ -4026,9 +3040,9 @@ function parseZodField(schema) {
|
|
|
4026
3040
|
});
|
|
4027
3041
|
let enumValues;
|
|
4028
3042
|
if (def?.entries) {
|
|
4029
|
-
const entries = def.entries;
|
|
4030
|
-
if (Array.isArray(entries)) enumValues = entries.map(String);
|
|
4031
|
-
else if (typeof entries === "object") enumValues = Object.keys(entries);
|
|
3043
|
+
const entries$1 = def.entries;
|
|
3044
|
+
if (Array.isArray(entries$1)) enumValues = entries$1.map(String);
|
|
3045
|
+
else if (typeof entries$1 === "object") enumValues = Object.keys(entries$1);
|
|
4032
3046
|
} else if (def?.values) {
|
|
4033
3047
|
const values = def.values;
|
|
4034
3048
|
if (Array.isArray(values)) enumValues = values.map(String);
|
|
@@ -4223,6 +3237,10 @@ function createSelectColumn() {
|
|
|
4223
3237
|
* 创建操作列
|
|
4224
3238
|
*/
|
|
4225
3239
|
function createActionsColumn(items) {
|
|
3240
|
+
const renderActionComponent = (item, row) => {
|
|
3241
|
+
if (!item.component) return null;
|
|
3242
|
+
return typeof item.component === "function" ? item.component(item.getContext?.(row.original)) : item.component;
|
|
3243
|
+
};
|
|
4226
3244
|
return {
|
|
4227
3245
|
id: "actions",
|
|
4228
3246
|
cell: ({ row }) => /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
@@ -4236,11 +3254,11 @@ function createActionsColumn(items) {
|
|
|
4236
3254
|
}), /* @__PURE__ */ jsx(DropdownMenuContent, {
|
|
4237
3255
|
align: "end",
|
|
4238
3256
|
className: "w-40",
|
|
4239
|
-
children: items.map((item, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [item.separator && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
3257
|
+
children: items.map((item, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [item.separator && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}), item.component ? renderActionComponent(item, row) : item.label && item.onClick ? /* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
4240
3258
|
className: item.variant === "destructive" ? "text-destructive" : "",
|
|
4241
|
-
onClick: () => item.onClick(row.original),
|
|
3259
|
+
onClick: () => item.onClick?.(row.original),
|
|
4242
3260
|
children: item.label
|
|
4243
|
-
})] }, i))
|
|
3261
|
+
}) : null] }, i))
|
|
4244
3262
|
})] }),
|
|
4245
3263
|
size: 40
|
|
4246
3264
|
};
|
|
@@ -4458,12 +3476,22 @@ function downloadCSV(csvContent, filename) {
|
|
|
4458
3476
|
|
|
4459
3477
|
//#endregion
|
|
4460
3478
|
//#region src/components/auto-crud/auto-table-action-bar.tsx
|
|
4461
|
-
|
|
4462
|
-
|
|
3479
|
+
const DEFAULT_DELETE_CONFIRMATION = {
|
|
3480
|
+
title: "确认批量删除",
|
|
3481
|
+
description: (count) => `此操作无法撤销。确定要删除选中的 ${count} 条记录吗?`,
|
|
3482
|
+
cancel: "取消",
|
|
3483
|
+
confirm: "确认删除"
|
|
3484
|
+
};
|
|
3485
|
+
function isBatchCustomAction(action$1) {
|
|
3486
|
+
return action$1.type === "custom";
|
|
4463
3487
|
}
|
|
4464
|
-
function AutoTableActionBar({ table, onDeleteSelected, onUpdateSelected, batchUpdateFields, enableExport = true, showDefaultExport = true, enableDelete = true, extraActions, actions }) {
|
|
3488
|
+
function AutoTableActionBar({ table, onDeleteSelected, onUpdateSelected, batchUpdateFields, enableExport = true, showDefaultExport = true, enableDelete = true, extraActions, actions, deleteConfirmation = DEFAULT_DELETE_CONFIRMATION }) {
|
|
4465
3489
|
const rows = table.getFilteredSelectedRowModel().rows;
|
|
4466
3490
|
const selectedRows = React.useMemo(() => rows.map((row) => row.original), [rows]);
|
|
3491
|
+
const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false);
|
|
3492
|
+
React.useEffect(() => {
|
|
3493
|
+
if (selectedRows.length === 0) setDeleteDialogOpen(false);
|
|
3494
|
+
}, [selectedRows.length]);
|
|
4467
3495
|
const clearSelection = React.useCallback(() => {
|
|
4468
3496
|
table.toggleAllRowsSelected(false);
|
|
4469
3497
|
}, [table]);
|
|
@@ -4486,8 +3514,34 @@ function AutoTableActionBar({ table, onDeleteSelected, onUpdateSelected, batchUp
|
|
|
4486
3514
|
});
|
|
4487
3515
|
}, [table]);
|
|
4488
3516
|
const onDelete = React.useCallback(() => {
|
|
4489
|
-
if (onDeleteSelected
|
|
4490
|
-
|
|
3517
|
+
if (onDeleteSelected && selectedRows.length > 0) {
|
|
3518
|
+
onDeleteSelected(selectedRows);
|
|
3519
|
+
clearSelection();
|
|
3520
|
+
}
|
|
3521
|
+
setDeleteDialogOpen(false);
|
|
3522
|
+
}, [
|
|
3523
|
+
selectedRows,
|
|
3524
|
+
onDeleteSelected,
|
|
3525
|
+
clearSelection
|
|
3526
|
+
]);
|
|
3527
|
+
const runDeleteAction = React.useCallback((action$1, event) => {
|
|
3528
|
+
if (selectedRows.length === 0) {
|
|
3529
|
+
setDeleteDialogOpen(false);
|
|
3530
|
+
return;
|
|
3531
|
+
}
|
|
3532
|
+
if (action$1.onClick) {
|
|
3533
|
+
action$1.onClick(selectedRows, actionContext, event);
|
|
3534
|
+
clearSelection();
|
|
3535
|
+
setDeleteDialogOpen(false);
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
onDelete();
|
|
3539
|
+
}, [
|
|
3540
|
+
actionContext,
|
|
3541
|
+
clearSelection,
|
|
3542
|
+
onDelete,
|
|
3543
|
+
selectedRows
|
|
3544
|
+
]);
|
|
4491
3545
|
const handleBatchUpdate = React.useCallback((field, value) => {
|
|
4492
3546
|
if (onUpdateSelected) {
|
|
4493
3547
|
onUpdateSelected(selectedRows, { [field]: value });
|
|
@@ -4514,18 +3568,28 @@ function AutoTableActionBar({ table, onDeleteSelected, onUpdateSelected, batchUp
|
|
|
4514
3568
|
children: option.label
|
|
4515
3569
|
}, option.value))
|
|
4516
3570
|
})] }, `${keyPrefix}-${fieldConfig.field}`)), [batchUpdateFields, handleBatchUpdate]);
|
|
4517
|
-
const renderBuiltinAction = React.useCallback((action) => {
|
|
4518
|
-
if (!(action.type === "batchUpdate" ? !!onUpdateSelected && !!batchUpdateFields?.length : action.type === "export" ? enableExport : enableDelete && !!onDeleteSelected)) return null;
|
|
4519
|
-
if (action.component) return renderComponent(action.component);
|
|
4520
|
-
if (action.type === "batchUpdate") return renderBatchUpdate(action.type);
|
|
4521
|
-
if (action.type === "export") return /* @__PURE__ */ jsxs(ActionBarItem, {
|
|
4522
|
-
onClick: (event) => action.onClick ? action.onClick(selectedRows, actionContext, event) : onExport(),
|
|
4523
|
-
children: [/* @__PURE__ */ jsx(Download, {}), action.label ?? "Export"]
|
|
3571
|
+
const renderBuiltinAction = React.useCallback((action$1) => {
|
|
3572
|
+
if (!(action$1.type === "batchUpdate" ? !!onUpdateSelected && !!batchUpdateFields?.length : action$1.type === "export" ? enableExport : enableDelete && !!onDeleteSelected)) return null;
|
|
3573
|
+
if (action$1.component) return renderComponent(action$1.component);
|
|
3574
|
+
if (action$1.type === "batchUpdate") return renderBatchUpdate(action$1.type);
|
|
3575
|
+
if (action$1.type === "export") return /* @__PURE__ */ jsxs(ActionBarItem, {
|
|
3576
|
+
onClick: (event) => action$1.onClick ? action$1.onClick(selectedRows, actionContext, event) : onExport(),
|
|
3577
|
+
children: [/* @__PURE__ */ jsx(Download, {}), action$1.label ?? "Export"]
|
|
4524
3578
|
}, "export");
|
|
4525
|
-
if (action.type === "delete") return /* @__PURE__ */ jsxs(
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
children: [/* @__PURE__ */
|
|
3579
|
+
if (action$1.type === "delete") return /* @__PURE__ */ jsxs(AlertDialog, {
|
|
3580
|
+
open: deleteDialogOpen,
|
|
3581
|
+
onOpenChange: setDeleteDialogOpen,
|
|
3582
|
+
children: [/* @__PURE__ */ jsxs(ActionBarItem, {
|
|
3583
|
+
variant: "destructive",
|
|
3584
|
+
onClick: () => setDeleteDialogOpen(true),
|
|
3585
|
+
onSelect: (event) => event.preventDefault(),
|
|
3586
|
+
children: [/* @__PURE__ */ jsx(Trash2, {}), action$1.label ?? "Delete"]
|
|
3587
|
+
}), /* @__PURE__ */ jsxs(AlertDialogContent, { children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: deleteConfirmation.title }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: deleteConfirmation.description(selectedRows.length) })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, { children: deleteConfirmation.cancel }), /* @__PURE__ */ jsx(AlertDialogAction, {
|
|
3588
|
+
className: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30",
|
|
3589
|
+
disabled: selectedRows.length === 0,
|
|
3590
|
+
onClick: (event) => runDeleteAction(action$1, event),
|
|
3591
|
+
children: deleteConfirmation.confirm
|
|
3592
|
+
})] })] })]
|
|
4529
3593
|
}, "delete");
|
|
4530
3594
|
return null;
|
|
4531
3595
|
}, [
|
|
@@ -4533,42 +3597,47 @@ function AutoTableActionBar({ table, onDeleteSelected, onUpdateSelected, batchUp
|
|
|
4533
3597
|
batchUpdateFields?.length,
|
|
4534
3598
|
enableDelete,
|
|
4535
3599
|
enableExport,
|
|
3600
|
+
deleteConfirmation,
|
|
3601
|
+
deleteDialogOpen,
|
|
4536
3602
|
onDelete,
|
|
4537
3603
|
onDeleteSelected,
|
|
4538
3604
|
onUpdateSelected,
|
|
4539
3605
|
onExport,
|
|
4540
3606
|
renderBatchUpdate,
|
|
4541
3607
|
renderComponent,
|
|
3608
|
+
runDeleteAction,
|
|
4542
3609
|
selectedRows
|
|
4543
3610
|
]);
|
|
4544
|
-
const renderCustomAction = React.useCallback((action, index) => {
|
|
4545
|
-
if (action.component) return /* @__PURE__ */ jsx(React.Fragment, { children: renderComponent(action.component) }, `custom-${index}`);
|
|
4546
|
-
if (!action.label || !action.onClick) return null;
|
|
3611
|
+
const renderCustomAction = React.useCallback((action$1, index$1) => {
|
|
3612
|
+
if (action$1.component) return /* @__PURE__ */ jsx(React.Fragment, { children: renderComponent(action$1.component) }, `custom-${index$1}`);
|
|
3613
|
+
if (!action$1.label || !action$1.onClick) return null;
|
|
4547
3614
|
return /* @__PURE__ */ jsx(ActionBarItem, {
|
|
4548
|
-
variant: action.variant === "destructive" ? "destructive" : "secondary",
|
|
4549
|
-
onClick: (event) => action.onClick?.(selectedRows, actionContext, event),
|
|
4550
|
-
children: action.label
|
|
4551
|
-
}, `custom-${index}`);
|
|
3615
|
+
variant: action$1.variant === "destructive" ? "destructive" : "secondary",
|
|
3616
|
+
onClick: (event) => action$1.onClick?.(selectedRows, actionContext, event),
|
|
3617
|
+
children: action$1.label
|
|
3618
|
+
}, `custom-${index$1}`);
|
|
4552
3619
|
}, [
|
|
4553
3620
|
actionContext,
|
|
4554
3621
|
renderComponent,
|
|
4555
3622
|
selectedRows
|
|
4556
3623
|
]);
|
|
4557
3624
|
const renderedActions = React.useMemo(() => {
|
|
4558
|
-
const
|
|
3625
|
+
const configuredActions = typeof actions === "function" ? actions([
|
|
4559
3626
|
{ type: "batchUpdate" },
|
|
4560
3627
|
{ type: "export" },
|
|
4561
3628
|
{ type: "delete" }
|
|
4562
3629
|
]) : actions;
|
|
4563
|
-
|
|
3630
|
+
const resolvedActions = configuredActions?.filter((action$1) => !action$1.hidden);
|
|
3631
|
+
if (configuredActions === void 0) return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4564
3632
|
renderBuiltinAction({ type: "batchUpdate" }),
|
|
4565
3633
|
extraActions,
|
|
4566
3634
|
showDefaultExport && renderBuiltinAction({ type: "export" }),
|
|
4567
3635
|
renderBuiltinAction({ type: "delete" })
|
|
4568
3636
|
] });
|
|
4569
|
-
if (!resolvedActions
|
|
4570
|
-
|
|
4571
|
-
const
|
|
3637
|
+
if (!resolvedActions || resolvedActions.length === 0) return null;
|
|
3638
|
+
if (!resolvedActions.some((action$1) => action$1.type !== "custom")) {
|
|
3639
|
+
const startNodes = resolvedActions.filter(isBatchCustomAction).filter((action$1) => action$1.position === "start").map((action$1, index$1) => renderCustomAction(action$1, index$1));
|
|
3640
|
+
const endNodes = resolvedActions.filter(isBatchCustomAction).filter((action$1) => action$1.position !== "start").map((action$1, index$1) => renderCustomAction(action$1, index$1));
|
|
4572
3641
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4573
3642
|
startNodes,
|
|
4574
3643
|
renderBuiltinAction({ type: "batchUpdate" }),
|
|
@@ -4578,9 +3647,9 @@ function AutoTableActionBar({ table, onDeleteSelected, onUpdateSelected, batchUp
|
|
|
4578
3647
|
endNodes
|
|
4579
3648
|
] });
|
|
4580
3649
|
}
|
|
4581
|
-
return resolvedActions.map((action, index) => {
|
|
4582
|
-
if (action.type === "custom") return renderCustomAction(action, index);
|
|
4583
|
-
return /* @__PURE__ */ jsx(React.Fragment, { children: renderBuiltinAction(action) }, action.type);
|
|
3650
|
+
return resolvedActions.map((action$1, index$1) => {
|
|
3651
|
+
if (action$1.type === "custom") return renderCustomAction(action$1, index$1);
|
|
3652
|
+
return /* @__PURE__ */ jsx(React.Fragment, { children: renderBuiltinAction(action$1) }, action$1.type);
|
|
4584
3653
|
});
|
|
4585
3654
|
}, [
|
|
4586
3655
|
actions,
|
|
@@ -4864,7 +3933,7 @@ const filterModeConfig = {
|
|
|
4864
3933
|
tooltip: "Linear-style command filters"
|
|
4865
3934
|
}
|
|
4866
3935
|
};
|
|
4867
|
-
function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", search = false, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, onSelectedRowsChange, getSelectedRows }) {
|
|
3936
|
+
function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", search = false, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, deleteConfirmation, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, onSelectedRowsChange, getSelectedRows }) {
|
|
4868
3937
|
const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
|
|
4869
3938
|
const [currentMode, setCurrentMode] = useQueryState("filterMode", parseAsStringEnum(modes).withDefault(modes[0] ?? "simple"));
|
|
4870
3939
|
const showToggle = modes.length > 1;
|
|
@@ -5041,7 +4110,8 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
|
|
|
5041
4110
|
enableExport,
|
|
5042
4111
|
showDefaultExport,
|
|
5043
4112
|
extraActions: actionBarExtra,
|
|
5044
|
-
actions: actionBarActions
|
|
4113
|
+
actions: actionBarActions,
|
|
4114
|
+
deleteConfirmation
|
|
5045
4115
|
})
|
|
5046
4116
|
]
|
|
5047
4117
|
});
|
|
@@ -5201,39 +4271,39 @@ function createEditFormSchema(schema, options) {
|
|
|
5201
4271
|
//#endregion
|
|
5202
4272
|
//#region src/lib/registries.ts
|
|
5203
4273
|
function createRegistry(label) {
|
|
5204
|
-
const entries = /* @__PURE__ */ new Map();
|
|
5205
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
5206
|
-
let notifyScheduled = false;
|
|
5207
|
-
const notify = () => {
|
|
5208
|
-
if (notifyScheduled) return;
|
|
5209
|
-
notifyScheduled = true;
|
|
4274
|
+
const entries$1 = /* @__PURE__ */ new Map();
|
|
4275
|
+
const listeners$1 = /* @__PURE__ */ new Set();
|
|
4276
|
+
let notifyScheduled$1 = false;
|
|
4277
|
+
const notify$1 = () => {
|
|
4278
|
+
if (notifyScheduled$1) return;
|
|
4279
|
+
notifyScheduled$1 = true;
|
|
5210
4280
|
queueMicrotask(() => {
|
|
5211
|
-
notifyScheduled = false;
|
|
5212
|
-
for (const listener of listeners) listener();
|
|
4281
|
+
notifyScheduled$1 = false;
|
|
4282
|
+
for (const listener of listeners$1) listener();
|
|
5213
4283
|
});
|
|
5214
4284
|
};
|
|
5215
4285
|
return {
|
|
5216
4286
|
register(name, value) {
|
|
5217
|
-
if (entries.has(name)) {
|
|
4287
|
+
if (entries$1.has(name)) {
|
|
5218
4288
|
console.warn(`[AutoCrud] ${label} "${name}" is already registered.`);
|
|
5219
4289
|
return;
|
|
5220
4290
|
}
|
|
5221
|
-
entries.set(name, value);
|
|
5222
|
-
notify();
|
|
4291
|
+
entries$1.set(name, value);
|
|
4292
|
+
notify$1();
|
|
5223
4293
|
},
|
|
5224
4294
|
unregister(name) {
|
|
5225
|
-
if (!entries.delete(name)) return;
|
|
5226
|
-
notify();
|
|
4295
|
+
if (!entries$1.delete(name)) return;
|
|
4296
|
+
notify$1();
|
|
5227
4297
|
},
|
|
5228
4298
|
get(name) {
|
|
5229
|
-
return entries.get(name);
|
|
4299
|
+
return entries$1.get(name);
|
|
5230
4300
|
},
|
|
5231
4301
|
all() {
|
|
5232
|
-
return Object.fromEntries(entries);
|
|
4302
|
+
return Object.fromEntries(entries$1);
|
|
5233
4303
|
},
|
|
5234
4304
|
subscribe(listener) {
|
|
5235
|
-
listeners.add(listener);
|
|
5236
|
-
return () => listeners.delete(listener);
|
|
4305
|
+
listeners$1.add(listener);
|
|
4306
|
+
return () => listeners$1.delete(listener);
|
|
5237
4307
|
}
|
|
5238
4308
|
};
|
|
5239
4309
|
}
|
|
@@ -5243,11 +4313,19 @@ function normalizeDataSourceRegistration(registration) {
|
|
|
5243
4313
|
if (typeof registration === "function") return {
|
|
5244
4314
|
dependencies: [],
|
|
5245
4315
|
reset: false,
|
|
4316
|
+
search: false,
|
|
4317
|
+
debounceMs: 300,
|
|
4318
|
+
loadMore: false,
|
|
4319
|
+
pageSize: 20,
|
|
5246
4320
|
load: registration
|
|
5247
4321
|
};
|
|
5248
4322
|
return {
|
|
5249
4323
|
dependencies: Array.isArray(registration.dependencies) ? registration.dependencies : [],
|
|
5250
4324
|
reset: registration.reset === true,
|
|
4325
|
+
search: registration.search === true,
|
|
4326
|
+
debounceMs: typeof registration.debounceMs === "number" && registration.debounceMs >= 0 ? registration.debounceMs : 300,
|
|
4327
|
+
loadMore: registration.loadMore === true,
|
|
4328
|
+
pageSize: typeof registration.pageSize === "number" && registration.pageSize > 0 ? registration.pageSize : 20,
|
|
5251
4329
|
load: registration.load
|
|
5252
4330
|
};
|
|
5253
4331
|
}
|
|
@@ -5274,10 +4352,63 @@ function normalizeOptions(result) {
|
|
|
5274
4352
|
value: String(option.value)
|
|
5275
4353
|
}));
|
|
5276
4354
|
}
|
|
4355
|
+
function normalizeHasMore(result) {
|
|
4356
|
+
return !Array.isArray(result) && result?.hasMore === true;
|
|
4357
|
+
}
|
|
4358
|
+
|
|
4359
|
+
//#endregion
|
|
4360
|
+
//#region src/lib/field-config.ts
|
|
4361
|
+
function normalizeFieldOptions$1(options) {
|
|
4362
|
+
if (!options || options.length === 0) return void 0;
|
|
4363
|
+
return options.map((option) => ({
|
|
4364
|
+
...option,
|
|
4365
|
+
value: String(option.value)
|
|
4366
|
+
}));
|
|
4367
|
+
}
|
|
4368
|
+
/**
|
|
4369
|
+
* 从统一字段配置生成 Formily overrides。
|
|
4370
|
+
* `legacyOverrides` 保持兼容旧 API,字段级 `form` 配置优先级最高。
|
|
4371
|
+
*/
|
|
4372
|
+
function buildFormOverrides(fields, legacyOverrides, denyFields) {
|
|
4373
|
+
const result = { ...legacyOverrides };
|
|
4374
|
+
if (denyFields) for (const field of denyFields) result[field] = {
|
|
4375
|
+
...result[field],
|
|
4376
|
+
"x-hidden": true
|
|
4377
|
+
};
|
|
4378
|
+
if (fields) for (const [key, config] of Object.entries(fields)) {
|
|
4379
|
+
const fieldOptions = normalizeFieldOptions$1((config.form && typeof config.form === "object" ? config.form : void 0)?.enum) ?? normalizeFieldOptions$1(config.enum);
|
|
4380
|
+
const fieldDataSource = fieldOptions || !config.dataSource ? void 0 : normalizeDataSourceConfig(config.dataSource);
|
|
4381
|
+
if (config.label) result[key] = {
|
|
4382
|
+
...result[key],
|
|
4383
|
+
title: config.label
|
|
4384
|
+
};
|
|
4385
|
+
if (config.hidden) result[key] = {
|
|
4386
|
+
...result[key],
|
|
4387
|
+
"x-hidden": true
|
|
4388
|
+
};
|
|
4389
|
+
if (fieldOptions) result[key] = {
|
|
4390
|
+
...result[key],
|
|
4391
|
+
enum: fieldOptions
|
|
4392
|
+
};
|
|
4393
|
+
if (fieldDataSource) result[key] = {
|
|
4394
|
+
...result[key],
|
|
4395
|
+
"x-data-source": config.dataSource
|
|
4396
|
+
};
|
|
4397
|
+
if (config.form === false) result[key] = {
|
|
4398
|
+
...result[key],
|
|
4399
|
+
"x-hidden": true
|
|
4400
|
+
};
|
|
4401
|
+
else if (config.form && typeof config.form === "object") result[key] = {
|
|
4402
|
+
...result[key],
|
|
4403
|
+
...config.form
|
|
4404
|
+
};
|
|
4405
|
+
}
|
|
4406
|
+
return result;
|
|
4407
|
+
}
|
|
5277
4408
|
|
|
5278
4409
|
//#endregion
|
|
5279
4410
|
//#region src/components/auto-crud/auto-form.tsx
|
|
5280
|
-
const COMBOBOX_LIST_CLASS = "[&_[
|
|
4411
|
+
const COMBOBOX_LIST_CLASS = "[&_[data-multi-combobox-viewport]]:max-h-[360px] [&_[data-multi-combobox-viewport]]:overflow-x-hidden [&_[data-multi-combobox-viewport]]:overflow-y-auto";
|
|
5281
4412
|
const FormilySwitch = connect(Switch, mapProps({
|
|
5282
4413
|
value: "checked",
|
|
5283
4414
|
onInput: "onCheckedChange"
|
|
@@ -5287,14 +4418,14 @@ const FormilyMultiCombobox = connect(AutoCrudMultiCombobox, mapProps({
|
|
|
5287
4418
|
dataSource: "options",
|
|
5288
4419
|
onInput: "onChange"
|
|
5289
4420
|
}, (props, field) => {
|
|
5290
|
-
const fieldValue = field.value;
|
|
4421
|
+
const fieldValue = field && typeof field === "object" && "value" in field ? field.value : props.value;
|
|
5291
4422
|
return {
|
|
5292
4423
|
...props,
|
|
5293
4424
|
value: Array.isArray(fieldValue) ? fieldValue.map(String) : []
|
|
5294
4425
|
};
|
|
5295
4426
|
}));
|
|
5296
4427
|
function AutoCrudCombobox({ options = [], className, value, onChange,...props }) {
|
|
5297
|
-
return /* @__PURE__ */ jsx(MultiCombobox, {
|
|
4428
|
+
return /* @__PURE__ */ jsx(MultiCombobox$1, {
|
|
5298
4429
|
...props,
|
|
5299
4430
|
value: value ? [value] : [],
|
|
5300
4431
|
options,
|
|
@@ -5304,7 +4435,7 @@ function AutoCrudCombobox({ options = [], className, value, onChange,...props })
|
|
|
5304
4435
|
});
|
|
5305
4436
|
}
|
|
5306
4437
|
function AutoCrudMultiCombobox({ options = [],...props }) {
|
|
5307
|
-
return /* @__PURE__ */ jsx(MultiCombobox, {
|
|
4438
|
+
return /* @__PURE__ */ jsx(MultiCombobox$1, {
|
|
5308
4439
|
...props,
|
|
5309
4440
|
options
|
|
5310
4441
|
});
|
|
@@ -5323,99 +4454,280 @@ const defaultFieldComponents = {
|
|
|
5323
4454
|
decorator: "FormItem"
|
|
5324
4455
|
}
|
|
5325
4456
|
};
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
4457
|
+
const AUTO_CRUD_DATA_SOURCE_SCOPE_KEY = "$autoCrudDataSource";
|
|
4458
|
+
const POPUP_SCROLL_LOAD_THRESHOLD$1 = 24;
|
|
4459
|
+
const dataSourceReactionStates = /* @__PURE__ */ new WeakMap();
|
|
4460
|
+
function createBoundAction(callback) {
|
|
4461
|
+
return action.bound?.(callback) ?? ((...args) => action(() => callback(...args)));
|
|
4462
|
+
}
|
|
4463
|
+
function useRegistryVersion$1(subscribe, onChange) {
|
|
4464
|
+
const [version$1, setVersion] = useState(0);
|
|
4465
|
+
useEffect(() => subscribe(() => {
|
|
4466
|
+
onChange?.();
|
|
4467
|
+
setVersion((current) => current + 1);
|
|
4468
|
+
}), [onChange, subscribe]);
|
|
4469
|
+
return version$1;
|
|
5330
4470
|
}
|
|
5331
4471
|
function isDataSourceConfig(value) {
|
|
5332
4472
|
return typeof value === "string" && value.length > 0;
|
|
5333
4473
|
}
|
|
5334
|
-
function
|
|
4474
|
+
function getSchemaDataSourceConfig(schema) {
|
|
4475
|
+
const customDataSource = schema["x-data-source"];
|
|
4476
|
+
const schemaDataSource = schema.dataSource;
|
|
4477
|
+
return isDataSourceConfig(customDataSource) ? customDataSource : isDataSourceConfig(schemaDataSource) ? schemaDataSource : void 0;
|
|
4478
|
+
}
|
|
4479
|
+
function mergeReactions(existing, reaction) {
|
|
4480
|
+
if (existing == null) return reaction;
|
|
4481
|
+
if (existing === reaction) return existing;
|
|
4482
|
+
if (Array.isArray(existing)) return existing.includes(reaction) ? existing : [...existing, reaction];
|
|
4483
|
+
return [existing, reaction];
|
|
4484
|
+
}
|
|
4485
|
+
function applyDataSourceReactions(schema) {
|
|
4486
|
+
const dataSourceConfig = getSchemaDataSourceConfig(schema);
|
|
4487
|
+
if (dataSourceConfig) {
|
|
4488
|
+
const reaction = `{{${AUTO_CRUD_DATA_SOURCE_SCOPE_KEY}(${JSON.stringify(dataSourceConfig)})}}`;
|
|
4489
|
+
schema["x-reactions"] = mergeReactions(schema["x-reactions"], reaction);
|
|
4490
|
+
}
|
|
5335
4491
|
const properties = schema.properties;
|
|
5336
|
-
if (
|
|
5337
|
-
|
|
5338
|
-
if (!property || typeof property !== "object" || Array.isArray(property)) continue;
|
|
5339
|
-
const record = property;
|
|
5340
|
-
const customDataSource = record["x-data-source"];
|
|
5341
|
-
const schemaDataSource = record.dataSource;
|
|
5342
|
-
const dataSourceConfig = isDataSourceConfig(customDataSource) ? customDataSource : isDataSourceConfig(schemaDataSource) ? schemaDataSource : void 0;
|
|
5343
|
-
if (dataSourceConfig) result[key] = dataSourceConfig;
|
|
5344
|
-
collectDataSourceConfigs(record, result);
|
|
4492
|
+
if (properties && typeof properties === "object" && !Array.isArray(properties)) {
|
|
4493
|
+
for (const property of Object.values(properties)) if (property && typeof property === "object" && !Array.isArray(property)) applyDataSourceReactions(property);
|
|
5345
4494
|
}
|
|
5346
|
-
|
|
4495
|
+
const items = schema.items;
|
|
4496
|
+
if (Array.isArray(items)) {
|
|
4497
|
+
for (const item of items) if (item && typeof item === "object") applyDataSourceReactions(item);
|
|
4498
|
+
} else if (items && typeof items === "object") applyDataSourceReactions(items);
|
|
4499
|
+
}
|
|
4500
|
+
function getDataSourceReactionState(field) {
|
|
4501
|
+
const existing = dataSourceReactionStates.get(field);
|
|
4502
|
+
if (existing) return existing;
|
|
4503
|
+
const next = {
|
|
4504
|
+
hasMore: false,
|
|
4505
|
+
initialized: false,
|
|
4506
|
+
appendLoading: false,
|
|
4507
|
+
options: [],
|
|
4508
|
+
page: 0,
|
|
4509
|
+
requestVersion: 0
|
|
4510
|
+
};
|
|
4511
|
+
dataSourceReactionStates.set(field, next);
|
|
4512
|
+
return next;
|
|
4513
|
+
}
|
|
4514
|
+
function mergeDataSourceOptions(current, incoming) {
|
|
4515
|
+
const optionsByValue = new Map(current.map((option) => [option.value, option]));
|
|
4516
|
+
for (const option of incoming) optionsByValue.set(option.value, option);
|
|
4517
|
+
return Array.from(optionsByValue.values());
|
|
4518
|
+
}
|
|
4519
|
+
function isNearPopupScrollBottom$1(target) {
|
|
4520
|
+
return target.scrollHeight - target.scrollTop - target.clientHeight <= POPUP_SCROLL_LOAD_THRESHOLD$1;
|
|
4521
|
+
}
|
|
4522
|
+
function getDataSourceDependencyValues(entry, field) {
|
|
4523
|
+
return Object.fromEntries(entry.dependencies.map((dependency) => [dependency, field.form.getValuesIn(dependency)]));
|
|
4524
|
+
}
|
|
4525
|
+
function loadAutoCrudDataSource({ entry, field, registryVersion, search, sourceKey, state, append = false }) {
|
|
4526
|
+
const values = getDataSourceDependencyValues(entry, field);
|
|
4527
|
+
const dependencyKey = JSON.stringify(values);
|
|
4528
|
+
const searchValue = entry.search ? search ?? "" : void 0;
|
|
4529
|
+
const page = entry.loadMore ? append ? state.page + 1 : 1 : void 0;
|
|
4530
|
+
const loadKey = JSON.stringify({
|
|
4531
|
+
values,
|
|
4532
|
+
search: searchValue
|
|
4533
|
+
});
|
|
4534
|
+
if (!append && state.loadKey === loadKey && state.registryVersion === registryVersion) return;
|
|
4535
|
+
if (entry.reset && state.dependencyKey !== void 0 && state.dependencyKey !== dependencyKey) field.setValue(void 0);
|
|
4536
|
+
state.dependencyKey = dependencyKey;
|
|
4537
|
+
if (!append) {
|
|
4538
|
+
state.loadKey = loadKey;
|
|
4539
|
+
state.options = [];
|
|
4540
|
+
state.page = 0;
|
|
4541
|
+
state.hasMore = false;
|
|
4542
|
+
}
|
|
4543
|
+
state.registryVersion = registryVersion;
|
|
4544
|
+
state.controller?.abort();
|
|
4545
|
+
const requestVersion = state.requestVersion + 1;
|
|
4546
|
+
state.requestVersion = requestVersion;
|
|
4547
|
+
const controller = typeof AbortController === "undefined" ? void 0 : new AbortController();
|
|
4548
|
+
state.controller = controller;
|
|
4549
|
+
if (append) {
|
|
4550
|
+
state.appendLoading = true;
|
|
4551
|
+
field.setComponentProps({ loading: true });
|
|
4552
|
+
} else {
|
|
4553
|
+
field.setLoading(true);
|
|
4554
|
+
field.setComponentProps({
|
|
4555
|
+
loading: true,
|
|
4556
|
+
...entry.loadMore ? { hasMore: false } : {}
|
|
4557
|
+
});
|
|
4558
|
+
}
|
|
4559
|
+
Promise.resolve(entry.load({
|
|
4560
|
+
field: field.path.toString(),
|
|
4561
|
+
page,
|
|
4562
|
+
pageSize: entry.loadMore ? entry.pageSize : void 0,
|
|
4563
|
+
search: searchValue,
|
|
4564
|
+
values,
|
|
4565
|
+
signal: controller?.signal
|
|
4566
|
+
})).then(createBoundAction((result) => {
|
|
4567
|
+
if (dataSourceReactionStates.get(field)?.requestVersion !== requestVersion || controller?.signal.aborted) return;
|
|
4568
|
+
const options = normalizeOptions(result);
|
|
4569
|
+
const nextOptions = append ? mergeDataSourceOptions(state.options, options) : options;
|
|
4570
|
+
state.options = nextOptions;
|
|
4571
|
+
state.page = page ?? 0;
|
|
4572
|
+
state.hasMore = entry.loadMore ? normalizeHasMore(result) : false;
|
|
4573
|
+
state.appendLoading = false;
|
|
4574
|
+
field.setDataSource(nextOptions);
|
|
4575
|
+
field.setLoading(false);
|
|
4576
|
+
field.setComponentProps({
|
|
4577
|
+
loading: false,
|
|
4578
|
+
...entry.loadMore ? { hasMore: state.hasMore } : {}
|
|
4579
|
+
});
|
|
4580
|
+
}), createBoundAction((error) => {
|
|
4581
|
+
if (dataSourceReactionStates.get(field)?.requestVersion !== requestVersion || controller?.signal.aborted) return;
|
|
4582
|
+
console.warn(`[AutoCrud] Failed to load data source "${sourceKey}".`, error);
|
|
4583
|
+
if (!append) {
|
|
4584
|
+
state.options = [];
|
|
4585
|
+
state.hasMore = false;
|
|
4586
|
+
field.setDataSource([]);
|
|
4587
|
+
}
|
|
4588
|
+
state.appendLoading = false;
|
|
4589
|
+
field.setLoading(false);
|
|
4590
|
+
field.setComponentProps({
|
|
4591
|
+
loading: false,
|
|
4592
|
+
...entry.loadMore ? { hasMore: state.hasMore } : {}
|
|
4593
|
+
});
|
|
4594
|
+
}));
|
|
5347
4595
|
}
|
|
5348
|
-
function
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
}
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
}));
|
|
5375
|
-
if (!active || loadVersions.get(fieldName) !== version) return;
|
|
5376
|
-
form.setFieldState(fieldName, (state) => {
|
|
5377
|
-
state.dataSource = options;
|
|
4596
|
+
function applySearchComponentProps({ entry, field, registryState, sourceKey, state }) {
|
|
4597
|
+
if (!entry.search) return;
|
|
4598
|
+
field.setComponentProps({
|
|
4599
|
+
searchValue: state.search ?? "",
|
|
4600
|
+
shouldFilter: false,
|
|
4601
|
+
onSearch: (search) => {
|
|
4602
|
+
if (state.search === search) return;
|
|
4603
|
+
state.search = search;
|
|
4604
|
+
field.setComponentProps({ searchValue: search });
|
|
4605
|
+
if (state.searchTimer) clearTimeout(state.searchTimer);
|
|
4606
|
+
state.searchTimer = setTimeout(() => {
|
|
4607
|
+
const currentEntry = dataSources.get(sourceKey);
|
|
4608
|
+
if (!currentEntry) {
|
|
4609
|
+
state.controller?.abort();
|
|
4610
|
+
field.setDataSource([]);
|
|
4611
|
+
field.setLoading(false);
|
|
4612
|
+
field.setComponentProps({ loading: false });
|
|
4613
|
+
return;
|
|
4614
|
+
}
|
|
4615
|
+
loadAutoCrudDataSource({
|
|
4616
|
+
entry: currentEntry,
|
|
4617
|
+
field,
|
|
4618
|
+
registryVersion: registryState.version,
|
|
4619
|
+
search,
|
|
4620
|
+
sourceKey,
|
|
4621
|
+
state
|
|
5378
4622
|
});
|
|
5379
|
-
}
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
4623
|
+
}, entry.debounceMs);
|
|
4624
|
+
}
|
|
4625
|
+
});
|
|
4626
|
+
}
|
|
4627
|
+
function applyLoadMoreComponentProps({ entry, field, registryState, sourceKey, state }) {
|
|
4628
|
+
if (!entry.loadMore) return;
|
|
4629
|
+
field.setComponentProps({
|
|
4630
|
+
hasMore: state.hasMore,
|
|
4631
|
+
loading: state.appendLoading,
|
|
4632
|
+
onPopupScroll: (event) => {
|
|
4633
|
+
if (!isNearPopupScrollBottom$1(event.currentTarget)) return;
|
|
4634
|
+
if (!state.hasMore || state.appendLoading) return;
|
|
4635
|
+
const currentEntry = dataSources.get(sourceKey);
|
|
4636
|
+
if (!currentEntry) {
|
|
4637
|
+
state.controller?.abort();
|
|
4638
|
+
state.hasMore = false;
|
|
4639
|
+
state.appendLoading = false;
|
|
4640
|
+
field.setDataSource([]);
|
|
4641
|
+
field.setLoading(false);
|
|
4642
|
+
field.setComponentProps({
|
|
4643
|
+
hasMore: false,
|
|
4644
|
+
loading: false
|
|
5384
4645
|
});
|
|
4646
|
+
return;
|
|
5385
4647
|
}
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
loadFieldOptions(fieldName, source);
|
|
5395
|
-
}
|
|
4648
|
+
loadAutoCrudDataSource({
|
|
4649
|
+
append: true,
|
|
4650
|
+
entry: currentEntry,
|
|
4651
|
+
field,
|
|
4652
|
+
registryVersion: registryState.version,
|
|
4653
|
+
search: state.search,
|
|
4654
|
+
sourceKey,
|
|
4655
|
+
state
|
|
5396
4656
|
});
|
|
4657
|
+
}
|
|
4658
|
+
});
|
|
4659
|
+
}
|
|
4660
|
+
function createAutoCrudDataSourceReaction(config, registryState) {
|
|
4661
|
+
const source = normalizeDataSourceConfig(config);
|
|
4662
|
+
return (field) => {
|
|
4663
|
+
const registryVersion = registryState.version;
|
|
4664
|
+
const state = getDataSourceReactionState(field);
|
|
4665
|
+
if (!state.initialized) {
|
|
4666
|
+
field.disposers.push(() => {
|
|
4667
|
+
state.controller?.abort();
|
|
4668
|
+
if (state.searchTimer) clearTimeout(state.searchTimer);
|
|
4669
|
+
});
|
|
4670
|
+
state.initialized = true;
|
|
4671
|
+
}
|
|
4672
|
+
if (!source) {
|
|
4673
|
+
field.setDataSource([]);
|
|
4674
|
+
field.setLoading(false);
|
|
4675
|
+
return;
|
|
4676
|
+
}
|
|
4677
|
+
const entry = dataSources.get(source.key);
|
|
4678
|
+
if (!entry) {
|
|
4679
|
+
state.controller?.abort();
|
|
4680
|
+
field.setDataSource([]);
|
|
4681
|
+
field.setLoading(false);
|
|
4682
|
+
return;
|
|
4683
|
+
}
|
|
4684
|
+
applySearchComponentProps({
|
|
4685
|
+
entry,
|
|
4686
|
+
field,
|
|
4687
|
+
registryState,
|
|
4688
|
+
sourceKey: source.key,
|
|
4689
|
+
state
|
|
5397
4690
|
});
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
4691
|
+
applyLoadMoreComponentProps({
|
|
4692
|
+
entry,
|
|
4693
|
+
field,
|
|
4694
|
+
registryState,
|
|
4695
|
+
sourceKey: source.key,
|
|
4696
|
+
state
|
|
4697
|
+
});
|
|
4698
|
+
loadAutoCrudDataSource({
|
|
4699
|
+
entry,
|
|
4700
|
+
field,
|
|
4701
|
+
registryVersion,
|
|
4702
|
+
search: state.search,
|
|
4703
|
+
sourceKey: source.key,
|
|
4704
|
+
state
|
|
4705
|
+
});
|
|
4706
|
+
};
|
|
5408
4707
|
}
|
|
5409
|
-
function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides, mode = "create", loading = false, gridColumns = 1, labelAlign = "top", labelWidth, showSubmitButton = true }, ref) {
|
|
4708
|
+
function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, fields, overrides, scope: scopeProp, mode = "create", loading = false, gridColumns = 1, labelAlign = "top", labelWidth, showSubmitButton = true }, ref) {
|
|
5410
4709
|
const form = useMemo(() => createForm({ initialValues }), [JSON.stringify(initialValues)]);
|
|
5411
|
-
const
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
4710
|
+
const dataSourceRegistryState = useMemo(() => observable({ version: 0 }), []);
|
|
4711
|
+
const updateDataSourceRegistryState = useCallback(() => {
|
|
4712
|
+
action(() => {
|
|
4713
|
+
dataSourceRegistryState.version += 1;
|
|
4714
|
+
});
|
|
4715
|
+
}, [dataSourceRegistryState]);
|
|
4716
|
+
const dataSourceRegistryVersion = useRegistryVersion$1(dataSources.subscribe, updateDataSourceRegistryState);
|
|
4717
|
+
const formOverrides = useMemo(() => buildFormOverrides(fields, overrides), [fields, overrides]);
|
|
4718
|
+
const formSchema = useMemo(() => {
|
|
4719
|
+
const nextSchema = createEditFormSchema(zodSchema, {
|
|
4720
|
+
overrides: formOverrides,
|
|
4721
|
+
layout: "grid",
|
|
4722
|
+
gridColumns
|
|
4723
|
+
});
|
|
4724
|
+
applyDataSourceReactions(nextSchema);
|
|
4725
|
+
return nextSchema;
|
|
4726
|
+
}, [
|
|
5416
4727
|
zodSchema,
|
|
5417
|
-
|
|
5418
|
-
gridColumns
|
|
4728
|
+
formOverrides,
|
|
4729
|
+
gridColumns,
|
|
4730
|
+
dataSourceRegistryVersion
|
|
5419
4731
|
]);
|
|
5420
4732
|
const formLayout = useMemo(() => {
|
|
5421
4733
|
const inlineLabel = labelAlign === "left" || labelAlign === "right";
|
|
@@ -5431,7 +4743,10 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
|
|
|
5431
4743
|
...defaultFieldComponents,
|
|
5432
4744
|
...components.all()
|
|
5433
4745
|
} }), [useRegistryVersion$1(components.subscribe)]);
|
|
5434
|
-
|
|
4746
|
+
const scope = useMemo(() => ({
|
|
4747
|
+
...scopeProp,
|
|
4748
|
+
[AUTO_CRUD_DATA_SOURCE_SCOPE_KEY]: (config) => createAutoCrudDataSourceReaction(config, dataSourceRegistryState)
|
|
4749
|
+
}), [dataSourceRegistryState, scopeProp]);
|
|
5435
4750
|
const handleSubmit = async () => {
|
|
5436
4751
|
await form.validate();
|
|
5437
4752
|
if (form.valid) await onSubmit(form.values);
|
|
@@ -5442,7 +4757,8 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
|
|
|
5442
4757
|
layout: formLayout,
|
|
5443
4758
|
children: [/* @__PURE__ */ jsx(JsonSchemaField, {
|
|
5444
4759
|
schema: formSchema,
|
|
5445
|
-
components: fieldComponents
|
|
4760
|
+
components: fieldComponents,
|
|
4761
|
+
scope
|
|
5446
4762
|
}), showSubmitButton && /* @__PURE__ */ jsx("div", {
|
|
5447
4763
|
className: "flex justify-end gap-2 mt-6",
|
|
5448
4764
|
children: /* @__PURE__ */ jsx(Button, {
|
|
@@ -5491,6 +4807,12 @@ const zhCN = {
|
|
|
5491
4807
|
confirm: "确认删除",
|
|
5492
4808
|
confirming: "删除中..."
|
|
5493
4809
|
},
|
|
4810
|
+
bulkDeleteModal: {
|
|
4811
|
+
title: "确认批量删除",
|
|
4812
|
+
description: (count) => `此操作无法撤销。确定要删除选中的 ${count} 条记录吗?`,
|
|
4813
|
+
cancel: "取消",
|
|
4814
|
+
confirm: "确认删除"
|
|
4815
|
+
},
|
|
5494
4816
|
importDialog: {
|
|
5495
4817
|
title: "导入数据",
|
|
5496
4818
|
uploadDescription: "上传 CSV 或 JSON 文件以批量导入数据",
|
|
@@ -5551,6 +4873,12 @@ const enUS = {
|
|
|
5551
4873
|
confirm: "Delete",
|
|
5552
4874
|
confirming: "Deleting..."
|
|
5553
4875
|
},
|
|
4876
|
+
bulkDeleteModal: {
|
|
4877
|
+
title: "Confirm Bulk Delete",
|
|
4878
|
+
description: (count) => `This action cannot be undone. Are you sure you want to delete ${count} selected ${count === 1 ? "record" : "records"}?`,
|
|
4879
|
+
cancel: "Cancel",
|
|
4880
|
+
confirm: "Delete"
|
|
4881
|
+
},
|
|
5554
4882
|
importDialog: {
|
|
5555
4883
|
title: "Import Data",
|
|
5556
4884
|
uploadDescription: "Upload a CSV or JSON file to bulk import data",
|
|
@@ -5611,6 +4939,12 @@ const jaJP = {
|
|
|
5611
4939
|
confirm: "削除する",
|
|
5612
4940
|
confirming: "削除中..."
|
|
5613
4941
|
},
|
|
4942
|
+
bulkDeleteModal: {
|
|
4943
|
+
title: "一括削除の確認",
|
|
4944
|
+
description: (count) => `この操作は元に戻せません。選択した${count}件のレコードを削除してもよろしいですか?`,
|
|
4945
|
+
cancel: "キャンセル",
|
|
4946
|
+
confirm: "削除する"
|
|
4947
|
+
},
|
|
5614
4948
|
importDialog: {
|
|
5615
4949
|
title: "データのインポート",
|
|
5616
4950
|
uploadDescription: "CSVまたはJSONファイルをアップロードして一括インポート",
|
|
@@ -5671,6 +5005,12 @@ const koKR = {
|
|
|
5671
5005
|
confirm: "삭제",
|
|
5672
5006
|
confirming: "삭제 중..."
|
|
5673
5007
|
},
|
|
5008
|
+
bulkDeleteModal: {
|
|
5009
|
+
title: "일괄 삭제 확인",
|
|
5010
|
+
description: (count) => `이 작업은 취소할 수 없습니다. 선택한 ${count}개 레코드를 삭제하시겠습니까?`,
|
|
5011
|
+
cancel: "취소",
|
|
5012
|
+
confirm: "삭제"
|
|
5013
|
+
},
|
|
5674
5014
|
importDialog: {
|
|
5675
5015
|
title: "데이터 가져오기",
|
|
5676
5016
|
uploadDescription: "CSV 또는 JSON 파일을 업로드하여 일괄 가져오기",
|
|
@@ -5731,6 +5071,12 @@ const frFR = {
|
|
|
5731
5071
|
confirm: "Supprimer",
|
|
5732
5072
|
confirming: "Suppression..."
|
|
5733
5073
|
},
|
|
5074
|
+
bulkDeleteModal: {
|
|
5075
|
+
title: "Confirmer la suppression en masse",
|
|
5076
|
+
description: (count) => `Cette action est irréversible. Voulez-vous vraiment supprimer les ${count} enregistrements sélectionnés ?`,
|
|
5077
|
+
cancel: "Annuler",
|
|
5078
|
+
confirm: "Supprimer"
|
|
5079
|
+
},
|
|
5734
5080
|
importDialog: {
|
|
5735
5081
|
title: "Importer des données",
|
|
5736
5082
|
uploadDescription: "Téléchargez un fichier CSV ou JSON pour importer en masse",
|
|
@@ -5791,6 +5137,12 @@ const deDE = {
|
|
|
5791
5137
|
confirm: "Löschen",
|
|
5792
5138
|
confirming: "Wird gelöscht..."
|
|
5793
5139
|
},
|
|
5140
|
+
bulkDeleteModal: {
|
|
5141
|
+
title: "Mehrfaches Löschen bestätigen",
|
|
5142
|
+
description: (count) => `Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie die ${count} ausgewählten Datensätze wirklich löschen?`,
|
|
5143
|
+
cancel: "Abbrechen",
|
|
5144
|
+
confirm: "Löschen"
|
|
5145
|
+
},
|
|
5794
5146
|
importDialog: {
|
|
5795
5147
|
title: "Daten importieren",
|
|
5796
5148
|
uploadDescription: "CSV- oder JSON-Datei hochladen, um Daten massenweise zu importieren",
|
|
@@ -5851,6 +5203,12 @@ const esES = {
|
|
|
5851
5203
|
confirm: "Eliminar",
|
|
5852
5204
|
confirming: "Eliminando..."
|
|
5853
5205
|
},
|
|
5206
|
+
bulkDeleteModal: {
|
|
5207
|
+
title: "Confirmar eliminación masiva",
|
|
5208
|
+
description: (count) => `Esta acción no se puede deshacer. ¿Está seguro de que desea eliminar los ${count} registros seleccionados?`,
|
|
5209
|
+
cancel: "Cancelar",
|
|
5210
|
+
confirm: "Eliminar"
|
|
5211
|
+
},
|
|
5854
5212
|
importDialog: {
|
|
5855
5213
|
title: "Importar datos",
|
|
5856
5214
|
uploadDescription: "Suba un archivo CSV o JSON para importar datos de forma masiva",
|
|
@@ -5910,7 +5268,7 @@ function resolveLocale(localeProp) {
|
|
|
5910
5268
|
|
|
5911
5269
|
//#endregion
|
|
5912
5270
|
//#region src/components/auto-crud/crud-form-modal.tsx
|
|
5913
|
-
function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubmit, loading = false, variant = "dialog", overrides, title, locale = zhCN.formModal, gridColumns, labelAlign, labelWidth, className }) {
|
|
5271
|
+
function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubmit, loading = false, variant = "dialog", overrides, scope, title, locale = zhCN.formModal, gridColumns, labelAlign, labelWidth, className }) {
|
|
5914
5272
|
const defaultTitle = mode === "create" ? locale.createTitle : locale.editTitle;
|
|
5915
5273
|
const formRef = useRef(null);
|
|
5916
5274
|
const handleSubmit = async () => {
|
|
@@ -5944,6 +5302,7 @@ function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubm
|
|
|
5944
5302
|
loading,
|
|
5945
5303
|
onSubmit,
|
|
5946
5304
|
overrides,
|
|
5305
|
+
scope,
|
|
5947
5306
|
gridColumns,
|
|
5948
5307
|
labelAlign,
|
|
5949
5308
|
labelWidth,
|
|
@@ -6022,7 +5381,7 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title, local
|
|
|
6022
5381
|
open,
|
|
6023
5382
|
onOpenChange: handleOpenChange,
|
|
6024
5383
|
children: /* @__PURE__ */ jsxs(DialogContent, {
|
|
6025
|
-
className: "sm:max-w-
|
|
5384
|
+
className: "w-[calc(100vw-2rem)] sm:max-w-5xl",
|
|
6026
5385
|
children: [/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: title ?? locale.title }), /* @__PURE__ */ jsxs(DialogDescription, { children: [
|
|
6027
5386
|
step === "upload" && locale.uploadDescription,
|
|
6028
5387
|
step === "preview" && locale.previewDescription(parsedData?.rows.length ?? 0),
|
|
@@ -6083,41 +5442,30 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title, local
|
|
|
6083
5442
|
/* @__PURE__ */ jsx("div", {
|
|
6084
5443
|
className: "rounded-md border",
|
|
6085
5444
|
children: /* @__PURE__ */ jsx("div", {
|
|
6086
|
-
className: "max-h-[
|
|
5445
|
+
className: "max-h-[45vh] overflow-auto",
|
|
6087
5446
|
children: /* @__PURE__ */ jsxs("table", {
|
|
6088
|
-
className: "w-
|
|
5447
|
+
className: "min-w-max text-sm",
|
|
6089
5448
|
children: [/* @__PURE__ */ jsx("thead", {
|
|
6090
5449
|
className: "sticky top-0 bg-muted",
|
|
6091
|
-
children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
children: h
|
|
6099
|
-
}, h)),
|
|
6100
|
-
parsedData.headers.length > 6 && /* @__PURE__ */ jsx("th", {
|
|
6101
|
-
className: "px-3 py-2 text-left font-medium text-muted-foreground",
|
|
6102
|
-
children: locale.moreColumns(parsedData.headers.length - 6)
|
|
6103
|
-
})
|
|
6104
|
-
] })
|
|
5450
|
+
children: /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("th", {
|
|
5451
|
+
className: "sticky left-0 z-10 bg-muted px-3 py-2 text-left font-medium text-muted-foreground",
|
|
5452
|
+
children: "#"
|
|
5453
|
+
}), parsedData.headers.map((h) => /* @__PURE__ */ jsx("th", {
|
|
5454
|
+
className: "max-w-[180px] whitespace-nowrap px-3 py-2 text-left font-medium text-muted-foreground",
|
|
5455
|
+
children: h
|
|
5456
|
+
}, h))] })
|
|
6105
5457
|
}), /* @__PURE__ */ jsx("tbody", { children: parsedData.rows.slice(0, 10).map((row, i) => /* @__PURE__ */ jsxs("tr", {
|
|
6106
5458
|
className: "border-t",
|
|
6107
|
-
children: [
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
5459
|
+
children: [/* @__PURE__ */ jsx("td", {
|
|
5460
|
+
className: "sticky left-0 bg-background px-3 py-2 text-muted-foreground",
|
|
5461
|
+
children: i + 1
|
|
5462
|
+
}), parsedData.headers.map((h) => /* @__PURE__ */ jsx("td", {
|
|
5463
|
+
className: "max-w-[180px] truncate px-3 py-2",
|
|
5464
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
5465
|
+
title: String(row[h] ?? ""),
|
|
6114
5466
|
children: String(row[h] ?? "")
|
|
6115
|
-
}, h)),
|
|
6116
|
-
parsedData.headers.length > 6 && /* @__PURE__ */ jsx("td", {
|
|
6117
|
-
className: "px-3 py-2 text-muted-foreground",
|
|
6118
|
-
children: "..."
|
|
6119
5467
|
})
|
|
6120
|
-
]
|
|
5468
|
+
}, h))]
|
|
6121
5469
|
}, i)) })]
|
|
6122
5470
|
})
|
|
6123
5471
|
})
|
|
@@ -6241,6 +5589,153 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title, local
|
|
|
6241
5589
|
});
|
|
6242
5590
|
}
|
|
6243
5591
|
|
|
5592
|
+
//#endregion
|
|
5593
|
+
//#region src/lib/crud-actions.ts
|
|
5594
|
+
const entries = /* @__PURE__ */ new Map();
|
|
5595
|
+
const index = /* @__PURE__ */ new Map();
|
|
5596
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
5597
|
+
let seq = 0;
|
|
5598
|
+
let version = 0;
|
|
5599
|
+
let notifyScheduled = false;
|
|
5600
|
+
function actionKey(registration, action$1, index$1) {
|
|
5601
|
+
return [
|
|
5602
|
+
registration.ownerId,
|
|
5603
|
+
registration.targetId,
|
|
5604
|
+
registration.zone,
|
|
5605
|
+
action$1.id ?? action$1.type,
|
|
5606
|
+
index$1
|
|
5607
|
+
].join(":");
|
|
5608
|
+
}
|
|
5609
|
+
function indexKey(targetId, zone) {
|
|
5610
|
+
return `${targetId}:${zone}`;
|
|
5611
|
+
}
|
|
5612
|
+
function addToIndex(key, entry) {
|
|
5613
|
+
const bucketKey = indexKey(entry.targetId, entry.zone);
|
|
5614
|
+
const bucket = index.get(bucketKey) ?? /* @__PURE__ */ new Set();
|
|
5615
|
+
bucket.add(key);
|
|
5616
|
+
index.set(bucketKey, bucket);
|
|
5617
|
+
}
|
|
5618
|
+
function removeFromIndex(key, entry) {
|
|
5619
|
+
const bucketKey = indexKey(entry.targetId, entry.zone);
|
|
5620
|
+
const bucket = index.get(bucketKey);
|
|
5621
|
+
if (!bucket) return;
|
|
5622
|
+
bucket.delete(key);
|
|
5623
|
+
if (bucket.size === 0) index.delete(bucketKey);
|
|
5624
|
+
}
|
|
5625
|
+
function notify() {
|
|
5626
|
+
version += 1;
|
|
5627
|
+
if (notifyScheduled) return;
|
|
5628
|
+
notifyScheduled = true;
|
|
5629
|
+
queueMicrotask(() => {
|
|
5630
|
+
notifyScheduled = false;
|
|
5631
|
+
for (const listener of listeners) listener();
|
|
5632
|
+
});
|
|
5633
|
+
}
|
|
5634
|
+
function unregisterOwnerActions(ownerId, options = {}, shouldNotify = true) {
|
|
5635
|
+
let changed = false;
|
|
5636
|
+
for (const [key, entry] of entries) {
|
|
5637
|
+
if (entry.ownerId !== ownerId) continue;
|
|
5638
|
+
if (options.targetId && entry.targetId !== options.targetId) continue;
|
|
5639
|
+
if (options.zone && entry.zone !== options.zone) continue;
|
|
5640
|
+
entries.delete(key);
|
|
5641
|
+
removeFromIndex(key, entry);
|
|
5642
|
+
changed = true;
|
|
5643
|
+
}
|
|
5644
|
+
if (changed && shouldNotify) notify();
|
|
5645
|
+
}
|
|
5646
|
+
function sortEntries(left, right) {
|
|
5647
|
+
const orderDiff = left.order - right.order;
|
|
5648
|
+
if (orderDiff !== 0) return orderDiff;
|
|
5649
|
+
const ownerDiff = left.ownerId.localeCompare(right.ownerId);
|
|
5650
|
+
if (ownerDiff !== 0) return ownerDiff;
|
|
5651
|
+
return left.seq - right.seq;
|
|
5652
|
+
}
|
|
5653
|
+
function withoutRegistryMeta(action$1) {
|
|
5654
|
+
const { id, order,...rest } = action$1;
|
|
5655
|
+
return rest;
|
|
5656
|
+
}
|
|
5657
|
+
function isCustomAction$1(action$1) {
|
|
5658
|
+
return action$1.type === "custom";
|
|
5659
|
+
}
|
|
5660
|
+
function resolveActions$1(targetId, zone, ownerActions) {
|
|
5661
|
+
const baseActions = ownerActions.filter((action$1) => !action$1.hidden).map((action$1) => withoutRegistryMeta(action$1));
|
|
5662
|
+
if (!targetId) return baseActions;
|
|
5663
|
+
const registered = crudActions.get(targetId, zone);
|
|
5664
|
+
if (registered.length === 0) return baseActions;
|
|
5665
|
+
const nextActions = [...baseActions];
|
|
5666
|
+
const startCustomActions = [];
|
|
5667
|
+
const endCustomActions = [];
|
|
5668
|
+
for (const entry of registered) {
|
|
5669
|
+
const action$1 = entry.action;
|
|
5670
|
+
if (action$1.hidden && isCustomAction$1(action$1)) continue;
|
|
5671
|
+
if (isCustomAction$1(action$1)) {
|
|
5672
|
+
const custom = withoutRegistryMeta(action$1);
|
|
5673
|
+
if (custom.position === "start") startCustomActions.push(custom);
|
|
5674
|
+
else endCustomActions.push(custom);
|
|
5675
|
+
continue;
|
|
5676
|
+
}
|
|
5677
|
+
const existingIndex = nextActions.findIndex((item) => item.type === action$1.type);
|
|
5678
|
+
if (action$1.hidden) {
|
|
5679
|
+
if (existingIndex >= 0) nextActions.splice(existingIndex, 1);
|
|
5680
|
+
continue;
|
|
5681
|
+
}
|
|
5682
|
+
const builtin = withoutRegistryMeta(action$1);
|
|
5683
|
+
if (existingIndex >= 0) nextActions[existingIndex] = {
|
|
5684
|
+
...nextActions[existingIndex],
|
|
5685
|
+
...builtin
|
|
5686
|
+
};
|
|
5687
|
+
else nextActions.push(builtin);
|
|
5688
|
+
}
|
|
5689
|
+
return [
|
|
5690
|
+
...startCustomActions,
|
|
5691
|
+
...nextActions,
|
|
5692
|
+
...endCustomActions
|
|
5693
|
+
];
|
|
5694
|
+
}
|
|
5695
|
+
const crudActions = {
|
|
5696
|
+
register(registration) {
|
|
5697
|
+
unregisterOwnerActions(registration.ownerId, {
|
|
5698
|
+
targetId: registration.targetId,
|
|
5699
|
+
zone: registration.zone
|
|
5700
|
+
}, false);
|
|
5701
|
+
registration.actions.forEach((action$1, index$1) => {
|
|
5702
|
+
const key = actionKey(registration, action$1, index$1);
|
|
5703
|
+
const entry = {
|
|
5704
|
+
targetId: registration.targetId,
|
|
5705
|
+
zone: registration.zone,
|
|
5706
|
+
ownerId: registration.ownerId,
|
|
5707
|
+
action: action$1,
|
|
5708
|
+
order: action$1.order ?? 100,
|
|
5709
|
+
seq: seq++
|
|
5710
|
+
};
|
|
5711
|
+
entries.set(key, entry);
|
|
5712
|
+
addToIndex(key, entry);
|
|
5713
|
+
});
|
|
5714
|
+
notify();
|
|
5715
|
+
},
|
|
5716
|
+
unregister: unregisterOwnerActions,
|
|
5717
|
+
clear() {
|
|
5718
|
+
if (entries.size === 0) return;
|
|
5719
|
+
entries.clear();
|
|
5720
|
+
index.clear();
|
|
5721
|
+
notify();
|
|
5722
|
+
},
|
|
5723
|
+
get(targetId, zone) {
|
|
5724
|
+
return Array.from(index.get(indexKey(targetId, zone)) ?? []).flatMap((key) => {
|
|
5725
|
+
const entry = entries.get(key);
|
|
5726
|
+
return entry ? [entry] : [];
|
|
5727
|
+
}).sort(sortEntries);
|
|
5728
|
+
},
|
|
5729
|
+
resolve: resolveActions$1,
|
|
5730
|
+
subscribe(listener) {
|
|
5731
|
+
listeners.add(listener);
|
|
5732
|
+
return () => listeners.delete(listener);
|
|
5733
|
+
},
|
|
5734
|
+
getSnapshot() {
|
|
5735
|
+
return version;
|
|
5736
|
+
}
|
|
5737
|
+
};
|
|
5738
|
+
|
|
6244
5739
|
//#endregion
|
|
6245
5740
|
//#region src/components/auto-crud/auto-crud-table.tsx
|
|
6246
5741
|
let autoCrudToolbarResolver = null;
|
|
@@ -6254,28 +5749,67 @@ function getDefaultToolbarActions() {
|
|
|
6254
5749
|
{ type: "create" }
|
|
6255
5750
|
];
|
|
6256
5751
|
}
|
|
5752
|
+
function getDefaultRowActions() {
|
|
5753
|
+
return [
|
|
5754
|
+
{ type: "view" },
|
|
5755
|
+
{ type: "edit" },
|
|
5756
|
+
{ type: "copy" },
|
|
5757
|
+
{ type: "delete" }
|
|
5758
|
+
];
|
|
5759
|
+
}
|
|
5760
|
+
function getDefaultBatchActions() {
|
|
5761
|
+
return [{ type: "batchUpdate" }, { type: "delete" }];
|
|
5762
|
+
}
|
|
5763
|
+
function isCustomAction(action$1) {
|
|
5764
|
+
return action$1.type === "custom";
|
|
5765
|
+
}
|
|
5766
|
+
function resolveOwnerActions(config, defaults) {
|
|
5767
|
+
const resolved = typeof config === "function" ? config([...defaults]) : config;
|
|
5768
|
+
const items = resolved !== void 0 && resolved.length > 0 ? [...resolved] : [];
|
|
5769
|
+
if (items.length === 0) return [...defaults];
|
|
5770
|
+
if (items.some((item) => !isCustomAction(item))) return items;
|
|
5771
|
+
const startItems = items.filter((item) => isCustomAction(item) && item.position === "start");
|
|
5772
|
+
const endItems = items.filter((item) => isCustomAction(item) && item.position !== "start");
|
|
5773
|
+
return [
|
|
5774
|
+
...startItems,
|
|
5775
|
+
...defaults,
|
|
5776
|
+
...endItems
|
|
5777
|
+
];
|
|
5778
|
+
}
|
|
6257
5779
|
function resolveOwnerToolbarActions(toolbar) {
|
|
6258
|
-
|
|
6259
|
-
|
|
5780
|
+
return resolveOwnerActions(toolbar, getDefaultToolbarActions());
|
|
5781
|
+
}
|
|
5782
|
+
function resolveOwnerRowActions(actions) {
|
|
5783
|
+
return resolveOwnerActions(actions, getDefaultRowActions());
|
|
5784
|
+
}
|
|
5785
|
+
function resolveOwnerBatchActions(actions) {
|
|
5786
|
+
return resolveOwnerActions(actions, getDefaultBatchActions());
|
|
5787
|
+
}
|
|
5788
|
+
function isUnifiedActionsConfig(actions) {
|
|
5789
|
+
return typeof actions === "object" && actions !== null && !Array.isArray(actions);
|
|
5790
|
+
}
|
|
5791
|
+
function readRowId(row, idKey) {
|
|
5792
|
+
const value = row[idKey];
|
|
5793
|
+
return value === null || value === void 0 ? void 0 : String(value);
|
|
6260
5794
|
}
|
|
6261
5795
|
function readRowIds(rows, idKey) {
|
|
6262
5796
|
return rows.flatMap((row) => {
|
|
6263
|
-
const value = row
|
|
6264
|
-
return value ===
|
|
5797
|
+
const value = readRowId(row, idKey);
|
|
5798
|
+
return value === void 0 ? [] : [value];
|
|
6265
5799
|
});
|
|
6266
5800
|
}
|
|
6267
5801
|
function areStringArraysEqual(left, right) {
|
|
6268
|
-
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
5802
|
+
return left.length === right.length && left.every((value, index$1) => value === right[index$1]);
|
|
6269
5803
|
}
|
|
6270
5804
|
function haveSameRowSelection(left, right, idKey) {
|
|
6271
5805
|
if (left.length !== right.length) return false;
|
|
6272
5806
|
const leftIds = readRowIds(left, idKey);
|
|
6273
5807
|
const rightIds = readRowIds(right, idKey);
|
|
6274
5808
|
if (leftIds.length === left.length && rightIds.length === right.length) return areStringArraysEqual(leftIds, rightIds);
|
|
6275
|
-
return left.every((row, index) => row === right[index]);
|
|
5809
|
+
return left.every((row, index$1) => row === right[index$1]);
|
|
6276
5810
|
}
|
|
6277
5811
|
function readCreateAction(actions) {
|
|
6278
|
-
return actions.find((action) => action.type === "create");
|
|
5812
|
+
return actions.find((action$1) => action$1.type === "create");
|
|
6279
5813
|
}
|
|
6280
5814
|
function resolveToolbarActionsWithResolver(targetId, ownerActions, context) {
|
|
6281
5815
|
const resolver = autoCrudToolbarResolver;
|
|
@@ -6327,6 +5861,15 @@ function toBatchOptions(options) {
|
|
|
6327
5861
|
value
|
|
6328
5862
|
}));
|
|
6329
5863
|
}
|
|
5864
|
+
const POPUP_SCROLL_LOAD_THRESHOLD = 24;
|
|
5865
|
+
function isNearPopupScrollBottom(target) {
|
|
5866
|
+
return target.scrollHeight - target.scrollTop - target.clientHeight <= POPUP_SCROLL_LOAD_THRESHOLD;
|
|
5867
|
+
}
|
|
5868
|
+
function mergeFilterOptions(current, incoming) {
|
|
5869
|
+
const optionsByValue = new Map((current ?? []).map((option) => [option.value, option]));
|
|
5870
|
+
for (const option of incoming) optionsByValue.set(option.value, option);
|
|
5871
|
+
return Array.from(optionsByValue.values());
|
|
5872
|
+
}
|
|
6330
5873
|
function getFilterDataSource(config) {
|
|
6331
5874
|
if (config.filter && typeof config.filter === "object") return config.filter.dataSource ?? config.dataSource;
|
|
6332
5875
|
return config.dataSource;
|
|
@@ -6341,13 +5884,29 @@ function shouldLoadDynamicFilterOptions(config) {
|
|
|
6341
5884
|
return normalizeDataSourceConfig(getFilterDataSource(config)) !== void 0;
|
|
6342
5885
|
}
|
|
6343
5886
|
function useRegistryVersion(subscribe) {
|
|
6344
|
-
const [version, setVersion] = React.useState(0);
|
|
5887
|
+
const [version$1, setVersion] = React.useState(0);
|
|
6345
5888
|
React.useEffect(() => subscribe(() => setVersion((current) => current + 1)), [subscribe]);
|
|
6346
|
-
return version;
|
|
5889
|
+
return version$1;
|
|
5890
|
+
}
|
|
5891
|
+
function useCrudActionsVersion() {
|
|
5892
|
+
return React.useSyncExternalStore(crudActions.subscribe, crudActions.getSnapshot, crudActions.getSnapshot);
|
|
6347
5893
|
}
|
|
6348
5894
|
function useDynamicFilterOptions(fields) {
|
|
6349
5895
|
const registryVersion = useRegistryVersion(dataSources.subscribe);
|
|
5896
|
+
const [searchValues, setSearchValues] = React.useState({});
|
|
5897
|
+
const [hasMoreByField, setHasMoreByField] = React.useState({});
|
|
5898
|
+
const [labelOptionsByField, setLabelOptionsByField] = React.useState({});
|
|
5899
|
+
const [loadingByField, setLoadingByField] = React.useState({});
|
|
6350
5900
|
const [optionsByField, setOptionsByField] = React.useState({});
|
|
5901
|
+
const controllersRef = React.useRef({});
|
|
5902
|
+
const hasMoreByFieldRef = React.useRef({});
|
|
5903
|
+
const loadingByFieldRef = React.useRef({});
|
|
5904
|
+
const optionsByFieldRef = React.useRef({});
|
|
5905
|
+
const pageByFieldRef = React.useRef({});
|
|
5906
|
+
const requestVersionsRef = React.useRef({});
|
|
5907
|
+
const searchValuesRef = React.useRef({});
|
|
5908
|
+
const sourceEntriesByFieldRef = React.useRef({});
|
|
5909
|
+
const timersRef = React.useRef({});
|
|
6351
5910
|
const sourceEntries = React.useMemo(() => {
|
|
6352
5911
|
if (!fields) return [];
|
|
6353
5912
|
return Object.entries(fields).flatMap(([field, config]) => {
|
|
@@ -6359,36 +5918,185 @@ function useDynamicFilterOptions(fields) {
|
|
|
6359
5918
|
}] : [];
|
|
6360
5919
|
});
|
|
6361
5920
|
}, [fields]);
|
|
5921
|
+
const sourceEntriesByField = React.useMemo(() => Object.fromEntries(sourceEntries.map(({ field, source }) => [field, source])), [sourceEntries]);
|
|
6362
5922
|
React.useEffect(() => {
|
|
6363
|
-
|
|
6364
|
-
|
|
5923
|
+
searchValuesRef.current = searchValues;
|
|
5924
|
+
}, [searchValues]);
|
|
5925
|
+
React.useEffect(() => {
|
|
5926
|
+
sourceEntriesByFieldRef.current = sourceEntriesByField;
|
|
5927
|
+
}, [sourceEntriesByField]);
|
|
5928
|
+
const registeredByField = React.useMemo(() => Object.fromEntries(sourceEntries.map(({ field, source }) => [field, dataSources.get(source.key) !== void 0])), [sourceEntries, registryVersion]);
|
|
5929
|
+
const searchableByField = React.useMemo(() => Object.fromEntries(sourceEntries.map(({ field, source }) => [field, dataSources.get(source.key)?.search === true])), [sourceEntries, registryVersion]);
|
|
5930
|
+
const setFieldState = React.useCallback((setter, ref, field, value) => {
|
|
5931
|
+
ref.current = {
|
|
5932
|
+
...ref.current,
|
|
5933
|
+
[field]: value
|
|
5934
|
+
};
|
|
5935
|
+
setter(ref.current);
|
|
5936
|
+
}, []);
|
|
5937
|
+
const removeInactiveFieldState = React.useCallback((setter, ref, activeFields) => {
|
|
5938
|
+
const nextEntries = Object.entries(ref.current).filter(([field]) => activeFields.has(field));
|
|
5939
|
+
if (nextEntries.length === Object.keys(ref.current).length) return;
|
|
5940
|
+
ref.current = Object.fromEntries(nextEntries);
|
|
5941
|
+
setter(ref.current);
|
|
5942
|
+
}, []);
|
|
5943
|
+
const setFieldOptions = React.useCallback((field, options) => {
|
|
5944
|
+
optionsByFieldRef.current = {
|
|
5945
|
+
...optionsByFieldRef.current,
|
|
5946
|
+
[field]: options
|
|
5947
|
+
};
|
|
5948
|
+
setOptionsByField(optionsByFieldRef.current);
|
|
5949
|
+
}, []);
|
|
5950
|
+
const setFieldLoading = React.useCallback((field, loading) => {
|
|
5951
|
+
setFieldState(setLoadingByField, loadingByFieldRef, field, loading);
|
|
5952
|
+
}, [setFieldState]);
|
|
5953
|
+
const setFieldHasMore = React.useCallback((field, hasMore) => {
|
|
5954
|
+
setFieldState(setHasMoreByField, hasMoreByFieldRef, field, hasMore);
|
|
5955
|
+
}, [setFieldState]);
|
|
5956
|
+
const clearFieldRequest = React.useCallback((field) => {
|
|
5957
|
+
const timer = timersRef.current[field];
|
|
5958
|
+
if (timer) clearTimeout(timer);
|
|
5959
|
+
timersRef.current[field] = void 0;
|
|
5960
|
+
controllersRef.current[field]?.abort();
|
|
5961
|
+
controllersRef.current[field] = void 0;
|
|
5962
|
+
}, []);
|
|
5963
|
+
const resetField = React.useCallback((field, { clearLabels = false } = {}) => {
|
|
5964
|
+
clearFieldRequest(field);
|
|
5965
|
+
setFieldOptions(field, []);
|
|
5966
|
+
setFieldLoading(field, false);
|
|
5967
|
+
setFieldHasMore(field, false);
|
|
5968
|
+
pageByFieldRef.current = {
|
|
5969
|
+
...pageByFieldRef.current,
|
|
5970
|
+
[field]: 0
|
|
5971
|
+
};
|
|
5972
|
+
if (clearLabels) setLabelOptionsByField((current) => {
|
|
5973
|
+
const { [field]: _,...rest } = current;
|
|
5974
|
+
return rest;
|
|
5975
|
+
});
|
|
5976
|
+
}, [
|
|
5977
|
+
clearFieldRequest,
|
|
5978
|
+
setFieldHasMore,
|
|
5979
|
+
setFieldLoading,
|
|
5980
|
+
setFieldOptions
|
|
5981
|
+
]);
|
|
5982
|
+
const loadFieldOptions = React.useCallback((field, append = false) => {
|
|
5983
|
+
if (append) {
|
|
5984
|
+
if (loadingByFieldRef.current[field] || !hasMoreByFieldRef.current[field]) return;
|
|
5985
|
+
}
|
|
5986
|
+
const source = sourceEntriesByFieldRef.current[field];
|
|
5987
|
+
const entry = source ? dataSources.get(source.key) : void 0;
|
|
5988
|
+
if (!source || !entry) {
|
|
5989
|
+
resetField(field);
|
|
6365
5990
|
return;
|
|
6366
5991
|
}
|
|
6367
|
-
|
|
5992
|
+
const requestVersion = (requestVersionsRef.current[field] ?? 0) + 1;
|
|
5993
|
+
requestVersionsRef.current = {
|
|
5994
|
+
...requestVersionsRef.current,
|
|
5995
|
+
[field]: requestVersion
|
|
5996
|
+
};
|
|
5997
|
+
clearFieldRequest(field);
|
|
6368
5998
|
const controller = typeof AbortController === "undefined" ? void 0 : new AbortController();
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
5999
|
+
controllersRef.current[field] = controller;
|
|
6000
|
+
const search = entry.search ? searchValuesRef.current[field] ?? "" : void 0;
|
|
6001
|
+
const page = entry.loadMore ? append ? (pageByFieldRef.current[field] ?? 0) + 1 : 1 : void 0;
|
|
6002
|
+
setFieldLoading(field, true);
|
|
6003
|
+
if (!append) setFieldHasMore(field, false);
|
|
6004
|
+
const timer = setTimeout(() => {
|
|
6005
|
+
Promise.resolve(entry.load({
|
|
6006
|
+
field,
|
|
6007
|
+
page,
|
|
6008
|
+
pageSize: entry.loadMore ? entry.pageSize : void 0,
|
|
6009
|
+
search,
|
|
6010
|
+
values: {},
|
|
6011
|
+
signal: controller?.signal
|
|
6012
|
+
})).then((result) => {
|
|
6013
|
+
if (requestVersionsRef.current[field] !== requestVersion || controller?.signal.aborted) return;
|
|
6014
|
+
const options = normalizeOptions(result);
|
|
6015
|
+
const nextOptions = append ? mergeFilterOptions(optionsByFieldRef.current[field], options) : options;
|
|
6016
|
+
const hasMore = normalizeHasMore(result);
|
|
6017
|
+
setFieldOptions(field, nextOptions);
|
|
6018
|
+
setLabelOptionsByField((current) => ({
|
|
6019
|
+
...current,
|
|
6020
|
+
[field]: mergeFilterOptions(current[field], nextOptions)
|
|
6021
|
+
}));
|
|
6022
|
+
pageByFieldRef.current = {
|
|
6023
|
+
...pageByFieldRef.current,
|
|
6024
|
+
[field]: page ?? 0
|
|
6025
|
+
};
|
|
6026
|
+
setFieldHasMore(field, hasMore);
|
|
6027
|
+
setFieldLoading(field, false);
|
|
6028
|
+
}, (error) => {
|
|
6029
|
+
if (controller?.signal.aborted) return;
|
|
6030
|
+
console.warn(`[AutoCrud] Failed to load filter data source "${source.key}".`, error);
|
|
6031
|
+
if (!append) {
|
|
6032
|
+
setFieldOptions(field, []);
|
|
6033
|
+
setFieldHasMore(field, false);
|
|
6034
|
+
pageByFieldRef.current = {
|
|
6035
|
+
...pageByFieldRef.current,
|
|
6036
|
+
[field]: 0
|
|
6037
|
+
};
|
|
6038
|
+
}
|
|
6039
|
+
setFieldLoading(field, false);
|
|
6040
|
+
});
|
|
6041
|
+
}, !append && entry.search ? entry.debounceMs : 0);
|
|
6042
|
+
timersRef.current[field] = timer;
|
|
6043
|
+
}, [
|
|
6044
|
+
clearFieldRequest,
|
|
6045
|
+
resetField,
|
|
6046
|
+
setFieldHasMore,
|
|
6047
|
+
setFieldLoading,
|
|
6048
|
+
setFieldOptions
|
|
6049
|
+
]);
|
|
6050
|
+
const setSearchValue = React.useCallback((field, value) => {
|
|
6051
|
+
searchValuesRef.current = {
|
|
6052
|
+
...searchValuesRef.current,
|
|
6053
|
+
[field]: value
|
|
6389
6054
|
};
|
|
6390
|
-
|
|
6391
|
-
|
|
6055
|
+
setSearchValues((current) => current[field] === value ? current : {
|
|
6056
|
+
...current,
|
|
6057
|
+
[field]: value
|
|
6058
|
+
});
|
|
6059
|
+
}, []);
|
|
6060
|
+
React.useEffect(() => {
|
|
6061
|
+
if (sourceEntries.length === 0) {
|
|
6062
|
+
hasMoreByFieldRef.current = {};
|
|
6063
|
+
loadingByFieldRef.current = {};
|
|
6064
|
+
optionsByFieldRef.current = {};
|
|
6065
|
+
pageByFieldRef.current = {};
|
|
6066
|
+
Object.keys(controllersRef.current).forEach(clearFieldRequest);
|
|
6067
|
+
setHasMoreByField({});
|
|
6068
|
+
setLoadingByField({});
|
|
6069
|
+
setOptionsByField({});
|
|
6070
|
+
return;
|
|
6071
|
+
}
|
|
6072
|
+
const activeFields = new Set(sourceEntries.map(({ field }) => field));
|
|
6073
|
+
removeInactiveFieldState(setHasMoreByField, hasMoreByFieldRef, activeFields);
|
|
6074
|
+
removeInactiveFieldState(setLoadingByField, loadingByFieldRef, activeFields);
|
|
6075
|
+
removeInactiveFieldState(setOptionsByField, optionsByFieldRef, activeFields);
|
|
6076
|
+
for (const field of Object.keys(controllersRef.current)) if (!activeFields.has(field)) clearFieldRequest(field);
|
|
6077
|
+
for (const { field } of sourceEntries) loadFieldOptions(field);
|
|
6078
|
+
}, [
|
|
6079
|
+
clearFieldRequest,
|
|
6080
|
+
loadFieldOptions,
|
|
6081
|
+
removeInactiveFieldState,
|
|
6082
|
+
sourceEntries,
|
|
6083
|
+
registryVersion,
|
|
6084
|
+
searchValues
|
|
6085
|
+
]);
|
|
6086
|
+
React.useEffect(() => () => {
|
|
6087
|
+
for (const field of Object.keys(controllersRef.current)) clearFieldRequest(field);
|
|
6088
|
+
}, [clearFieldRequest]);
|
|
6089
|
+
return {
|
|
6090
|
+
hasMoreByField,
|
|
6091
|
+
labelOptionsByField,
|
|
6092
|
+
loadingByField,
|
|
6093
|
+
optionsByField,
|
|
6094
|
+
registeredByField,
|
|
6095
|
+
searchValues,
|
|
6096
|
+
searchableByField,
|
|
6097
|
+
loadMore: (field) => loadFieldOptions(field, true),
|
|
6098
|
+
setSearchValue
|
|
6099
|
+
};
|
|
6392
6100
|
}
|
|
6393
6101
|
function getOptionLabel(value, options) {
|
|
6394
6102
|
const stringValue = String(value);
|
|
@@ -6398,12 +6106,15 @@ function getOptionLabel(value, options) {
|
|
|
6398
6106
|
* 从统一配置生成表格 overrides
|
|
6399
6107
|
* 当 filter 配置存在时,合并到 meta 中(不影响列隐藏)
|
|
6400
6108
|
*/
|
|
6401
|
-
function buildTableOverrides(fields, legacyOverrides,
|
|
6109
|
+
function buildTableOverrides(fields, legacyOverrides, dynamicFilterState) {
|
|
6402
6110
|
const result = { ...legacyOverrides };
|
|
6403
6111
|
if (fields) for (const [key, config] of Object.entries(fields)) {
|
|
6404
6112
|
const fieldOptions = normalizeFieldOptions(config.enum);
|
|
6405
6113
|
const tableOptions = toTableOptions(fieldOptions);
|
|
6406
|
-
const
|
|
6114
|
+
const filterDataSourceRegistered = dynamicFilterState?.registeredByField[key];
|
|
6115
|
+
const filterDataSourceSearchable = dynamicFilterState?.searchableByField[key];
|
|
6116
|
+
const currentDynamicOptions = !fieldOptions && filterDataSourceRegistered ? toTableOptions(dynamicFilterState?.optionsByField[key] ?? []) : void 0;
|
|
6117
|
+
const dynamicOptions = !fieldOptions && filterDataSourceRegistered ? toTableOptions(dynamicFilterState?.labelOptionsByField[key] ?? dynamicFilterState?.optionsByField[key] ?? []) : void 0;
|
|
6407
6118
|
const tableMeta = config.table !== false && typeof config.table === "object" ? config.table.meta : void 0;
|
|
6408
6119
|
const fieldEnumMeta = tableOptions ? {
|
|
6409
6120
|
options: tableOptions,
|
|
@@ -6411,7 +6122,19 @@ function buildTableOverrides(fields, legacyOverrides, dynamicFilterOptions) {
|
|
|
6411
6122
|
} : void 0;
|
|
6412
6123
|
const fieldDataSourceMeta = dynamicOptions ? {
|
|
6413
6124
|
options: dynamicOptions,
|
|
6414
|
-
|
|
6125
|
+
autoCrudFilterOptions: currentDynamicOptions ?? dynamicOptions,
|
|
6126
|
+
autoCrudFilterHasMore: dynamicFilterState?.hasMoreByField[key] ?? false,
|
|
6127
|
+
autoCrudFilterLoading: dynamicFilterState?.loadingByField[key] ?? false,
|
|
6128
|
+
autoCrudFilterOnPopupScroll: (event) => {
|
|
6129
|
+
if (!isNearPopupScrollBottom(event.currentTarget)) return;
|
|
6130
|
+
dynamicFilterState?.loadMore(key);
|
|
6131
|
+
},
|
|
6132
|
+
variant: "multiSelect",
|
|
6133
|
+
...filterDataSourceSearchable ? {
|
|
6134
|
+
autoCrudFilterSearchValue: dynamicFilterState?.searchValues[key] ?? "",
|
|
6135
|
+
autoCrudFilterOnSearch: (value) => dynamicFilterState?.setSearchValue(key, value),
|
|
6136
|
+
autoCrudFilterShouldFilter: false
|
|
6137
|
+
} : void 0
|
|
6415
6138
|
} : void 0;
|
|
6416
6139
|
let filterMeta;
|
|
6417
6140
|
if (config.filter && typeof config.filter === "object") {
|
|
@@ -6463,46 +6186,6 @@ function buildTableOverrides(fields, legacyOverrides, dynamicFilterOptions) {
|
|
|
6463
6186
|
return result;
|
|
6464
6187
|
}
|
|
6465
6188
|
/**
|
|
6466
|
-
* 从统一配置生成表单 overrides
|
|
6467
|
-
* Critical #2: 支持 denyFields 隐藏敏感字段
|
|
6468
|
-
*/
|
|
6469
|
-
function buildFormOverrides(fields, legacyOverrides, denyFields) {
|
|
6470
|
-
const result = { ...legacyOverrides };
|
|
6471
|
-
if (denyFields) for (const field of denyFields) result[field] = {
|
|
6472
|
-
...result[field],
|
|
6473
|
-
"x-hidden": true
|
|
6474
|
-
};
|
|
6475
|
-
if (fields) for (const [key, config] of Object.entries(fields)) {
|
|
6476
|
-
const fieldOptions = normalizeFieldOptions((config.form && typeof config.form === "object" ? config.form : void 0)?.enum) ?? normalizeFieldOptions(config.enum);
|
|
6477
|
-
const fieldDataSource = fieldOptions || !config.dataSource ? void 0 : normalizeDataSourceConfig(config.dataSource);
|
|
6478
|
-
if (config.label) result[key] = {
|
|
6479
|
-
...result[key],
|
|
6480
|
-
title: config.label
|
|
6481
|
-
};
|
|
6482
|
-
if (config.hidden) result[key] = {
|
|
6483
|
-
...result[key],
|
|
6484
|
-
"x-hidden": true
|
|
6485
|
-
};
|
|
6486
|
-
if (fieldOptions) result[key] = {
|
|
6487
|
-
...result[key],
|
|
6488
|
-
enum: fieldOptions
|
|
6489
|
-
};
|
|
6490
|
-
if (fieldDataSource) result[key] = {
|
|
6491
|
-
...result[key],
|
|
6492
|
-
"x-data-source": config.dataSource
|
|
6493
|
-
};
|
|
6494
|
-
if (config.form === false) result[key] = {
|
|
6495
|
-
...result[key],
|
|
6496
|
-
"x-hidden": true
|
|
6497
|
-
};
|
|
6498
|
-
else if (config.form && typeof config.form === "object") result[key] = {
|
|
6499
|
-
...result[key],
|
|
6500
|
-
...config.form
|
|
6501
|
-
};
|
|
6502
|
-
}
|
|
6503
|
-
return result;
|
|
6504
|
-
}
|
|
6505
|
-
/**
|
|
6506
6189
|
* 从统一配置生成隐藏列列表
|
|
6507
6190
|
*/
|
|
6508
6191
|
function buildHiddenColumns(fields, legacyHidden, denyFields) {
|
|
@@ -6648,13 +6331,23 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
|
|
|
6648
6331
|
/**
|
|
6649
6332
|
* 解析列表行操作
|
|
6650
6333
|
*/
|
|
6651
|
-
function resolveActions(actionsOrFn, defaults, rowActionsLocale) {
|
|
6652
|
-
const
|
|
6334
|
+
function resolveActions(actionsOrFn, defaults, rowActionsLocale, context) {
|
|
6335
|
+
const resolvedItems = typeof actionsOrFn === "function" ? actionsOrFn([
|
|
6653
6336
|
{ type: "view" },
|
|
6654
6337
|
{ type: "edit" },
|
|
6655
6338
|
{ type: "copy" },
|
|
6656
6339
|
{ type: "delete" }
|
|
6657
6340
|
]) : actionsOrFn;
|
|
6341
|
+
const getContext = (row) => ({
|
|
6342
|
+
crudId: context.crudId,
|
|
6343
|
+
idKey: context.idKey,
|
|
6344
|
+
row,
|
|
6345
|
+
rowId: readRowId(row, context.idKey),
|
|
6346
|
+
openView: defaults.openView,
|
|
6347
|
+
...defaults.openEdit ? { openEdit: defaults.openEdit } : {},
|
|
6348
|
+
...defaults.copyRow ? { copyRow: defaults.copyRow } : {},
|
|
6349
|
+
...defaults.openDelete ? { openDelete: defaults.openDelete } : {}
|
|
6350
|
+
});
|
|
6658
6351
|
const defaultItems = [
|
|
6659
6352
|
{
|
|
6660
6353
|
label: rowActionsLocale.view,
|
|
@@ -6675,17 +6368,23 @@ function resolveActions(actionsOrFn, defaults, rowActionsLocale) {
|
|
|
6675
6368
|
variant: "destructive"
|
|
6676
6369
|
}] : []
|
|
6677
6370
|
];
|
|
6678
|
-
if (
|
|
6371
|
+
if (resolvedItems === void 0) return defaultItems;
|
|
6372
|
+
const items = resolvedItems.filter((item) => !item.hidden);
|
|
6373
|
+
if (items.length === 0) return [];
|
|
6679
6374
|
if (!items.some((i) => i.type !== "custom")) {
|
|
6680
|
-
const startItems = items.filter((i) => i.type === "custom" && i.position === "start").map(({ label, onClick, separator, variant }) => ({
|
|
6375
|
+
const startItems = items.filter((i) => i.type === "custom" && i.position === "start").map(({ label, onClick, component, separator, variant }) => ({
|
|
6681
6376
|
label,
|
|
6682
6377
|
onClick,
|
|
6378
|
+
component,
|
|
6379
|
+
getContext,
|
|
6683
6380
|
separator,
|
|
6684
6381
|
variant
|
|
6685
6382
|
}));
|
|
6686
|
-
const endItems = items.filter((i) => i.type === "custom" && i.position !== "start").map(({ label, onClick, separator, variant }) => ({
|
|
6383
|
+
const endItems = items.filter((i) => i.type === "custom" && i.position !== "start").map(({ label, onClick, component, separator, variant }) => ({
|
|
6687
6384
|
label,
|
|
6688
6385
|
onClick,
|
|
6386
|
+
component,
|
|
6387
|
+
getContext,
|
|
6689
6388
|
separator,
|
|
6690
6389
|
variant
|
|
6691
6390
|
}));
|
|
@@ -6712,6 +6411,8 @@ function resolveActions(actionsOrFn, defaults, rowActionsLocale) {
|
|
|
6712
6411
|
if (item.type === "custom") return [{
|
|
6713
6412
|
label: item.label,
|
|
6714
6413
|
onClick: item.onClick,
|
|
6414
|
+
component: item.component,
|
|
6415
|
+
getContext,
|
|
6715
6416
|
separator: item.separator,
|
|
6716
6417
|
variant: item.variant
|
|
6717
6418
|
}];
|
|
@@ -6751,7 +6452,32 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6751
6452
|
const getSelectedRowsRef = React.useRef(null);
|
|
6752
6453
|
const dynamicFilterOptions = useDynamicFilterOptions(resolvedFields);
|
|
6753
6454
|
const resourceIdKey = resource.idKey ?? "id";
|
|
6754
|
-
const
|
|
6455
|
+
const actionRegistryVersion = useCrudActionsVersion();
|
|
6456
|
+
const unifiedActions = React.useMemo(() => {
|
|
6457
|
+
if (isUnifiedActionsConfig(actionItems)) return actionItems;
|
|
6458
|
+
return { row: actionItems };
|
|
6459
|
+
}, [actionItems]);
|
|
6460
|
+
const ownerToolbarConfig = unifiedActions.toolbar ?? toolbar ?? toolbarActions;
|
|
6461
|
+
const ownerRowConfig = unifiedActions.row;
|
|
6462
|
+
const ownerBatchConfig = unifiedActions.batch ?? tableConfig?.batchActions;
|
|
6463
|
+
const ownerToolbarActions = React.useMemo(() => resolveOwnerToolbarActions(ownerToolbarConfig), [ownerToolbarConfig]);
|
|
6464
|
+
const ownerRowActions = React.useMemo(() => resolveOwnerRowActions(ownerRowConfig), [ownerRowConfig]);
|
|
6465
|
+
const ownerBatchActions = React.useMemo(() => resolveOwnerBatchActions(ownerBatchConfig), [ownerBatchConfig]);
|
|
6466
|
+
const registryToolbarActions = React.useMemo(() => crudActions.resolve(id, "toolbar", ownerToolbarActions), [
|
|
6467
|
+
id,
|
|
6468
|
+
ownerToolbarActions,
|
|
6469
|
+
actionRegistryVersion
|
|
6470
|
+
]);
|
|
6471
|
+
const registryRowActions = React.useMemo(() => crudActions.resolve(id, "row", ownerRowActions), [
|
|
6472
|
+
id,
|
|
6473
|
+
ownerRowActions,
|
|
6474
|
+
actionRegistryVersion
|
|
6475
|
+
]);
|
|
6476
|
+
const registryBatchActions = React.useMemo(() => crudActions.resolve(id, "batch", ownerBatchActions), [
|
|
6477
|
+
id,
|
|
6478
|
+
ownerBatchActions,
|
|
6479
|
+
actionRegistryVersion
|
|
6480
|
+
]);
|
|
6755
6481
|
const selectedCount = selectedRows.length;
|
|
6756
6482
|
const selectedRowIds = React.useMemo(() => readRowIds(selectedRows, resourceIdKey), [selectedRows, resourceIdKey]);
|
|
6757
6483
|
const rowIds = React.useMemo(() => readRowIds(resource.tableData.data, resourceIdKey), [resource.tableData.data, resourceIdKey]);
|
|
@@ -6760,7 +6486,7 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6760
6486
|
}, [resourceIdKey]);
|
|
6761
6487
|
const ownerCreateAction = React.useMemo(() => readCreateAction(ownerToolbarActions), [ownerToolbarActions]);
|
|
6762
6488
|
const toolbarOpenCreate = can.create ? ownerCreateAction?.onClick ?? onCreate ?? resource.handlers.openCreate : void 0;
|
|
6763
|
-
const
|
|
6489
|
+
const toolbarContext = React.useMemo(() => ({
|
|
6764
6490
|
crudId: id ?? "",
|
|
6765
6491
|
idKey: resourceIdKey,
|
|
6766
6492
|
rowIds,
|
|
@@ -6774,7 +6500,8 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6774
6500
|
selectedRowIds,
|
|
6775
6501
|
selectedCount,
|
|
6776
6502
|
toolbarOpenCreate
|
|
6777
|
-
])
|
|
6503
|
+
]);
|
|
6504
|
+
const resolvedToolbarActions = resolveToolbarActionsWithResolver(id, registryToolbarActions, toolbarContext);
|
|
6778
6505
|
const importColumns = React.useMemo(() => {
|
|
6779
6506
|
const shape = resolvedSchema.shape;
|
|
6780
6507
|
const denySet = new Set(denyFields ?? []);
|
|
@@ -6844,6 +6571,30 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6844
6571
|
resource.modal.copySource,
|
|
6845
6572
|
resource.modal.selected
|
|
6846
6573
|
]);
|
|
6574
|
+
const rowActionDefaults = React.useMemo(() => ({
|
|
6575
|
+
openView: resource.handlers.openView,
|
|
6576
|
+
openEdit: can.update ? resource.handlers.openEdit : void 0,
|
|
6577
|
+
copyRow: can.create ? resource.handlers.copyRow : void 0,
|
|
6578
|
+
openDelete: can.delete ? resource.handlers.openDelete : void 0
|
|
6579
|
+
}), [
|
|
6580
|
+
can.create,
|
|
6581
|
+
can.delete,
|
|
6582
|
+
can.update,
|
|
6583
|
+
resource.handlers.copyRow,
|
|
6584
|
+
resource.handlers.openDelete,
|
|
6585
|
+
resource.handlers.openEdit,
|
|
6586
|
+
resource.handlers.openView
|
|
6587
|
+
]);
|
|
6588
|
+
const tableRowActions = React.useMemo(() => resolveActions(registryRowActions, rowActionDefaults, locale.rowActions, {
|
|
6589
|
+
crudId: id ?? "",
|
|
6590
|
+
idKey: resourceIdKey
|
|
6591
|
+
}), [
|
|
6592
|
+
id,
|
|
6593
|
+
locale.rowActions,
|
|
6594
|
+
registryRowActions,
|
|
6595
|
+
resourceIdKey,
|
|
6596
|
+
rowActionDefaults
|
|
6597
|
+
]);
|
|
6847
6598
|
return /* @__PURE__ */ jsxs("div", {
|
|
6848
6599
|
className: "space-y-4",
|
|
6849
6600
|
children: [
|
|
@@ -6880,17 +6631,11 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6880
6631
|
}, "create");
|
|
6881
6632
|
return null;
|
|
6882
6633
|
};
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
children: [
|
|
6886
|
-
renderBuiltinButton("import"),
|
|
6887
|
-
renderBuiltinButton("export"),
|
|
6888
|
-
renderBuiltinButton("create")
|
|
6889
|
-
]
|
|
6890
|
-
});
|
|
6634
|
+
const renderToolbarComponent = (component) => typeof component === "function" ? component(toolbarContext) : component;
|
|
6635
|
+
if (!resolvedToolbarActions || resolvedToolbarActions.length === 0) return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2" });
|
|
6891
6636
|
if (!resolvedToolbarActions.some((i) => i.type !== "custom")) {
|
|
6892
|
-
const startNodes = resolvedToolbarActions.filter((i) => i.type === "custom" && i.position === "start").map((a, i) => /* @__PURE__ */ jsx(React.Fragment, { children: a.component }, `start-${i}`));
|
|
6893
|
-
const endNodes = resolvedToolbarActions.filter((i) => i.type === "custom" && i.position !== "start").map((a, i) => /* @__PURE__ */ jsx(React.Fragment, { children: a.component }, `end-${i}`));
|
|
6637
|
+
const startNodes = resolvedToolbarActions.filter((i) => i.type === "custom" && i.position === "start").map((a, i) => /* @__PURE__ */ jsx(React.Fragment, { children: renderToolbarComponent(a.component) }, `start-${i}`));
|
|
6638
|
+
const endNodes = resolvedToolbarActions.filter((i) => i.type === "custom" && i.position !== "start").map((a, i) => /* @__PURE__ */ jsx(React.Fragment, { children: renderToolbarComponent(a.component) }, `end-${i}`));
|
|
6894
6639
|
return /* @__PURE__ */ jsxs("div", {
|
|
6895
6640
|
className: "flex items-center gap-2",
|
|
6896
6641
|
children: [
|
|
@@ -6904,13 +6649,13 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6904
6649
|
}
|
|
6905
6650
|
return /* @__PURE__ */ jsx("div", {
|
|
6906
6651
|
className: "flex items-center gap-2",
|
|
6907
|
-
children: resolvedToolbarActions.map((action, index) => {
|
|
6908
|
-
if (action.type === "custom") return /* @__PURE__ */ jsx(React.Fragment, { children: action.component }, `custom-${index}`);
|
|
6909
|
-
if (!(action.type === "import" ? canImport : action.type === "export" ? canExport : can.create)) return null;
|
|
6910
|
-
if (action.component) return /* @__PURE__ */ jsx(React.Fragment, { children: action.component }, action.type);
|
|
6911
|
-
return renderBuiltinButton(action.type, {
|
|
6912
|
-
onClick: action.onClick,
|
|
6913
|
-
label: action.label
|
|
6652
|
+
children: resolvedToolbarActions.map((action$1, index$1) => {
|
|
6653
|
+
if (action$1.type === "custom") return /* @__PURE__ */ jsx(React.Fragment, { children: renderToolbarComponent(action$1.component) }, `custom-${index$1}`);
|
|
6654
|
+
if (!(action$1.type === "import" ? canImport : action$1.type === "export" ? canExport : can.create)) return null;
|
|
6655
|
+
if (action$1.component) return /* @__PURE__ */ jsx(React.Fragment, { children: renderToolbarComponent(action$1.component) }, action$1.type);
|
|
6656
|
+
return renderBuiltinButton(action$1.type, {
|
|
6657
|
+
onClick: action$1.onClick,
|
|
6658
|
+
label: action$1.label
|
|
6914
6659
|
});
|
|
6915
6660
|
})
|
|
6916
6661
|
});
|
|
@@ -6924,16 +6669,12 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6924
6669
|
exclude: hiddenColumns,
|
|
6925
6670
|
filterMode: tableConfig?.filterModes,
|
|
6926
6671
|
search: searchConfig,
|
|
6927
|
-
actions:
|
|
6928
|
-
openView: resource.handlers.openView,
|
|
6929
|
-
openEdit: can.update ? resource.handlers.openEdit : void 0,
|
|
6930
|
-
copyRow: can.create ? resource.handlers.copyRow : void 0,
|
|
6931
|
-
openDelete: can.delete ? resource.handlers.openDelete : void 0
|
|
6932
|
-
}, locale.rowActions),
|
|
6672
|
+
actions: tableRowActions,
|
|
6933
6673
|
onDeleteSelected: can.delete ? resource.handlers.deleteMany : void 0,
|
|
6934
6674
|
onUpdateSelected: can.update ? resource.handlers.updateMany : void 0,
|
|
6935
6675
|
batchUpdateFields: can.update ? batchFields : void 0,
|
|
6936
|
-
actionBarActions:
|
|
6676
|
+
actionBarActions: registryBatchActions,
|
|
6677
|
+
deleteConfirmation: locale.bulkDeleteModal,
|
|
6937
6678
|
enableExport: canExport,
|
|
6938
6679
|
showDefaultExport: false,
|
|
6939
6680
|
initialSorting: tableConfig?.defaultSort,
|
|
@@ -6953,6 +6694,7 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6953
6694
|
loading: resource.mutations.isCreating || resource.mutations.isUpdating,
|
|
6954
6695
|
variant: resource.modal.variant,
|
|
6955
6696
|
overrides: formOverrides,
|
|
6697
|
+
scope: formConfig?.scope,
|
|
6956
6698
|
locale: locale.formModal,
|
|
6957
6699
|
gridColumns: formConfig?.columns,
|
|
6958
6700
|
className: formConfig?.className
|
|
@@ -6964,7 +6706,7 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
6964
6706
|
data: resource.modal.selected,
|
|
6965
6707
|
schema: resolvedSchema,
|
|
6966
6708
|
fields: resolvedFields,
|
|
6967
|
-
dynamicOptions: dynamicFilterOptions,
|
|
6709
|
+
dynamicOptions: dynamicFilterOptions.optionsByField,
|
|
6968
6710
|
denyFields,
|
|
6969
6711
|
locale
|
|
6970
6712
|
}),
|
|
@@ -7087,15 +6829,25 @@ function DataTableAdvancedToolbar({ table, children, className,...props }) {
|
|
|
7087
6829
|
function DataTableFacetedFilter({ column, title, options, multiple }) {
|
|
7088
6830
|
const columnFilterValue = column?.getFilterValue();
|
|
7089
6831
|
const selectedValues = Array.isArray(columnFilterValue) ? columnFilterValue.filter((value) => typeof value === "string") : [];
|
|
6832
|
+
const currentOptions = column?.columnDef.meta?.autoCrudFilterOptions ?? options;
|
|
6833
|
+
const currentOptionValues = new Set(currentOptions.map((option) => option.value));
|
|
6834
|
+
const selectedOptionValues = new Set(selectedValues);
|
|
6835
|
+
const comboboxOptions = currentOptions === options ? currentOptions : [...options.filter((option) => selectedOptionValues.has(option.value) && !currentOptionValues.has(option.value)), ...currentOptions];
|
|
7090
6836
|
const onChange = (nextValues) => {
|
|
7091
6837
|
column?.setFilterValue(nextValues.length ? nextValues : void 0);
|
|
7092
6838
|
};
|
|
7093
|
-
return /* @__PURE__ */ jsx(MultiCombobox, {
|
|
6839
|
+
return /* @__PURE__ */ jsx(MultiCombobox$1, {
|
|
7094
6840
|
value: selectedValues,
|
|
7095
6841
|
onChange,
|
|
7096
|
-
options,
|
|
6842
|
+
options: comboboxOptions,
|
|
7097
6843
|
selectionMode: multiple ? "multiple" : "single",
|
|
7098
6844
|
searchPlaceholder: title,
|
|
6845
|
+
hasMore: column?.columnDef.meta?.autoCrudFilterHasMore,
|
|
6846
|
+
loading: column?.columnDef.meta?.autoCrudFilterLoading,
|
|
6847
|
+
searchValue: column?.columnDef.meta?.autoCrudFilterSearchValue,
|
|
6848
|
+
shouldFilter: column?.columnDef.meta?.autoCrudFilterShouldFilter,
|
|
6849
|
+
onPopupScroll: column?.columnDef.meta?.autoCrudFilterOnPopupScroll,
|
|
6850
|
+
onSearch: column?.columnDef.meta?.autoCrudFilterOnSearch,
|
|
7099
6851
|
contentClassName: "w-50",
|
|
7100
6852
|
matchTriggerWidth: false,
|
|
7101
6853
|
clearText: "Clear filters",
|
|
@@ -7674,8 +7426,8 @@ function readMetadataFields(fields) {
|
|
|
7674
7426
|
/**
|
|
7675
7427
|
* Modal 状态 Reducer
|
|
7676
7428
|
*/
|
|
7677
|
-
function modalReducer(state, action) {
|
|
7678
|
-
switch (action.type) {
|
|
7429
|
+
function modalReducer(state, action$1) {
|
|
7430
|
+
switch (action$1.type) {
|
|
7679
7431
|
case "OPEN_CREATE": return {
|
|
7680
7432
|
...state,
|
|
7681
7433
|
createOpen: true,
|
|
@@ -7686,22 +7438,22 @@ function modalReducer(state, action) {
|
|
|
7686
7438
|
...state,
|
|
7687
7439
|
createOpen: true,
|
|
7688
7440
|
selected: null,
|
|
7689
|
-
copySource: action.payload
|
|
7441
|
+
copySource: action$1.payload
|
|
7690
7442
|
};
|
|
7691
7443
|
case "OPEN_EDIT": return {
|
|
7692
7444
|
...state,
|
|
7693
7445
|
editOpen: true,
|
|
7694
|
-
selected: action.payload
|
|
7446
|
+
selected: action$1.payload
|
|
7695
7447
|
};
|
|
7696
7448
|
case "OPEN_DELETE": return {
|
|
7697
7449
|
...state,
|
|
7698
7450
|
deleteOpen: true,
|
|
7699
|
-
selected: action.payload
|
|
7451
|
+
selected: action$1.payload
|
|
7700
7452
|
};
|
|
7701
7453
|
case "OPEN_VIEW": return {
|
|
7702
7454
|
...state,
|
|
7703
7455
|
viewOpen: true,
|
|
7704
|
-
selected: action.payload
|
|
7456
|
+
selected: action$1.payload
|
|
7705
7457
|
};
|
|
7706
7458
|
case "CLOSE_ALL": return {
|
|
7707
7459
|
...state,
|
|
@@ -7714,7 +7466,7 @@ function modalReducer(state, action) {
|
|
|
7714
7466
|
};
|
|
7715
7467
|
case "SET_VARIANT": return {
|
|
7716
7468
|
...state,
|
|
7717
|
-
variant: action.payload
|
|
7469
|
+
variant: action$1.payload
|
|
7718
7470
|
};
|
|
7719
7471
|
default: return state;
|
|
7720
7472
|
}
|
|
@@ -8331,19 +8083,19 @@ function createMemoryDataSource(initialData = []) {
|
|
|
8331
8083
|
return newItem;
|
|
8332
8084
|
},
|
|
8333
8085
|
async update(id, updateData) {
|
|
8334
|
-
const index = data.findIndex((item) => item.id === id);
|
|
8335
|
-
if (index === -1) throw new Error(`Item with id ${id} not found`);
|
|
8086
|
+
const index$1 = data.findIndex((item) => item.id === id);
|
|
8087
|
+
if (index$1 === -1) throw new Error(`Item with id ${id} not found`);
|
|
8336
8088
|
const updated = {
|
|
8337
|
-
...data[index],
|
|
8089
|
+
...data[index$1],
|
|
8338
8090
|
...updateData
|
|
8339
8091
|
};
|
|
8340
|
-
data[index] = updated;
|
|
8092
|
+
data[index$1] = updated;
|
|
8341
8093
|
return updated;
|
|
8342
8094
|
},
|
|
8343
8095
|
async delete(id) {
|
|
8344
|
-
const index = data.findIndex((item) => item.id === id);
|
|
8345
|
-
if (index === -1) throw new Error(`Item with id ${id} not found`);
|
|
8346
|
-
data.splice(index, 1);
|
|
8096
|
+
const index$1 = data.findIndex((item) => item.id === id);
|
|
8097
|
+
if (index$1 === -1) throw new Error(`Item with id ${id} not found`);
|
|
8098
|
+
data.splice(index$1, 1);
|
|
8347
8099
|
},
|
|
8348
8100
|
async deleteMany(ids) {
|
|
8349
8101
|
data = data.filter((item) => !ids.includes(item.id));
|
|
@@ -8352,4 +8104,4 @@ function createMemoryDataSource(initialData = []) {
|
|
|
8352
8104
|
}
|
|
8353
8105
|
|
|
8354
8106
|
//#endregion
|
|
8355
|
-
export { AutoCrudTable, AutoForm, AutoTable, AutoTableActionBar, AutoTableSimpleFilters, CrudFormModal, DataTable, DataTableAdvancedToolbar, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, DataTableToolbar, DataTableViewOptions, ExportDialog, ImportDialog, MultiCombobox, SchemaAdapter, cn, coerceRowValues, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, formComponents, formatDate, frFR, generateCSVTemplate, getUrlParams, humanize, jaJP, koKR, noopToastAdapter, normalizeDataSourceConfig, normalizeOptions, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, resolveLocale, setSearchParams, setToolbarResolver, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates, zhCN };
|
|
8107
|
+
export { AutoCrudTable, AutoForm, AutoTable, AutoTableActionBar, AutoTableSimpleFilters, CrudFormModal, DataTable, DataTableAdvancedToolbar, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, DataTableToolbar, DataTableViewOptions, ExportDialog, ImportDialog, MultiCombobox, SchemaAdapter, cn, coerceRowValues, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, crudActions, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, formComponents, formatDate, frFR, generateCSVTemplate, getUrlParams, humanize, jaJP, koKR, noopToastAdapter, normalizeDataSourceConfig, normalizeOptions, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, resolveLocale, setSearchParams, setToolbarResolver, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates, zhCN };
|