@umodeapporg/ui 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/UI/DataTable/DataTableBulkEditDialog.d.ts +3 -3
- package/dist/components/UI/DataTable/DataTableBulkEditDialog.js +179 -150
- package/dist/components/UI/DataTable/DataTableBulkEditDialog.js.map +1 -1
- package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.d.ts +13 -0
- package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js +94 -0
- package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js.map +1 -0
- package/dist/components/UI/DataTable/DataTableBulkEditValueEditor.d.ts +16 -0
- package/dist/components/UI/DataTable/DataTableBulkEditValueEditor.js +79 -0
- package/dist/components/UI/DataTable/DataTableBulkEditValueEditor.js.map +1 -0
- package/dist/components/UI/DataTable/bulkEditValues.d.ts +8 -0
- package/dist/components/UI/DataTable/bulkEditValues.js +13 -0
- package/dist/components/UI/DataTable/bulkEditValues.js.map +1 -0
- package/dist/locales/en/ui.json.d.ts +13 -1
- package/dist/locales/en/ui.json.js +1 -1
- package/dist/locales/index.d.ts +24 -0
- package/dist/locales/pt/ui.json.d.ts +13 -1
- package/dist/locales/pt/ui.json.js +1 -1
- package/package.json +1 -1
|
@@ -13,9 +13,9 @@ interface IDataTableBulkEditDialogProps<T extends IDataTableRow> {
|
|
|
13
13
|
onApply: (changes: Array<IDataTableFillChange<T>>) => void;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Bulk edit in two modes. "Same value" applies one value to one field across the
|
|
17
|
+
* chosen scope; "Values per item" opens an item x fields matrix prefilled with
|
|
18
|
+
* the current values, and only the changed cells are written.
|
|
19
19
|
*
|
|
20
20
|
* O diálogo não persiste nada. Ele devolve as células escritas por `onApply`, que
|
|
21
21
|
* o host entrega ao `onFillCells` da fila de commit: um render, um flush e uma
|
|
@@ -1,31 +1,14 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useState, useRef, useEffect } from "react";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
|
-
import DatePicker from "../../DateTime/DatePicker.js";
|
|
5
|
-
import ControlledCheckbox from "../../Form/ControlledCheckbox.js";
|
|
6
4
|
import ControlledDialog from "../../Form/ControlledDialog.js";
|
|
7
|
-
import ControlledInput from "../../Form/ControlledInput.js";
|
|
8
5
|
import { ControlledSelect } from "../../Form/ControlledSelect.js";
|
|
9
6
|
import ControlledViewToggle from "../../Form/ControlledViewToggle.js";
|
|
10
|
-
import "
|
|
11
|
-
import "../../Form/ControlledButton.js";
|
|
12
|
-
import "tailwind-merge";
|
|
13
|
-
import "../ControlledMenu.js";
|
|
14
|
-
import "motion/react";
|
|
15
|
-
import "react-dom";
|
|
16
|
-
import "@floating-ui/dom";
|
|
17
|
-
import "../../../motion/motionPresets.js";
|
|
18
|
-
import "../../../motion/motionTokens.js";
|
|
19
|
-
import { normalizeText } from "../../../helpers/stringHelpers.js";
|
|
20
|
-
import "../MaterialSymbol/index.js";
|
|
21
|
-
import "date-fns";
|
|
22
|
-
import "../../DateTime/DateRangePicker.js";
|
|
7
|
+
import ControlledTooltip from "../ControlledTooltip.js";
|
|
23
8
|
import NoDataFound from "../NoDataFound/index.js";
|
|
24
|
-
import "
|
|
25
|
-
import "
|
|
26
|
-
import "
|
|
27
|
-
import "../../Navigation/UiNavigationContext.js";
|
|
28
|
-
import UnifiedSearchBar from "../../Search/UnifiedSearchBar.js";
|
|
9
|
+
import { isBulkValueEmpty, areBulkValuesEqual } from "./bulkEditValues.js";
|
|
10
|
+
import DataTableBulkEditRowsPicker from "./DataTableBulkEditRowsPicker.js";
|
|
11
|
+
import DataTableBulkEditValueEditor from "./DataTableBulkEditValueEditor.js";
|
|
29
12
|
const resolveDefaultRowLabel = (row, columns) => {
|
|
30
13
|
const label = (columns ?? []).filter((column) => column.type !== "actions" && !!column.accessor).map((column) => column.accessor?.(row)).find((value) => typeof value === "string" || typeof value === "number");
|
|
31
14
|
return label == null || label === "" ? row.id : String(label);
|
|
@@ -41,158 +24,229 @@ const DataTableBulkEditDialog = ({
|
|
|
41
24
|
}) => {
|
|
42
25
|
const { t } = useTranslation("ui");
|
|
43
26
|
const editableColumns = useMemo(() => (columns ?? []).filter((column) => !!column.editable), [columns]);
|
|
27
|
+
const [mode, setMode] = useState("same");
|
|
44
28
|
const [columnId, setColumnId] = useState("");
|
|
45
29
|
const [value, setValue] = useState("");
|
|
30
|
+
const [perItemColumnIds, setPerItemColumnIds] = useState([]);
|
|
31
|
+
const [drafts, setDrafts] = useState({});
|
|
46
32
|
const [scope, setScope] = useState("loaded");
|
|
47
33
|
const [pickedIds, setPickedIds] = useState([]);
|
|
48
|
-
const [searchTerm, setSearchTerm] = useState("");
|
|
49
34
|
const wasOpenRef = useRef(false);
|
|
50
35
|
useEffect(() => {
|
|
51
36
|
if (open === wasOpenRef.current) return;
|
|
52
37
|
wasOpenRef.current = open;
|
|
53
38
|
if (!open) return;
|
|
54
39
|
const preselected = Array.from(initialTargetIds ?? []);
|
|
40
|
+
setMode("same");
|
|
55
41
|
setColumnId(editableColumns[0]?.id ?? "");
|
|
56
42
|
setValue("");
|
|
43
|
+
setPerItemColumnIds([]);
|
|
44
|
+
setDrafts({});
|
|
57
45
|
setScope(preselected.length > 0 ? "picked" : "loaded");
|
|
58
46
|
setPickedIds(preselected);
|
|
59
|
-
setSearchTerm("");
|
|
60
47
|
}, [open, editableColumns, initialTargetIds]);
|
|
61
48
|
const selectedColumn = useMemo(
|
|
62
49
|
() => editableColumns.find((column) => column.id === columnId),
|
|
63
50
|
[editableColumns, columnId]
|
|
64
51
|
);
|
|
65
52
|
const editable = selectedColumn?.editable;
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
53
|
+
const selectedPerItemColumns = useMemo(
|
|
54
|
+
() => perItemColumnIds.map((id) => editableColumns.find((column) => column.id === id)).filter((column) => !!column),
|
|
55
|
+
[perItemColumnIds, editableColumns]
|
|
56
|
+
);
|
|
69
57
|
const resolveLabel = useMemo(
|
|
70
58
|
() => getRowLabel ?? ((row) => resolveDefaultRowLabel(row, columns ?? [])),
|
|
71
59
|
[getRowLabel, columns]
|
|
72
60
|
);
|
|
73
|
-
const eligibleRows = useMemo(
|
|
74
|
-
()
|
|
75
|
-
|
|
76
|
-
|
|
61
|
+
const eligibleRows = useMemo(() => {
|
|
62
|
+
if (mode === "same") return (rows ?? []).filter((row) => !(editable?.disabled?.(row) ?? false));
|
|
63
|
+
const referenceColumns = selectedPerItemColumns.length > 0 ? selectedPerItemColumns : editableColumns;
|
|
64
|
+
return (rows ?? []).filter(
|
|
65
|
+
(row) => referenceColumns.some((column) => !(column.editable?.disabled?.(row) ?? false))
|
|
66
|
+
);
|
|
67
|
+
}, [mode, rows, editable, selectedPerItemColumns, editableColumns]);
|
|
77
68
|
const pickedIdSet = useMemo(() => new Set(pickedIds), [pickedIds]);
|
|
78
69
|
const targetRows = useMemo(
|
|
79
70
|
() => scope === "loaded" ? eligibleRows : eligibleRows.filter((row) => pickedIdSet.has(row.id)),
|
|
80
71
|
[scope, eligibleRows, pickedIdSet]
|
|
81
72
|
);
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
return eligibleRows.filter((row) => normalizeText(resolveLabel(row)).includes(term));
|
|
86
|
-
}, [eligibleRows, searchTerm, resolveLabel]);
|
|
87
|
-
const validationError = useMemo(() => {
|
|
88
|
-
if (!editable?.validate) return null;
|
|
73
|
+
const isValueEmpty = isBulkValueEmpty(value);
|
|
74
|
+
const sameValueValidationError = useMemo(() => {
|
|
75
|
+
if (mode !== "same" || !editable?.validate) return null;
|
|
89
76
|
const firstInvalid = targetRows.map((row) => editable.validate?.(value, row) ?? null).find((message) => !!message);
|
|
90
77
|
return firstInvalid ?? null;
|
|
91
|
-
}, [editable, targetRows, value]);
|
|
78
|
+
}, [mode, editable, targetRows, value]);
|
|
79
|
+
const perItemChanges = useMemo(() => {
|
|
80
|
+
if (mode !== "perItem") return [];
|
|
81
|
+
const changes = [];
|
|
82
|
+
targetRows.forEach((row) => {
|
|
83
|
+
const rowDraft = drafts[row.id];
|
|
84
|
+
if (!rowDraft) return;
|
|
85
|
+
selectedPerItemColumns.forEach((column) => {
|
|
86
|
+
if (!Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return;
|
|
87
|
+
if (column.editable?.disabled?.(row) ?? false) return;
|
|
88
|
+
const draftValue = rowDraft[column.id];
|
|
89
|
+
if (areBulkValuesEqual(draftValue, column.editable?.getValue(row) ?? null)) return;
|
|
90
|
+
changes.push({ row, columnId: column.id, value: draftValue });
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
return changes;
|
|
94
|
+
}, [mode, targetRows, drafts, selectedPerItemColumns]);
|
|
95
|
+
const perItemValidationError = useMemo(() => {
|
|
96
|
+
const firstInvalid = perItemChanges.map((change) => {
|
|
97
|
+
const column = selectedPerItemColumns.find((entry) => entry.id === change.columnId);
|
|
98
|
+
return column?.editable?.validate?.(change.value, change.row) ?? null;
|
|
99
|
+
}).find((message) => !!message);
|
|
100
|
+
return firstInvalid ?? null;
|
|
101
|
+
}, [perItemChanges, selectedPerItemColumns]);
|
|
102
|
+
const setDraftValue = (rowId, targetColumnId, nextValue) => setDrafts((current) => ({
|
|
103
|
+
...current,
|
|
104
|
+
[rowId]: { ...current[rowId], [targetColumnId]: nextValue }
|
|
105
|
+
}));
|
|
106
|
+
const resolveCellValue = (row, column) => {
|
|
107
|
+
const rowDraft = drafts[row.id];
|
|
108
|
+
if (rowDraft && Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return rowDraft[column.id];
|
|
109
|
+
return column.editable?.getValue(row) ?? null;
|
|
110
|
+
};
|
|
111
|
+
const isCellDirty = (row, column) => {
|
|
112
|
+
const rowDraft = drafts[row.id];
|
|
113
|
+
if (!rowDraft || !Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return false;
|
|
114
|
+
return !areBulkValuesEqual(rowDraft[column.id], column.editable?.getValue(row) ?? null);
|
|
115
|
+
};
|
|
116
|
+
const modeOptions = [
|
|
117
|
+
{ value: "same", label: t("dataTable.bulkEdit.modeSame"), icon: "stacks" },
|
|
118
|
+
{ value: "perItem", label: t("dataTable.bulkEdit.modePerItem"), icon: "edit_note" }
|
|
119
|
+
];
|
|
92
120
|
const scopeOptions = [
|
|
93
121
|
{ value: "loaded", label: t("dataTable.bulkEdit.scopeLoaded"), icon: "select_all", count: eligibleRows.length },
|
|
94
122
|
{ value: "picked", label: t("dataTable.bulkEdit.scopePicked"), icon: "checklist", count: pickedIdSet.size }
|
|
95
123
|
];
|
|
96
|
-
const togglePicked = (rowId) => setPickedIds((current) => current.includes(rowId) ? current.filter((id) => id !== rowId) : [...current, rowId]);
|
|
97
|
-
const areAllVisiblePicked = visibleRows.length > 0 && visibleRows.every((row) => pickedIdSet.has(row.id));
|
|
98
|
-
const toggleAllVisible = () => setPickedIds((current) => {
|
|
99
|
-
const visibleIds = visibleRows.map((row) => row.id);
|
|
100
|
-
if (areAllVisiblePicked) return current.filter((id) => !visibleIds.includes(id));
|
|
101
|
-
return Array.from(/* @__PURE__ */ new Set([...current, ...visibleIds]));
|
|
102
|
-
});
|
|
103
124
|
const handleApply = () => {
|
|
125
|
+
if (mode === "perItem") {
|
|
126
|
+
if (perItemChanges.length === 0) return;
|
|
127
|
+
onApply(perItemChanges);
|
|
128
|
+
onClose();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
104
131
|
if (!selectedColumn || targetRows.length === 0) return;
|
|
105
132
|
onApply(targetRows.map((row) => ({ row, columnId: selectedColumn.id, value })));
|
|
106
133
|
onClose();
|
|
107
134
|
};
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (editable.type === "multiselect") {
|
|
114
|
-
return /* @__PURE__ */ jsx(
|
|
115
|
-
ControlledSelect,
|
|
116
|
-
{
|
|
117
|
-
className: "w-full",
|
|
118
|
-
label: t("dataTable.bulkEdit.newValue"),
|
|
119
|
-
options: Array.isArray(editable.options) ? editable.options : [],
|
|
120
|
-
selectedValue: arrayValue,
|
|
121
|
-
onChangeHook: (selected) => setValue(Array.isArray(selected) ? selected : [selected]),
|
|
122
|
-
multiple: true,
|
|
123
|
-
searchable: true,
|
|
124
|
-
clearable: true
|
|
125
|
-
}
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
if (editable.type === "select") {
|
|
129
|
-
return /* @__PURE__ */ jsx(
|
|
130
|
-
ControlledSelect,
|
|
131
|
-
{
|
|
132
|
-
className: "w-full",
|
|
133
|
-
label: t("dataTable.bulkEdit.newValue"),
|
|
134
|
-
options: Array.isArray(editable.options) ? editable.options : [],
|
|
135
|
-
selectedValue: stringValue,
|
|
136
|
-
onChangeHook: (selected) => setValue(typeof selected === "string" ? selected : ""),
|
|
137
|
-
multiple: false,
|
|
138
|
-
searchable: true,
|
|
139
|
-
clearable: true
|
|
140
|
-
}
|
|
141
|
-
);
|
|
135
|
+
const confirmLabel = mode === "perItem" ? perItemChanges.length === 0 ? t("dataTable.bulkEdit.confirmNoChanges") : t("dataTable.bulkEdit.confirmChanges", { count: perItemChanges.length }) : targetRows.length === 0 ? t("dataTable.bulkEdit.confirmEmpty") : t("dataTable.bulkEdit.confirm", { count: targetRows.length });
|
|
136
|
+
const disableConfirm = mode === "perItem" ? perItemChanges.length === 0 || !!perItemValidationError : !selectedColumn || targetRows.length === 0 || isValueEmpty || !!sameValueValidationError;
|
|
137
|
+
const renderPerItemMatrix = () => {
|
|
138
|
+
if (selectedPerItemColumns.length === 0) {
|
|
139
|
+
return /* @__PURE__ */ jsx(NoDataFound, { size: "sm", variant: "waiting", message: t("dataTable.bulkEdit.pickFieldsHint") });
|
|
142
140
|
}
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
const startValue = parsed && !Number.isNaN(parsed.getTime()) ? parsed : void 0;
|
|
146
|
-
return /* @__PURE__ */ jsx(
|
|
147
|
-
DatePicker,
|
|
148
|
-
{
|
|
149
|
-
label: t("dataTable.bulkEdit.newValue"),
|
|
150
|
-
startValue,
|
|
151
|
-
onUpdate: (nextValue) => setValue(nextValue ?? "")
|
|
152
|
-
}
|
|
153
|
-
);
|
|
141
|
+
if (targetRows.length === 0) {
|
|
142
|
+
return /* @__PURE__ */ jsx(NoDataFound, { size: "sm", variant: "waiting", message: t("dataTable.bulkEdit.noTargets") });
|
|
154
143
|
}
|
|
155
|
-
return /* @__PURE__ */
|
|
156
|
-
|
|
157
|
-
{
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
144
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-col gap-2 rounded-xl border border-border bg-surface-sunken p-3", children: [
|
|
145
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-foreground-muted", children: t("dataTable.bulkEdit.perItemHint") }),
|
|
146
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-max flex-col gap-2", children: [
|
|
147
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
148
|
+
/* @__PURE__ */ jsx("span", { className: "w-52 shrink-0 text-xs font-medium uppercase tracking-wide text-foreground-subtle", children: t("dataTable.bulkEdit.itemColumn") }),
|
|
149
|
+
selectedPerItemColumns.map((column) => /* @__PURE__ */ jsx(
|
|
150
|
+
"span",
|
|
151
|
+
{
|
|
152
|
+
className: "w-64 shrink-0 truncate text-xs font-medium uppercase tracking-wide text-foreground-subtle",
|
|
153
|
+
children: column.header
|
|
154
|
+
},
|
|
155
|
+
column.id
|
|
156
|
+
))
|
|
157
|
+
] }),
|
|
158
|
+
/* @__PURE__ */ jsx("div", { className: "flex max-h-96 flex-col gap-2 overflow-y-auto pt-1", children: targetRows.map((row) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
159
|
+
/* @__PURE__ */ jsx(ControlledTooltip, { content: resolveLabel(row), className: "z-[9999]", placement: "top-start", children: /* @__PURE__ */ jsx("span", { className: "w-52 shrink-0 truncate text-sm text-foreground", children: resolveLabel(row) }) }),
|
|
160
|
+
selectedPerItemColumns.map((column) => {
|
|
161
|
+
const cellEditable = column.editable;
|
|
162
|
+
if (!cellEditable) return /* @__PURE__ */ jsx("span", { className: "w-64 shrink-0" }, column.id);
|
|
163
|
+
const isDisabled = cellEditable.disabled?.(row) ?? false;
|
|
164
|
+
const cellValue = resolveCellValue(row, column);
|
|
165
|
+
const dirty = isCellDirty(row, column);
|
|
166
|
+
const cellError = dirty ? cellEditable.validate?.(cellValue, row) ?? null : null;
|
|
167
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative w-64 shrink-0", children: [
|
|
168
|
+
dirty && /* @__PURE__ */ jsx("span", { className: "absolute -right-1 -top-1 z-10 size-2 rounded-full bg-brand", "aria-hidden": true }),
|
|
169
|
+
/* @__PURE__ */ jsx(
|
|
170
|
+
DataTableBulkEditValueEditor,
|
|
171
|
+
{
|
|
172
|
+
editable: cellEditable,
|
|
173
|
+
value: cellValue,
|
|
174
|
+
onChange: (nextValue) => setDraftValue(row.id, column.id, nextValue),
|
|
175
|
+
disabled: isDisabled,
|
|
176
|
+
errorMessage: cellError
|
|
177
|
+
}
|
|
178
|
+
)
|
|
179
|
+
] }, column.id);
|
|
180
|
+
})
|
|
181
|
+
] }, row.id)) })
|
|
182
|
+
] }) })
|
|
183
|
+
] });
|
|
166
184
|
};
|
|
167
185
|
return /* @__PURE__ */ jsx(
|
|
168
186
|
ControlledDialog,
|
|
169
187
|
{
|
|
170
188
|
open,
|
|
171
|
-
size: "md",
|
|
189
|
+
size: mode === "perItem" ? "xl" : "md",
|
|
172
190
|
title: t("dataTable.bulkEdit.title"),
|
|
173
|
-
subtitle: t("dataTable.bulkEdit.subtitle"),
|
|
191
|
+
subtitle: mode === "perItem" ? t("dataTable.bulkEdit.subtitlePerItem") : t("dataTable.bulkEdit.subtitle"),
|
|
174
192
|
onCancel: onClose,
|
|
175
193
|
onConfirm: handleApply,
|
|
176
|
-
confirmLabel
|
|
194
|
+
confirmLabel,
|
|
177
195
|
cancelLabel: t("actions.cancel"),
|
|
178
|
-
disableConfirm
|
|
196
|
+
disableConfirm,
|
|
179
197
|
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
180
|
-
/* @__PURE__ */
|
|
198
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
199
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium uppercase tracking-wide text-foreground-subtle", children: t("dataTable.bulkEdit.modeLabel") }),
|
|
200
|
+
/* @__PURE__ */ jsx(
|
|
201
|
+
ControlledViewToggle,
|
|
202
|
+
{
|
|
203
|
+
options: modeOptions,
|
|
204
|
+
value: mode,
|
|
205
|
+
onChange: setMode,
|
|
206
|
+
showLabels: true,
|
|
207
|
+
size: "sm"
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
] }),
|
|
211
|
+
mode === "same" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
212
|
+
/* @__PURE__ */ jsx(
|
|
213
|
+
ControlledSelect,
|
|
214
|
+
{
|
|
215
|
+
className: "w-full",
|
|
216
|
+
label: t("dataTable.bulkEdit.field"),
|
|
217
|
+
options: editableColumns.map((column) => ({ value: column.id, label: column.header })),
|
|
218
|
+
selectedValue: columnId,
|
|
219
|
+
onChangeHook: (selected) => {
|
|
220
|
+
setColumnId(typeof selected === "string" ? selected : "");
|
|
221
|
+
setValue("");
|
|
222
|
+
},
|
|
223
|
+
multiple: false,
|
|
224
|
+
searchable: true
|
|
225
|
+
}
|
|
226
|
+
),
|
|
227
|
+
editable && /* @__PURE__ */ jsx(
|
|
228
|
+
DataTableBulkEditValueEditor,
|
|
229
|
+
{
|
|
230
|
+
editable,
|
|
231
|
+
value,
|
|
232
|
+
onChange: setValue,
|
|
233
|
+
label: t("dataTable.bulkEdit.newValue"),
|
|
234
|
+
errorMessage: sameValueValidationError
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
] }) : /* @__PURE__ */ jsx(
|
|
181
238
|
ControlledSelect,
|
|
182
239
|
{
|
|
183
240
|
className: "w-full",
|
|
184
|
-
label: t("dataTable.bulkEdit.
|
|
241
|
+
label: t("dataTable.bulkEdit.fields"),
|
|
185
242
|
options: editableColumns.map((column) => ({ value: column.id, label: column.header })),
|
|
186
|
-
selectedValue:
|
|
187
|
-
onChangeHook: (selected) =>
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
multiple: false,
|
|
192
|
-
searchable: true
|
|
243
|
+
selectedValue: perItemColumnIds,
|
|
244
|
+
onChangeHook: (selected) => setPerItemColumnIds(Array.isArray(selected) ? selected : [selected].filter(Boolean)),
|
|
245
|
+
multiple: true,
|
|
246
|
+
searchable: true,
|
|
247
|
+
clearable: true
|
|
193
248
|
}
|
|
194
249
|
),
|
|
195
|
-
renderValueEditor(),
|
|
196
250
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
197
251
|
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium uppercase tracking-wide text-foreground-subtle", children: t("dataTable.bulkEdit.scopeLabel") }),
|
|
198
252
|
/* @__PURE__ */ jsx(
|
|
@@ -206,41 +260,16 @@ const DataTableBulkEditDialog = ({
|
|
|
206
260
|
}
|
|
207
261
|
)
|
|
208
262
|
] }),
|
|
209
|
-
scope === "picked" && /* @__PURE__ */
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
onClearFilter: () => setSearchTerm("")
|
|
220
|
-
}
|
|
221
|
-
),
|
|
222
|
-
visibleRows.length === 0 ? /* @__PURE__ */ jsx(NoDataFound, { size: "sm", variant: "search", message: t("dataTable.bulkEdit.noRowsFound") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
223
|
-
/* @__PURE__ */ jsx(
|
|
224
|
-
ControlledCheckbox,
|
|
225
|
-
{
|
|
226
|
-
checked: areAllVisiblePicked,
|
|
227
|
-
onChange: toggleAllVisible,
|
|
228
|
-
label: t("dataTable.bulkEdit.selectAll", { count: visibleRows.length }),
|
|
229
|
-
size: "sm"
|
|
230
|
-
}
|
|
231
|
-
),
|
|
232
|
-
/* @__PURE__ */ jsx("div", { className: "flex max-h-64 flex-col overflow-y-auto", children: visibleRows.map((row) => /* @__PURE__ */ jsx(
|
|
233
|
-
ControlledCheckbox,
|
|
234
|
-
{
|
|
235
|
-
checked: pickedIdSet.has(row.id),
|
|
236
|
-
onChange: () => togglePicked(row.id),
|
|
237
|
-
label: resolveLabel(row),
|
|
238
|
-
size: "sm"
|
|
239
|
-
},
|
|
240
|
-
row.id
|
|
241
|
-
)) })
|
|
242
|
-
] })
|
|
243
|
-
] })
|
|
263
|
+
scope === "picked" && /* @__PURE__ */ jsx(
|
|
264
|
+
DataTableBulkEditRowsPicker,
|
|
265
|
+
{
|
|
266
|
+
rows: eligibleRows,
|
|
267
|
+
resolveLabel,
|
|
268
|
+
pickedIds,
|
|
269
|
+
onChange: setPickedIds
|
|
270
|
+
}
|
|
271
|
+
),
|
|
272
|
+
mode === "perItem" && renderPerItemMatrix()
|
|
244
273
|
] })
|
|
245
274
|
}
|
|
246
275
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTableBulkEditDialog.js","sources":["../../../../src/components/UI/DataTable/DataTableBulkEditDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport DatePicker from '@/components/DateTime/DatePicker';\nimport ControlledCheckbox from '@/components/Form/ControlledCheckbox';\nimport ControlledDialog from '@/components/Form/ControlledDialog';\nimport ControlledInput from '@/components/Form/ControlledInput';\nimport { ControlledSelect } from '@/components/Form/ControlledSelect';\nimport ControlledViewToggle, { type IControlledViewToggleOption } from '@/components/Form/ControlledViewToggle';\nimport { UnifiedSearchBar } from '@/components/Search';\nimport NoDataFound from '@/components/UI/NoDataFound';\nimport { normalizeText } from '@/helpers/stringHelpers';\n\nimport type { IDataTableColumnConfig, IDataTableFillChange, IDataTableRow } from './types';\n\ntype TBulkEditScope = 'loaded' | 'picked';\n\ninterface IDataTableBulkEditDialogProps<T extends IDataTableRow> {\n open: boolean;\n onClose: () => void;\n /** Todas as colunas da tabela: só as editáveis viram opção de campo. */\n columns: Array<IDataTableColumnConfig<T>>;\n /** As linhas carregadas: a página atual na lista, o acumulado do scroll nos cards. */\n rows: Array<T>;\n /** Rótulo de cada linha na lista de escolha. Sem ele, a primeira coluna de texto responde. */\n getRowLabel?: (row: T) => string;\n /** Ids já escolhidos fora do diálogo: abre no modo \"escolher\" com eles marcados. */\n initialTargetIds?: ReadonlyArray<string>;\n onApply: (changes: Array<IDataTableFillChange<T>>) => void;\n}\n\n/**\n * Sem `getRowLabel`, o nome sai da primeira coluna que sabe devolver um valor\n * legível. Colunas que só desenham (`render`, sem `accessor`) não respondem, e é\n * por isso que uma tabela cuja identidade mora numa coluna `custom` precisa\n * declarar `getRowLabel`: aqui só sobraria o id.\n */\nconst resolveDefaultRowLabel = <T extends IDataTableRow>(row: T, columns: Array<IDataTableColumnConfig<T>>): string => {\n const label = (columns ?? [])\n .filter((column) => column.type !== 'actions' && !!column.accessor)\n .map((column) => column.accessor?.(row))\n .find((value) => typeof value === 'string' || typeof value === 'number');\n\n return label == null || label === '' ? row.id : String(label);\n};\n\n/**\n * Aplica um valor a um campo em várias linhas de uma vez. É a mesma edição de\n * célula da planilha, com o alcance escolhido pelo usuário: todas as linhas\n * carregadas ou um subconjunto marcado na lista.\n *\n * O diálogo não persiste nada. Ele devolve as células escritas por `onApply`, que\n * o host entrega ao `onFillCells` da fila de commit: um render, um flush e uma\n * requisição em lote, exatamente como o preenchimento por arraste.\n */\nconst DataTableBulkEditDialog = <T extends IDataTableRow>({\n open,\n onClose,\n columns,\n rows,\n getRowLabel,\n initialTargetIds,\n onApply,\n}: IDataTableBulkEditDialogProps<T>) => {\n const { t } = useTranslation('ui');\n\n const editableColumns = useMemo(() => (columns ?? []).filter((column) => !!column.editable), [columns]);\n\n const [columnId, setColumnId] = useState('');\n const [value, setValue] = useState<unknown>('');\n const [scope, setScope] = useState<TBulkEditScope>('loaded');\n const [pickedIds, setPickedIds] = useState<Array<string>>([]);\n const [searchTerm, setSearchTerm] = useState('');\n\n // Cada abertura recomeça do zero: um valor sobrando da vez anterior seria\n // aplicado em massa por um clique só. O reset acontece na transição de abrir,\n // nunca por uma nova referência de `columns` ou de `initialTargetIds`, que a\n // tela recria ao marcar uma linha atrás do diálogo.\n const wasOpenRef = useRef(false);\n useEffect(() => {\n if (open === wasOpenRef.current) return;\n wasOpenRef.current = open;\n if (!open) return;\n\n const preselected = Array.from(initialTargetIds ?? []);\n setColumnId(editableColumns[0]?.id ?? '');\n setValue('');\n setScope(preselected.length > 0 ? 'picked' : 'loaded');\n setPickedIds(preselected);\n setSearchTerm('');\n }, [open, editableColumns, initialTargetIds]);\n\n const selectedColumn = useMemo(\n () => editableColumns.find((column) => column.id === columnId),\n [editableColumns, columnId],\n );\n const editable = selectedColumn?.editable;\n\n const arrayValue = Array.isArray(value) ? value.map(String) : [];\n const stringValue = value == null || Array.isArray(value) ? '' : String(value);\n // Vazio não aplica: o diálogo abre sem valor, e um Enter distraído limparia o\n // campo em todo o alcance. Limpar continua sendo edição célula a célula.\n const isValueEmpty = Array.isArray(value) ? value.length === 0 : value == null || value === '';\n\n const resolveLabel = useMemo(\n () => getRowLabel ?? ((row: T) => resolveDefaultRowLabel(row, columns ?? [])),\n [getRowLabel, columns],\n );\n\n // Uma linha que o próprio editor recusa (variante, registro bloqueado) não entra\n // no alcance nem aparece na lista: prometer a alteração e descartá-la é pior.\n const eligibleRows = useMemo(\n () => (rows ?? []).filter((row) => !(editable?.disabled?.(row) ?? false)),\n [rows, editable],\n );\n\n const pickedIdSet = useMemo(() => new Set(pickedIds), [pickedIds]);\n\n const targetRows = useMemo(\n () => (scope === 'loaded' ? eligibleRows : eligibleRows.filter((row) => pickedIdSet.has(row.id))),\n [scope, eligibleRows, pickedIdSet],\n );\n\n const visibleRows = useMemo(() => {\n const term = normalizeText(searchTerm.trim());\n if (term === '') return eligibleRows;\n\n return eligibleRows.filter((row) => normalizeText(resolveLabel(row)).includes(term));\n }, [eligibleRows, searchTerm, resolveLabel]);\n\n const validationError = useMemo(() => {\n if (!editable?.validate) return null;\n\n const firstInvalid = targetRows.map((row) => editable.validate?.(value, row) ?? null).find((message) => !!message);\n\n return firstInvalid ?? null;\n }, [editable, targetRows, value]);\n\n const scopeOptions: Array<IControlledViewToggleOption<TBulkEditScope>> = [\n { value: 'loaded', label: t('dataTable.bulkEdit.scopeLoaded'), icon: 'select_all', count: eligibleRows.length },\n { value: 'picked', label: t('dataTable.bulkEdit.scopePicked'), icon: 'checklist', count: pickedIdSet.size },\n ];\n\n const togglePicked = (rowId: string) =>\n setPickedIds((current) => (current.includes(rowId) ? current.filter((id) => id !== rowId) : [...current, rowId]));\n\n const areAllVisiblePicked = visibleRows.length > 0 && visibleRows.every((row) => pickedIdSet.has(row.id));\n\n const toggleAllVisible = () =>\n setPickedIds((current) => {\n const visibleIds = visibleRows.map((row) => row.id);\n if (areAllVisiblePicked) return current.filter((id) => !visibleIds.includes(id));\n\n return Array.from(new Set([...current, ...visibleIds]));\n });\n\n const handleApply = () => {\n if (!selectedColumn || targetRows.length === 0) return;\n\n onApply(targetRows.map((row) => ({ row, columnId: selectedColumn.id, value })));\n onClose();\n };\n\n const renderValueEditor = () => {\n if (!editable) return null;\n\n if (editable.renderEditor) {\n return editable.renderEditor({ value, onChange: setValue, label: t('dataTable.bulkEdit.newValue') });\n }\n\n if (editable.type === 'multiselect') {\n return (\n <ControlledSelect\n className=\"w-full\"\n label={t('dataTable.bulkEdit.newValue')}\n options={Array.isArray(editable.options) ? editable.options : []}\n selectedValue={arrayValue}\n onChangeHook={(selected) => setValue(Array.isArray(selected) ? selected : [selected])}\n multiple\n searchable\n clearable\n />\n );\n }\n\n if (editable.type === 'select') {\n return (\n <ControlledSelect\n className=\"w-full\"\n label={t('dataTable.bulkEdit.newValue')}\n options={Array.isArray(editable.options) ? editable.options : []}\n selectedValue={stringValue}\n onChangeHook={(selected) => setValue(typeof selected === 'string' ? selected : '')}\n multiple={false}\n searchable\n clearable\n />\n );\n }\n\n if (editable.type === 'date') {\n const parsed = stringValue ? new Date(stringValue) : undefined;\n const startValue = parsed && !Number.isNaN(parsed.getTime()) ? parsed : undefined;\n\n return (\n <DatePicker\n label={t('dataTable.bulkEdit.newValue')}\n startValue={startValue}\n onUpdate={(nextValue) => setValue(nextValue ?? '')}\n />\n );\n }\n\n return (\n <ControlledInput\n label={t('dataTable.bulkEdit.newValue')}\n value={stringValue}\n onChange={(event) => setValue(event.target.value)}\n inputMode={editable.type === 'number' ? 'decimal' : undefined}\n error={!!validationError}\n errorMessage={validationError ?? undefined}\n />\n );\n };\n\n return (\n <ControlledDialog\n open={open}\n size=\"md\"\n title={t('dataTable.bulkEdit.title')}\n subtitle={t('dataTable.bulkEdit.subtitle')}\n onCancel={onClose}\n onConfirm={handleApply}\n confirmLabel={\n targetRows.length === 0\n ? t('dataTable.bulkEdit.confirmEmpty')\n : t('dataTable.bulkEdit.confirm', { count: targetRows.length })\n }\n cancelLabel={t('actions.cancel')}\n disableConfirm={!selectedColumn || targetRows.length === 0 || isValueEmpty || !!validationError}\n >\n <div className=\"flex flex-col gap-4\">\n <ControlledSelect\n className=\"w-full\"\n label={t('dataTable.bulkEdit.field')}\n options={editableColumns.map((column) => ({ value: column.id, label: column.header }))}\n selectedValue={columnId}\n onChangeHook={(selected) => {\n setColumnId(typeof selected === 'string' ? selected : '');\n setValue('');\n }}\n multiple={false}\n searchable\n />\n\n {renderValueEditor()}\n\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-foreground-subtle\">\n {t('dataTable.bulkEdit.scopeLabel')}\n </span>\n <ControlledViewToggle<TBulkEditScope>\n options={scopeOptions}\n value={scope}\n onChange={setScope}\n showLabels\n size=\"sm\"\n />\n </div>\n\n {scope === 'picked' && (\n <div className=\"flex min-h-0 flex-col gap-2 rounded-xl border border-border bg-surface-sunken p-3\">\n <UnifiedSearchBar\n mode=\"callback\"\n label={t('dataTable.bulkEdit.searchRows')}\n debounceTime={200}\n minSearchLength={1}\n showClearButton\n filterCallback={setSearchTerm}\n onClearFilter={() => setSearchTerm('')}\n />\n\n {visibleRows.length === 0 ? (\n <NoDataFound size=\"sm\" variant=\"search\" message={t('dataTable.bulkEdit.noRowsFound')} />\n ) : (\n <>\n <ControlledCheckbox\n checked={areAllVisiblePicked}\n onChange={toggleAllVisible}\n label={t('dataTable.bulkEdit.selectAll', { count: visibleRows.length })}\n size=\"sm\"\n />\n <div className=\"flex max-h-64 flex-col overflow-y-auto\">\n {visibleRows.map((row) => (\n <ControlledCheckbox\n key={row.id}\n checked={pickedIdSet.has(row.id)}\n onChange={() => togglePicked(row.id)}\n label={resolveLabel(row)}\n size=\"sm\"\n />\n ))}\n </div>\n </>\n )}\n </div>\n )}\n </div>\n </ControlledDialog>\n );\n};\n\nexport default DataTableBulkEditDialog;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAM,yBAAyB,CAA0B,KAAQ,YAAsD;AACrH,QAAM,SAAS,WAAW,CAAA,GACvB,OAAO,CAAC,WAAW,OAAO,SAAS,aAAa,CAAC,CAAC,OAAO,QAAQ,EACjE,IAAI,CAAC,WAAW,OAAO,WAAW,GAAG,CAAC,EACtC,KAAK,CAAC,UAAU,OAAO,UAAU,YAAY,OAAO,UAAU,QAAQ;AAEzE,SAAO,SAAS,QAAQ,UAAU,KAAK,IAAI,KAAK,OAAO,KAAK;AAC9D;AAWA,MAAM,0BAA0B,CAA0B;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAwC;AACtC,QAAM,EAAE,EAAA,IAAM,eAAe,IAAI;AAEjC,QAAM,kBAAkB,QAAQ,OAAO,WAAW,CAAA,GAAI,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,QAAQ,GAAG,CAAC,OAAO,CAAC;AAEtG,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,EAAE;AAC3C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAkB,EAAE;AAC9C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAyB,QAAQ;AAC3D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAwB,CAAA,CAAE;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,EAAE;AAM/C,QAAM,aAAa,OAAO,KAAK;AAC/B,YAAU,MAAM;AACd,QAAI,SAAS,WAAW,QAAS;AACjC,eAAW,UAAU;AACrB,QAAI,CAAC,KAAM;AAEX,UAAM,cAAc,MAAM,KAAK,oBAAoB,CAAA,CAAE;AACrD,gBAAY,gBAAgB,CAAC,GAAG,MAAM,EAAE;AACxC,aAAS,EAAE;AACX,aAAS,YAAY,SAAS,IAAI,WAAW,QAAQ;AACrD,iBAAa,WAAW;AACxB,kBAAc,EAAE;AAAA,EAClB,GAAG,CAAC,MAAM,iBAAiB,gBAAgB,CAAC;AAE5C,QAAM,iBAAiB;AAAA,IACrB,MAAM,gBAAgB,KAAK,CAAC,WAAW,OAAO,OAAO,QAAQ;AAAA,IAC7D,CAAC,iBAAiB,QAAQ;AAAA,EAAA;AAE5B,QAAM,WAAW,gBAAgB;AAEjC,QAAM,aAAa,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,MAAM,IAAI,CAAA;AAC9D,QAAM,cAAc,SAAS,QAAQ,MAAM,QAAQ,KAAK,IAAI,KAAK,OAAO,KAAK;AAG7E,QAAM,eAAe,MAAM,QAAQ,KAAK,IAAI,MAAM,WAAW,IAAI,SAAS,QAAQ,UAAU;AAE5F,QAAM,eAAe;AAAA,IACnB,MAAM,gBAAgB,CAAC,QAAW,uBAAuB,KAAK,WAAW,CAAA,CAAE;AAAA,IAC3E,CAAC,aAAa,OAAO;AAAA,EAAA;AAKvB,QAAM,eAAe;AAAA,IACnB,OAAO,QAAQ,IAAI,OAAO,CAAC,QAAQ,EAAE,UAAU,WAAW,GAAG,KAAK,MAAM;AAAA,IACxE,CAAC,MAAM,QAAQ;AAAA,EAAA;AAGjB,QAAM,cAAc,QAAQ,MAAM,IAAI,IAAI,SAAS,GAAG,CAAC,SAAS,CAAC;AAEjE,QAAM,aAAa;AAAA,IACjB,MAAO,UAAU,WAAW,eAAe,aAAa,OAAO,CAAC,QAAQ,YAAY,IAAI,IAAI,EAAE,CAAC;AAAA,IAC/F,CAAC,OAAO,cAAc,WAAW;AAAA,EAAA;AAGnC,QAAM,cAAc,QAAQ,MAAM;AAChC,UAAM,OAAO,cAAc,WAAW,KAAA,CAAM;AAC5C,QAAI,SAAS,GAAI,QAAO;AAExB,WAAO,aAAa,OAAO,CAAC,QAAQ,cAAc,aAAa,GAAG,CAAC,EAAE,SAAS,IAAI,CAAC;AAAA,EACrF,GAAG,CAAC,cAAc,YAAY,YAAY,CAAC;AAE3C,QAAM,kBAAkB,QAAQ,MAAM;AACpC,QAAI,CAAC,UAAU,SAAU,QAAO;AAEhC,UAAM,eAAe,WAAW,IAAI,CAAC,QAAQ,SAAS,WAAW,OAAO,GAAG,KAAK,IAAI,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,OAAO;AAEjH,WAAO,gBAAgB;AAAA,EACzB,GAAG,CAAC,UAAU,YAAY,KAAK,CAAC;AAEhC,QAAM,eAAmE;AAAA,IACvE,EAAE,OAAO,UAAU,OAAO,EAAE,gCAAgC,GAAG,MAAM,cAAc,OAAO,aAAa,OAAA;AAAA,IACvG,EAAE,OAAO,UAAU,OAAO,EAAE,gCAAgC,GAAG,MAAM,aAAa,OAAO,YAAY,KAAA;AAAA,EAAK;AAG5G,QAAM,eAAe,CAAC,UACpB,aAAa,CAAC,YAAa,QAAQ,SAAS,KAAK,IAAI,QAAQ,OAAO,CAAC,OAAO,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,KAAK,CAAE;AAElH,QAAM,sBAAsB,YAAY,SAAS,KAAK,YAAY,MAAM,CAAC,QAAQ,YAAY,IAAI,IAAI,EAAE,CAAC;AAExG,QAAM,mBAAmB,MACvB,aAAa,CAAC,YAAY;AACxB,UAAM,aAAa,YAAY,IAAI,CAAC,QAAQ,IAAI,EAAE;AAClD,QAAI,oBAAqB,QAAO,QAAQ,OAAO,CAAC,OAAO,CAAC,WAAW,SAAS,EAAE,CAAC;AAE/E,WAAO,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC;AAAA,EACxD,CAAC;AAEH,QAAM,cAAc,MAAM;AACxB,QAAI,CAAC,kBAAkB,WAAW,WAAW,EAAG;AAEhD,YAAQ,WAAW,IAAI,CAAC,SAAS,EAAE,KAAK,UAAU,eAAe,IAAI,MAAA,EAAQ,CAAC;AAC9E,YAAA;AAAA,EACF;AAEA,QAAM,oBAAoB,MAAM;AAC9B,QAAI,CAAC,SAAU,QAAO;AAEtB,QAAI,SAAS,cAAc;AACzB,aAAO,SAAS,aAAa,EAAE,OAAO,UAAU,UAAU,OAAO,EAAE,6BAA6B,GAAG;AAAA,IACrG;AAEA,QAAI,SAAS,SAAS,eAAe;AACnC,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO,EAAE,6BAA6B;AAAA,UACtC,SAAS,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,UAAU,CAAA;AAAA,UAC9D,eAAe;AAAA,UACf,cAAc,CAAC,aAAa,SAAS,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,CAAC;AAAA,UACpF,UAAQ;AAAA,UACR,YAAU;AAAA,UACV,WAAS;AAAA,QAAA;AAAA,MAAA;AAAA,IAGf;AAEA,QAAI,SAAS,SAAS,UAAU;AAC9B,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO,EAAE,6BAA6B;AAAA,UACtC,SAAS,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,UAAU,CAAA;AAAA,UAC9D,eAAe;AAAA,UACf,cAAc,CAAC,aAAa,SAAS,OAAO,aAAa,WAAW,WAAW,EAAE;AAAA,UACjF,UAAU;AAAA,UACV,YAAU;AAAA,UACV,WAAS;AAAA,QAAA;AAAA,MAAA;AAAA,IAGf;AAEA,QAAI,SAAS,SAAS,QAAQ;AAC5B,YAAM,SAAS,cAAc,IAAI,KAAK,WAAW,IAAI;AACrD,YAAM,aAAa,UAAU,CAAC,OAAO,MAAM,OAAO,QAAA,CAAS,IAAI,SAAS;AAExE,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,OAAO,EAAE,6BAA6B;AAAA,UACtC;AAAA,UACA,UAAU,CAAC,cAAc,SAAS,aAAa,EAAE;AAAA,QAAA;AAAA,MAAA;AAAA,IAGvD;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,OAAO,EAAE,6BAA6B;AAAA,QACtC,OAAO;AAAA,QACP,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,QAChD,WAAW,SAAS,SAAS,WAAW,YAAY;AAAA,QACpD,OAAO,CAAC,CAAC;AAAA,QACT,cAAc,mBAAmB;AAAA,MAAA;AAAA,IAAA;AAAA,EAGvC;AAEA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,MAAK;AAAA,MACL,OAAO,EAAE,0BAA0B;AAAA,MACnC,UAAU,EAAE,6BAA6B;AAAA,MACzC,UAAU;AAAA,MACV,WAAW;AAAA,MACX,cACE,WAAW,WAAW,IAClB,EAAE,iCAAiC,IACnC,EAAE,8BAA8B,EAAE,OAAO,WAAW,QAAQ;AAAA,MAElE,aAAa,EAAE,gBAAgB;AAAA,MAC/B,gBAAgB,CAAC,kBAAkB,WAAW,WAAW,KAAK,gBAAgB,CAAC,CAAC;AAAA,MAEhF,UAAA,qBAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO,EAAE,0BAA0B;AAAA,YACnC,SAAS,gBAAgB,IAAI,CAAC,YAAY,EAAE,OAAO,OAAO,IAAI,OAAO,OAAO,OAAA,EAAS;AAAA,YACrF,eAAe;AAAA,YACf,cAAc,CAAC,aAAa;AAC1B,0BAAY,OAAO,aAAa,WAAW,WAAW,EAAE;AACxD,uBAAS,EAAE;AAAA,YACb;AAAA,YACA,UAAU;AAAA,YACV,YAAU;AAAA,UAAA;AAAA,QAAA;AAAA,QAGX,kBAAA;AAAA,QAED,qBAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,sEACb,UAAA,EAAE,+BAA+B,GACpC;AAAA,UACA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,SAAS;AAAA,cACT,OAAO;AAAA,cACP,UAAU;AAAA,cACV,YAAU;AAAA,cACV,MAAK;AAAA,YAAA;AAAA,UAAA;AAAA,QACP,GACF;AAAA,QAEC,UAAU,YACT,qBAAC,OAAA,EAAI,WAAU,qFACb,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAO,EAAE,+BAA+B;AAAA,cACxC,cAAc;AAAA,cACd,iBAAiB;AAAA,cACjB,iBAAe;AAAA,cACf,gBAAgB;AAAA,cAChB,eAAe,MAAM,cAAc,EAAE;AAAA,YAAA;AAAA,UAAA;AAAA,UAGtC,YAAY,WAAW,IACtB,oBAAC,eAAY,MAAK,MAAK,SAAQ,UAAS,SAAS,EAAE,gCAAgC,EAAA,CAAG,IAEtF,qBAAA,UAAA,EACE,UAAA;AAAA,YAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,SAAS;AAAA,gBACT,UAAU;AAAA,gBACV,OAAO,EAAE,gCAAgC,EAAE,OAAO,YAAY,QAAQ;AAAA,gBACtE,MAAK;AAAA,cAAA;AAAA,YAAA;AAAA,gCAEN,OAAA,EAAI,WAAU,0CACZ,UAAA,YAAY,IAAI,CAAC,QAChB;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,SAAS,YAAY,IAAI,IAAI,EAAE;AAAA,gBAC/B,UAAU,MAAM,aAAa,IAAI,EAAE;AAAA,gBACnC,OAAO,aAAa,GAAG;AAAA,gBACvB,MAAK;AAAA,cAAA;AAAA,cAJA,IAAI;AAAA,YAAA,CAMZ,EAAA,CACH;AAAA,UAAA,EAAA,CACF;AAAA,QAAA,EAAA,CAEJ;AAAA,MAAA,EAAA,CAEJ;AAAA,IAAA;AAAA,EAAA;AAGN;"}
|
|
1
|
+
{"version":3,"file":"DataTableBulkEditDialog.js","sources":["../../../../src/components/UI/DataTable/DataTableBulkEditDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport ControlledDialog from '@/components/Form/ControlledDialog';\nimport { ControlledSelect } from '@/components/Form/ControlledSelect';\nimport ControlledViewToggle, { type IControlledViewToggleOption } from '@/components/Form/ControlledViewToggle';\nimport ControlledTooltip from '@/components/UI/ControlledTooltip';\nimport NoDataFound from '@/components/UI/NoDataFound';\n\nimport { areBulkValuesEqual, isBulkValueEmpty } from './bulkEditValues';\nimport DataTableBulkEditRowsPicker from './DataTableBulkEditRowsPicker';\nimport DataTableBulkEditValueEditor from './DataTableBulkEditValueEditor';\nimport type { IDataTableColumnConfig, IDataTableFillChange, IDataTableRow } from './types';\n\ntype TBulkEditMode = 'same' | 'perItem';\ntype TBulkEditScope = 'loaded' | 'picked';\n\ninterface IDataTableBulkEditDialogProps<T extends IDataTableRow> {\n open: boolean;\n onClose: () => void;\n /** Todas as colunas da tabela: só as editáveis viram opção de campo. */\n columns: Array<IDataTableColumnConfig<T>>;\n /** As linhas carregadas: a página atual na lista, o acumulado do scroll nos cards. */\n rows: Array<T>;\n /** Rótulo de cada linha na lista de escolha. Sem ele, a primeira coluna de texto responde. */\n getRowLabel?: (row: T) => string;\n /** Ids já escolhidos fora do diálogo: abre no modo \"escolher\" com eles marcados. */\n initialTargetIds?: ReadonlyArray<string>;\n onApply: (changes: Array<IDataTableFillChange<T>>) => void;\n}\n\n/**\n * Sem `getRowLabel`, o nome sai da primeira coluna que sabe devolver um valor\n * legível. Colunas que só desenham (`render`, sem `accessor`) não respondem, e é\n * por isso que uma tabela cuja identidade mora numa coluna `custom` precisa\n * declarar `getRowLabel`: aqui só sobraria o id.\n */\nconst resolveDefaultRowLabel = <T extends IDataTableRow>(row: T, columns: Array<IDataTableColumnConfig<T>>): string => {\n const label = (columns ?? [])\n .filter((column) => column.type !== 'actions' && !!column.accessor)\n .map((column) => column.accessor?.(row))\n .find((value) => typeof value === 'string' || typeof value === 'number');\n\n return label == null || label === '' ? row.id : String(label);\n};\n\n/**\n * Bulk edit in two modes. \"Same value\" applies one value to one field across the\n * chosen scope; \"Values per item\" opens an item x fields matrix prefilled with\n * the current values, and only the changed cells are written.\n *\n * O diálogo não persiste nada. Ele devolve as células escritas por `onApply`, que\n * o host entrega ao `onFillCells` da fila de commit: um render, um flush e uma\n * requisição em lote, exatamente como o preenchimento por arraste.\n */\nconst DataTableBulkEditDialog = <T extends IDataTableRow>({\n open,\n onClose,\n columns,\n rows,\n getRowLabel,\n initialTargetIds,\n onApply,\n}: IDataTableBulkEditDialogProps<T>) => {\n const { t } = useTranslation('ui');\n\n const editableColumns = useMemo(() => (columns ?? []).filter((column) => !!column.editable), [columns]);\n\n const [mode, setMode] = useState<TBulkEditMode>('same');\n const [columnId, setColumnId] = useState('');\n const [value, setValue] = useState<unknown>('');\n const [perItemColumnIds, setPerItemColumnIds] = useState<Array<string>>([]);\n const [drafts, setDrafts] = useState<Record<string, Record<string, unknown>>>({});\n const [scope, setScope] = useState<TBulkEditScope>('loaded');\n const [pickedIds, setPickedIds] = useState<Array<string>>([]);\n\n // Cada abertura recomeça do zero: um valor ou rascunho sobrando da vez anterior\n // seria aplicado em massa por um clique só. O reset acontece na transição de\n // abrir, nunca por uma nova referência de `columns` ou de `initialTargetIds`,\n // que a tela recria ao marcar uma linha atrás do diálogo.\n const wasOpenRef = useRef(false);\n useEffect(() => {\n if (open === wasOpenRef.current) return;\n wasOpenRef.current = open;\n if (!open) return;\n\n const preselected = Array.from(initialTargetIds ?? []);\n setMode('same');\n setColumnId(editableColumns[0]?.id ?? '');\n setValue('');\n setPerItemColumnIds([]);\n setDrafts({});\n setScope(preselected.length > 0 ? 'picked' : 'loaded');\n setPickedIds(preselected);\n }, [open, editableColumns, initialTargetIds]);\n\n const selectedColumn = useMemo(\n () => editableColumns.find((column) => column.id === columnId),\n [editableColumns, columnId],\n );\n const editable = selectedColumn?.editable;\n\n const selectedPerItemColumns = useMemo(\n () =>\n perItemColumnIds\n .map((id) => editableColumns.find((column) => column.id === id))\n .filter((column): column is IDataTableColumnConfig<T> => !!column),\n [perItemColumnIds, editableColumns],\n );\n\n const resolveLabel = useMemo(\n () => getRowLabel ?? ((row: T) => resolveDefaultRowLabel(row, columns ?? [])),\n [getRowLabel, columns],\n );\n\n // Uma linha que os editores recusam (variante, registro bloqueado) não entra\n // no alcance nem aparece nas listas: prometer a alteração e descartá-la é pior.\n const eligibleRows = useMemo(() => {\n if (mode === 'same') return (rows ?? []).filter((row) => !(editable?.disabled?.(row) ?? false));\n\n const referenceColumns = selectedPerItemColumns.length > 0 ? selectedPerItemColumns : editableColumns;\n return (rows ?? []).filter((row) =>\n referenceColumns.some((column) => !(column.editable?.disabled?.(row) ?? false)),\n );\n }, [mode, rows, editable, selectedPerItemColumns, editableColumns]);\n\n const pickedIdSet = useMemo(() => new Set(pickedIds), [pickedIds]);\n\n const targetRows = useMemo(\n () => (scope === 'loaded' ? eligibleRows : eligibleRows.filter((row) => pickedIdSet.has(row.id))),\n [scope, eligibleRows, pickedIdSet],\n );\n\n // Vazio não aplica no modo \"mesmo valor\": o diálogo abre sem valor, e um Enter\n // distraído limparia o campo em todo o alcance. Na matriz por item, limpar uma\n // célula é uma alteração explícita e legítima.\n const isValueEmpty = isBulkValueEmpty(value);\n\n const sameValueValidationError = useMemo(() => {\n if (mode !== 'same' || !editable?.validate) return null;\n\n const firstInvalid = targetRows.map((row) => editable.validate?.(value, row) ?? null).find((message) => !!message);\n\n return firstInvalid ?? null;\n }, [mode, editable, targetRows, value]);\n\n // Only what actually changed becomes a write: a cell with no draft, or edited\n // back to the current value, stays out.\n const perItemChanges = useMemo(() => {\n if (mode !== 'perItem') return [];\n\n const changes: Array<IDataTableFillChange<T>> = [];\n targetRows.forEach((row) => {\n const rowDraft = drafts[row.id];\n if (!rowDraft) return;\n\n selectedPerItemColumns.forEach((column) => {\n if (!Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return;\n if (column.editable?.disabled?.(row) ?? false) return;\n\n const draftValue = rowDraft[column.id];\n if (areBulkValuesEqual(draftValue, column.editable?.getValue(row) ?? null)) return;\n\n changes.push({ row, columnId: column.id, value: draftValue });\n });\n });\n\n return changes;\n }, [mode, targetRows, drafts, selectedPerItemColumns]);\n\n const perItemValidationError = useMemo(() => {\n const firstInvalid = perItemChanges\n .map((change) => {\n const column = selectedPerItemColumns.find((entry) => entry.id === change.columnId);\n return column?.editable?.validate?.(change.value, change.row) ?? null;\n })\n .find((message) => !!message);\n\n return firstInvalid ?? null;\n }, [perItemChanges, selectedPerItemColumns]);\n\n const setDraftValue = (rowId: string, targetColumnId: string, nextValue: unknown) =>\n setDrafts((current) => ({\n ...current,\n [rowId]: { ...current[rowId], [targetColumnId]: nextValue },\n }));\n\n const resolveCellValue = (row: T, column: IDataTableColumnConfig<T>): unknown => {\n const rowDraft = drafts[row.id];\n if (rowDraft && Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return rowDraft[column.id];\n\n return column.editable?.getValue(row) ?? null;\n };\n\n const isCellDirty = (row: T, column: IDataTableColumnConfig<T>): boolean => {\n const rowDraft = drafts[row.id];\n if (!rowDraft || !Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return false;\n\n return !areBulkValuesEqual(rowDraft[column.id], column.editable?.getValue(row) ?? null);\n };\n\n const modeOptions: Array<IControlledViewToggleOption<TBulkEditMode>> = [\n { value: 'same', label: t('dataTable.bulkEdit.modeSame'), icon: 'stacks' },\n { value: 'perItem', label: t('dataTable.bulkEdit.modePerItem'), icon: 'edit_note' },\n ];\n\n const scopeOptions: Array<IControlledViewToggleOption<TBulkEditScope>> = [\n { value: 'loaded', label: t('dataTable.bulkEdit.scopeLoaded'), icon: 'select_all', count: eligibleRows.length },\n { value: 'picked', label: t('dataTable.bulkEdit.scopePicked'), icon: 'checklist', count: pickedIdSet.size },\n ];\n\n const handleApply = () => {\n if (mode === 'perItem') {\n if (perItemChanges.length === 0) return;\n\n onApply(perItemChanges);\n onClose();\n return;\n }\n\n if (!selectedColumn || targetRows.length === 0) return;\n\n onApply(targetRows.map((row) => ({ row, columnId: selectedColumn.id, value })));\n onClose();\n };\n\n const confirmLabel =\n mode === 'perItem'\n ? perItemChanges.length === 0\n ? t('dataTable.bulkEdit.confirmNoChanges')\n : t('dataTable.bulkEdit.confirmChanges', { count: perItemChanges.length })\n : targetRows.length === 0\n ? t('dataTable.bulkEdit.confirmEmpty')\n : t('dataTable.bulkEdit.confirm', { count: targetRows.length });\n\n const disableConfirm =\n mode === 'perItem'\n ? perItemChanges.length === 0 || !!perItemValidationError\n : !selectedColumn || targetRows.length === 0 || isValueEmpty || !!sameValueValidationError;\n\n const renderPerItemMatrix = () => {\n if (selectedPerItemColumns.length === 0) {\n return <NoDataFound size=\"sm\" variant=\"waiting\" message={t('dataTable.bulkEdit.pickFieldsHint')} />;\n }\n\n if (targetRows.length === 0) {\n return <NoDataFound size=\"sm\" variant=\"waiting\" message={t('dataTable.bulkEdit.noTargets')} />;\n }\n\n return (\n <div className=\"flex min-h-0 flex-col gap-2 rounded-xl border border-border bg-surface-sunken p-3\">\n <span className=\"text-xs text-foreground-muted\">{t('dataTable.bulkEdit.perItemHint')}</span>\n <div className=\"overflow-x-auto\">\n <div className=\"flex min-w-max flex-col gap-2\">\n <div className=\"flex items-center gap-3\">\n <span className=\"w-52 shrink-0 text-xs font-medium uppercase tracking-wide text-foreground-subtle\">\n {t('dataTable.bulkEdit.itemColumn')}\n </span>\n {selectedPerItemColumns.map((column) => (\n <span\n key={column.id}\n className=\"w-64 shrink-0 truncate text-xs font-medium uppercase tracking-wide text-foreground-subtle\"\n >\n {column.header}\n </span>\n ))}\n </div>\n\n <div className=\"flex max-h-96 flex-col gap-2 overflow-y-auto pt-1\">\n {targetRows.map((row) => (\n <div key={row.id} className=\"flex items-center gap-3\">\n <ControlledTooltip content={resolveLabel(row)} className=\"z-[9999]\" placement=\"top-start\">\n <span className=\"w-52 shrink-0 truncate text-sm text-foreground\">{resolveLabel(row)}</span>\n </ControlledTooltip>\n {selectedPerItemColumns.map((column) => {\n const cellEditable = column.editable;\n if (!cellEditable) return <span key={column.id} className=\"w-64 shrink-0\" />;\n\n const isDisabled = cellEditable.disabled?.(row) ?? false;\n const cellValue = resolveCellValue(row, column);\n const dirty = isCellDirty(row, column);\n const cellError = dirty ? (cellEditable.validate?.(cellValue, row) ?? null) : null;\n\n return (\n <div key={column.id} className=\"relative w-64 shrink-0\">\n {dirty && (\n <span className=\"absolute -right-1 -top-1 z-10 size-2 rounded-full bg-brand\" aria-hidden />\n )}\n <DataTableBulkEditValueEditor\n editable={cellEditable}\n value={cellValue}\n onChange={(nextValue) => setDraftValue(row.id, column.id, nextValue)}\n disabled={isDisabled}\n errorMessage={cellError}\n />\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n );\n };\n\n return (\n <ControlledDialog\n open={open}\n size={mode === 'perItem' ? 'xl' : 'md'}\n title={t('dataTable.bulkEdit.title')}\n subtitle={mode === 'perItem' ? t('dataTable.bulkEdit.subtitlePerItem') : t('dataTable.bulkEdit.subtitle')}\n onCancel={onClose}\n onConfirm={handleApply}\n confirmLabel={confirmLabel}\n cancelLabel={t('actions.cancel')}\n disableConfirm={disableConfirm}\n >\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-foreground-subtle\">\n {t('dataTable.bulkEdit.modeLabel')}\n </span>\n <ControlledViewToggle<TBulkEditMode>\n options={modeOptions}\n value={mode}\n onChange={setMode}\n showLabels\n size=\"sm\"\n />\n </div>\n\n {mode === 'same' ? (\n <>\n <ControlledSelect\n className=\"w-full\"\n label={t('dataTable.bulkEdit.field')}\n options={editableColumns.map((column) => ({ value: column.id, label: column.header }))}\n selectedValue={columnId}\n onChangeHook={(selected) => {\n setColumnId(typeof selected === 'string' ? selected : '');\n setValue('');\n }}\n multiple={false}\n searchable\n />\n\n {editable && (\n <DataTableBulkEditValueEditor\n editable={editable}\n value={value}\n onChange={setValue}\n label={t('dataTable.bulkEdit.newValue')}\n errorMessage={sameValueValidationError}\n />\n )}\n </>\n ) : (\n <ControlledSelect\n className=\"w-full\"\n label={t('dataTable.bulkEdit.fields')}\n options={editableColumns.map((column) => ({ value: column.id, label: column.header }))}\n selectedValue={perItemColumnIds}\n onChangeHook={(selected) =>\n setPerItemColumnIds(Array.isArray(selected) ? selected : [selected].filter(Boolean))\n }\n multiple\n searchable\n clearable\n />\n )}\n\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-foreground-subtle\">\n {t('dataTable.bulkEdit.scopeLabel')}\n </span>\n <ControlledViewToggle<TBulkEditScope>\n options={scopeOptions}\n value={scope}\n onChange={setScope}\n showLabels\n size=\"sm\"\n />\n </div>\n\n {scope === 'picked' && (\n <DataTableBulkEditRowsPicker\n rows={eligibleRows}\n resolveLabel={resolveLabel}\n pickedIds={pickedIds}\n onChange={setPickedIds}\n />\n )}\n\n {mode === 'perItem' && renderPerItemMatrix()}\n </div>\n </ControlledDialog>\n );\n};\n\nexport default DataTableBulkEditDialog;\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IDataTableRow } from './types';
|
|
2
|
+
interface IDataTableBulkEditRowsPickerProps<T extends IDataTableRow> {
|
|
3
|
+
rows: Array<T>;
|
|
4
|
+
resolveLabel: (row: T) => string;
|
|
5
|
+
pickedIds: Array<string>;
|
|
6
|
+
onChange: (nextPickedIds: Array<string>) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The scope picking list: search, toggle the visible ones, toggle one by one.
|
|
10
|
+
* It is the same in both dialog modes, so it lives outside of it.
|
|
11
|
+
*/
|
|
12
|
+
declare const DataTableBulkEditRowsPicker: <T extends IDataTableRow>({ rows, resolveLabel, pickedIds, onChange, }: IDataTableBulkEditRowsPickerProps<T>) => import("react").JSX.Element;
|
|
13
|
+
export default DataTableBulkEditRowsPicker;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useMemo } from "react";
|
|
3
|
+
import { useTranslation } from "react-i18next";
|
|
4
|
+
import ControlledCheckbox from "../../Form/ControlledCheckbox.js";
|
|
5
|
+
import "classnames";
|
|
6
|
+
import "../../Form/ControlledButton.js";
|
|
7
|
+
import "tailwind-merge";
|
|
8
|
+
import "../ControlledMenu.js";
|
|
9
|
+
import "motion/react";
|
|
10
|
+
import "react-dom";
|
|
11
|
+
import "@floating-ui/dom";
|
|
12
|
+
import "../../../motion/motionPresets.js";
|
|
13
|
+
import "../../../motion/motionTokens.js";
|
|
14
|
+
import { normalizeText } from "../../../helpers/stringHelpers.js";
|
|
15
|
+
import "../MaterialSymbol/index.js";
|
|
16
|
+
import "date-fns";
|
|
17
|
+
import "date-fns/locale";
|
|
18
|
+
import "react-day-picker";
|
|
19
|
+
import "../ControlledPopover.js";
|
|
20
|
+
import "react-day-picker/style.css";
|
|
21
|
+
import "../../DateTime/DateRangePicker.js";
|
|
22
|
+
import "react-infinite-scroll-component";
|
|
23
|
+
import "react-window";
|
|
24
|
+
import NoDataFound from "../NoDataFound/index.js";
|
|
25
|
+
/* empty css */
|
|
26
|
+
import "../../Search/filters/FiltersButton.js";
|
|
27
|
+
import "../../../helpers/utilityHelpers.js";
|
|
28
|
+
import "../../Form/ControlledDialog.js";
|
|
29
|
+
import "../../Navigation/UiNavigationContext.js";
|
|
30
|
+
import UnifiedSearchBar from "../../Search/UnifiedSearchBar.js";
|
|
31
|
+
const DataTableBulkEditRowsPicker = ({
|
|
32
|
+
rows,
|
|
33
|
+
resolveLabel,
|
|
34
|
+
pickedIds,
|
|
35
|
+
onChange
|
|
36
|
+
}) => {
|
|
37
|
+
const { t } = useTranslation("ui");
|
|
38
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
39
|
+
const pickedIdSet = useMemo(() => new Set(pickedIds), [pickedIds]);
|
|
40
|
+
const visibleRows = useMemo(() => {
|
|
41
|
+
const term = normalizeText(searchTerm.trim());
|
|
42
|
+
if (term === "") return rows;
|
|
43
|
+
return rows.filter((row) => normalizeText(resolveLabel(row)).includes(term));
|
|
44
|
+
}, [rows, searchTerm, resolveLabel]);
|
|
45
|
+
const areAllVisiblePicked = visibleRows.length > 0 && visibleRows.every((row) => pickedIdSet.has(row.id));
|
|
46
|
+
const toggleAllVisible = () => {
|
|
47
|
+
const visibleIds = visibleRows.map((row) => row.id);
|
|
48
|
+
if (areAllVisiblePicked) {
|
|
49
|
+
onChange(pickedIds.filter((id) => !visibleIds.includes(id)));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
onChange(Array.from(/* @__PURE__ */ new Set([...pickedIds, ...visibleIds])));
|
|
53
|
+
};
|
|
54
|
+
const togglePicked = (rowId) => onChange(pickedIds.includes(rowId) ? pickedIds.filter((id) => id !== rowId) : [...pickedIds, rowId]);
|
|
55
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-col gap-2 rounded-xl border border-border bg-surface-sunken p-3", children: [
|
|
56
|
+
/* @__PURE__ */ jsx(
|
|
57
|
+
UnifiedSearchBar,
|
|
58
|
+
{
|
|
59
|
+
mode: "callback",
|
|
60
|
+
label: t("dataTable.bulkEdit.searchRows"),
|
|
61
|
+
debounceTime: 200,
|
|
62
|
+
minSearchLength: 1,
|
|
63
|
+
showClearButton: true,
|
|
64
|
+
filterCallback: setSearchTerm,
|
|
65
|
+
onClearFilter: () => setSearchTerm("")
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
visibleRows.length === 0 ? /* @__PURE__ */ jsx(NoDataFound, { size: "sm", variant: "search", message: t("dataTable.bulkEdit.noRowsFound") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
69
|
+
/* @__PURE__ */ jsx(
|
|
70
|
+
ControlledCheckbox,
|
|
71
|
+
{
|
|
72
|
+
checked: areAllVisiblePicked,
|
|
73
|
+
onChange: toggleAllVisible,
|
|
74
|
+
label: t("dataTable.bulkEdit.selectAll", { count: visibleRows.length }),
|
|
75
|
+
size: "sm"
|
|
76
|
+
}
|
|
77
|
+
),
|
|
78
|
+
/* @__PURE__ */ jsx("div", { className: "flex max-h-64 flex-col overflow-y-auto", children: visibleRows.map((row) => /* @__PURE__ */ jsx(
|
|
79
|
+
ControlledCheckbox,
|
|
80
|
+
{
|
|
81
|
+
checked: pickedIdSet.has(row.id),
|
|
82
|
+
onChange: () => togglePicked(row.id),
|
|
83
|
+
label: resolveLabel(row),
|
|
84
|
+
size: "sm"
|
|
85
|
+
},
|
|
86
|
+
row.id
|
|
87
|
+
)) })
|
|
88
|
+
] })
|
|
89
|
+
] });
|
|
90
|
+
};
|
|
91
|
+
export {
|
|
92
|
+
DataTableBulkEditRowsPicker as default
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=DataTableBulkEditRowsPicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataTableBulkEditRowsPicker.js","sources":["../../../../src/components/UI/DataTable/DataTableBulkEditRowsPicker.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport ControlledCheckbox from '@/components/Form/ControlledCheckbox';\nimport { UnifiedSearchBar } from '@/components/Search';\nimport NoDataFound from '@/components/UI/NoDataFound';\nimport { normalizeText } from '@/helpers/stringHelpers';\n\nimport type { IDataTableRow } from './types';\n\ninterface IDataTableBulkEditRowsPickerProps<T extends IDataTableRow> {\n rows: Array<T>;\n resolveLabel: (row: T) => string;\n pickedIds: Array<string>;\n onChange: (nextPickedIds: Array<string>) => void;\n}\n\n/**\n * The scope picking list: search, toggle the visible ones, toggle one by one.\n * It is the same in both dialog modes, so it lives outside of it.\n */\nconst DataTableBulkEditRowsPicker = <T extends IDataTableRow>({\n rows,\n resolveLabel,\n pickedIds,\n onChange,\n}: IDataTableBulkEditRowsPickerProps<T>) => {\n const { t } = useTranslation('ui');\n const [searchTerm, setSearchTerm] = useState('');\n\n const pickedIdSet = useMemo(() => new Set(pickedIds), [pickedIds]);\n\n const visibleRows = useMemo(() => {\n const term = normalizeText(searchTerm.trim());\n if (term === '') return rows;\n\n return rows.filter((row) => normalizeText(resolveLabel(row)).includes(term));\n }, [rows, searchTerm, resolveLabel]);\n\n const areAllVisiblePicked = visibleRows.length > 0 && visibleRows.every((row) => pickedIdSet.has(row.id));\n\n const toggleAllVisible = () => {\n const visibleIds = visibleRows.map((row) => row.id);\n if (areAllVisiblePicked) {\n onChange(pickedIds.filter((id) => !visibleIds.includes(id)));\n return;\n }\n\n onChange(Array.from(new Set([...pickedIds, ...visibleIds])));\n };\n\n const togglePicked = (rowId: string) =>\n onChange(pickedIds.includes(rowId) ? pickedIds.filter((id) => id !== rowId) : [...pickedIds, rowId]);\n\n return (\n <div className=\"flex min-h-0 flex-col gap-2 rounded-xl border border-border bg-surface-sunken p-3\">\n <UnifiedSearchBar\n mode=\"callback\"\n label={t('dataTable.bulkEdit.searchRows')}\n debounceTime={200}\n minSearchLength={1}\n showClearButton\n filterCallback={setSearchTerm}\n onClearFilter={() => setSearchTerm('')}\n />\n\n {visibleRows.length === 0 ? (\n <NoDataFound size=\"sm\" variant=\"search\" message={t('dataTable.bulkEdit.noRowsFound')} />\n ) : (\n <>\n <ControlledCheckbox\n checked={areAllVisiblePicked}\n onChange={toggleAllVisible}\n label={t('dataTable.bulkEdit.selectAll', { count: visibleRows.length })}\n size=\"sm\"\n />\n <div className=\"flex max-h-64 flex-col overflow-y-auto\">\n {visibleRows.map((row) => (\n <ControlledCheckbox\n key={row.id}\n checked={pickedIdSet.has(row.id)}\n onChange={() => togglePicked(row.id)}\n label={resolveLabel(row)}\n size=\"sm\"\n />\n ))}\n </div>\n </>\n )}\n </div>\n );\n};\n\nexport default DataTableBulkEditRowsPicker;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,MAAM,8BAA8B,CAA0B;AAAA,EAC5D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA4C;AAC1C,QAAM,EAAE,EAAA,IAAM,eAAe,IAAI;AACjC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,EAAE;AAE/C,QAAM,cAAc,QAAQ,MAAM,IAAI,IAAI,SAAS,GAAG,CAAC,SAAS,CAAC;AAEjE,QAAM,cAAc,QAAQ,MAAM;AAChC,UAAM,OAAO,cAAc,WAAW,KAAA,CAAM;AAC5C,QAAI,SAAS,GAAI,QAAO;AAExB,WAAO,KAAK,OAAO,CAAC,QAAQ,cAAc,aAAa,GAAG,CAAC,EAAE,SAAS,IAAI,CAAC;AAAA,EAC7E,GAAG,CAAC,MAAM,YAAY,YAAY,CAAC;AAEnC,QAAM,sBAAsB,YAAY,SAAS,KAAK,YAAY,MAAM,CAAC,QAAQ,YAAY,IAAI,IAAI,EAAE,CAAC;AAExG,QAAM,mBAAmB,MAAM;AAC7B,UAAM,aAAa,YAAY,IAAI,CAAC,QAAQ,IAAI,EAAE;AAClD,QAAI,qBAAqB;AACvB,eAAS,UAAU,OAAO,CAAC,OAAO,CAAC,WAAW,SAAS,EAAE,CAAC,CAAC;AAC3D;AAAA,IACF;AAEA,aAAS,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC;AAAA,EAC7D;AAEA,QAAM,eAAe,CAAC,UACpB,SAAS,UAAU,SAAS,KAAK,IAAI,UAAU,OAAO,CAAC,OAAO,OAAO,KAAK,IAAI,CAAC,GAAG,WAAW,KAAK,CAAC;AAErG,SACE,qBAAC,OAAA,EAAI,WAAU,qFACb,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,OAAO,EAAE,+BAA+B;AAAA,QACxC,cAAc;AAAA,QACd,iBAAiB;AAAA,QACjB,iBAAe;AAAA,QACf,gBAAgB;AAAA,QAChB,eAAe,MAAM,cAAc,EAAE;AAAA,MAAA;AAAA,IAAA;AAAA,IAGtC,YAAY,WAAW,IACtB,oBAAC,eAAY,MAAK,MAAK,SAAQ,UAAS,SAAS,EAAE,gCAAgC,EAAA,CAAG,IAEtF,qBAAA,UAAA,EACE,UAAA;AAAA,MAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,SAAS;AAAA,UACT,UAAU;AAAA,UACV,OAAO,EAAE,gCAAgC,EAAE,OAAO,YAAY,QAAQ;AAAA,UACtE,MAAK;AAAA,QAAA;AAAA,MAAA;AAAA,0BAEN,OAAA,EAAI,WAAU,0CACZ,UAAA,YAAY,IAAI,CAAC,QAChB;AAAA,QAAC;AAAA,QAAA;AAAA,UAEC,SAAS,YAAY,IAAI,IAAI,EAAE;AAAA,UAC/B,UAAU,MAAM,aAAa,IAAI,EAAE;AAAA,UACnC,OAAO,aAAa,GAAG;AAAA,UACvB,MAAK;AAAA,QAAA;AAAA,QAJA,IAAI;AAAA,MAAA,CAMZ,EAAA,CACH;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ;"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IDataTableEditableConfig, IDataTableRow } from './types';
|
|
2
|
+
interface IDataTableBulkEditValueEditorProps<T extends IDataTableRow> {
|
|
3
|
+
editable: IDataTableEditableConfig<T>;
|
|
4
|
+
value: unknown;
|
|
5
|
+
onChange: (value: unknown) => void;
|
|
6
|
+
label?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
errorMessage?: string | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A value editor derived from the column's `editable` descriptor, the same one
|
|
12
|
+
* the spreadsheet cell opens. Used by the "same value" mode (one editor for the
|
|
13
|
+
* whole scope) and by the per-item matrix (one editor per cell).
|
|
14
|
+
*/
|
|
15
|
+
declare const DataTableBulkEditValueEditor: <T extends IDataTableRow>({ editable, value, onChange, label, disabled, errorMessage, }: IDataTableBulkEditValueEditorProps<T>) => import("react").JSX.Element;
|
|
16
|
+
export default DataTableBulkEditValueEditor;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import DatePicker from "../../DateTime/DatePicker.js";
|
|
3
|
+
import ControlledInput from "../../Form/ControlledInput.js";
|
|
4
|
+
import { ControlledSelect } from "../../Form/ControlledSelect.js";
|
|
5
|
+
const DataTableBulkEditValueEditor = ({
|
|
6
|
+
editable,
|
|
7
|
+
value,
|
|
8
|
+
onChange,
|
|
9
|
+
label,
|
|
10
|
+
disabled,
|
|
11
|
+
errorMessage
|
|
12
|
+
}) => {
|
|
13
|
+
if (editable.renderEditor) {
|
|
14
|
+
return /* @__PURE__ */ jsx(Fragment, { children: editable.renderEditor({ value, onChange, disabled, label }) });
|
|
15
|
+
}
|
|
16
|
+
const arrayValue = Array.isArray(value) ? value.map(String) : [];
|
|
17
|
+
const stringValue = value == null || Array.isArray(value) ? "" : String(value);
|
|
18
|
+
if (editable.type === "multiselect") {
|
|
19
|
+
return /* @__PURE__ */ jsx(
|
|
20
|
+
ControlledSelect,
|
|
21
|
+
{
|
|
22
|
+
className: "w-full",
|
|
23
|
+
label,
|
|
24
|
+
options: Array.isArray(editable.options) ? editable.options : [],
|
|
25
|
+
selectedValue: arrayValue,
|
|
26
|
+
onChangeHook: (selected) => onChange(Array.isArray(selected) ? selected : [selected]),
|
|
27
|
+
multiple: true,
|
|
28
|
+
searchable: true,
|
|
29
|
+
clearable: true,
|
|
30
|
+
disabled
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (editable.type === "select") {
|
|
35
|
+
return /* @__PURE__ */ jsx(
|
|
36
|
+
ControlledSelect,
|
|
37
|
+
{
|
|
38
|
+
className: "w-full",
|
|
39
|
+
label,
|
|
40
|
+
options: Array.isArray(editable.options) ? editable.options : [],
|
|
41
|
+
selectedValue: stringValue,
|
|
42
|
+
onChangeHook: (selected) => onChange(typeof selected === "string" ? selected : ""),
|
|
43
|
+
multiple: false,
|
|
44
|
+
searchable: true,
|
|
45
|
+
clearable: true,
|
|
46
|
+
disabled
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (editable.type === "date") {
|
|
51
|
+
const parsed = stringValue ? new Date(stringValue) : void 0;
|
|
52
|
+
const startValue = parsed && !Number.isNaN(parsed.getTime()) ? parsed : void 0;
|
|
53
|
+
return /* @__PURE__ */ jsx(
|
|
54
|
+
DatePicker,
|
|
55
|
+
{
|
|
56
|
+
label,
|
|
57
|
+
startValue,
|
|
58
|
+
onUpdate: (nextValue) => onChange(nextValue ?? ""),
|
|
59
|
+
disabled
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return /* @__PURE__ */ jsx(
|
|
64
|
+
ControlledInput,
|
|
65
|
+
{
|
|
66
|
+
label,
|
|
67
|
+
value: stringValue,
|
|
68
|
+
onChange: (event) => onChange(event.target.value),
|
|
69
|
+
inputMode: editable.type === "number" ? "decimal" : void 0,
|
|
70
|
+
disabled,
|
|
71
|
+
error: !!errorMessage,
|
|
72
|
+
errorMessage: errorMessage ?? void 0
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
export {
|
|
77
|
+
DataTableBulkEditValueEditor as default
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=DataTableBulkEditValueEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataTableBulkEditValueEditor.js","sources":["../../../../src/components/UI/DataTable/DataTableBulkEditValueEditor.tsx"],"sourcesContent":["import DatePicker from '@/components/DateTime/DatePicker';\nimport ControlledInput from '@/components/Form/ControlledInput';\nimport { ControlledSelect } from '@/components/Form/ControlledSelect';\n\nimport type { IDataTableEditableConfig, IDataTableRow } from './types';\n\ninterface IDataTableBulkEditValueEditorProps<T extends IDataTableRow> {\n editable: IDataTableEditableConfig<T>;\n value: unknown;\n onChange: (value: unknown) => void;\n label?: string;\n disabled?: boolean;\n errorMessage?: string | null;\n}\n\n/**\n * A value editor derived from the column's `editable` descriptor, the same one\n * the spreadsheet cell opens. Used by the \"same value\" mode (one editor for the\n * whole scope) and by the per-item matrix (one editor per cell).\n */\nconst DataTableBulkEditValueEditor = <T extends IDataTableRow>({\n editable,\n value,\n onChange,\n label,\n disabled,\n errorMessage,\n}: IDataTableBulkEditValueEditorProps<T>) => {\n if (editable.renderEditor) {\n return <>{editable.renderEditor({ value, onChange, disabled, label })}</>;\n }\n\n const arrayValue = Array.isArray(value) ? value.map(String) : [];\n const stringValue = value == null || Array.isArray(value) ? '' : String(value);\n\n if (editable.type === 'multiselect') {\n return (\n <ControlledSelect\n className=\"w-full\"\n label={label}\n options={Array.isArray(editable.options) ? editable.options : []}\n selectedValue={arrayValue}\n onChangeHook={(selected) => onChange(Array.isArray(selected) ? selected : [selected])}\n multiple\n searchable\n clearable\n disabled={disabled}\n />\n );\n }\n\n if (editable.type === 'select') {\n return (\n <ControlledSelect\n className=\"w-full\"\n label={label}\n options={Array.isArray(editable.options) ? editable.options : []}\n selectedValue={stringValue}\n onChangeHook={(selected) => onChange(typeof selected === 'string' ? selected : '')}\n multiple={false}\n searchable\n clearable\n disabled={disabled}\n />\n );\n }\n\n if (editable.type === 'date') {\n const parsed = stringValue ? new Date(stringValue) : undefined;\n const startValue = parsed && !Number.isNaN(parsed.getTime()) ? parsed : undefined;\n\n return (\n <DatePicker\n label={label}\n startValue={startValue}\n onUpdate={(nextValue) => onChange(nextValue ?? '')}\n disabled={disabled}\n />\n );\n }\n\n return (\n <ControlledInput\n label={label}\n value={stringValue}\n onChange={(event) => onChange(event.target.value)}\n inputMode={editable.type === 'number' ? 'decimal' : undefined}\n disabled={disabled}\n error={!!errorMessage}\n errorMessage={errorMessage ?? undefined}\n />\n );\n};\n\nexport default DataTableBulkEditValueEditor;\n"],"names":[],"mappings":";;;;AAoBA,MAAM,+BAA+B,CAA0B;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA6C;AAC3C,MAAI,SAAS,cAAc;AACzB,WAAO,oBAAA,UAAA,EAAG,mBAAS,aAAa,EAAE,OAAO,UAAU,UAAU,MAAA,CAAO,EAAA,CAAE;AAAA,EACxE;AAEA,QAAM,aAAa,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,MAAM,IAAI,CAAA;AAC9D,QAAM,cAAc,SAAS,QAAQ,MAAM,QAAQ,KAAK,IAAI,KAAK,OAAO,KAAK;AAE7E,MAAI,SAAS,SAAS,eAAe;AACnC,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV;AAAA,QACA,SAAS,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,UAAU,CAAA;AAAA,QAC9D,eAAe;AAAA,QACf,cAAc,CAAC,aAAa,SAAS,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,CAAC;AAAA,QACpF,UAAQ;AAAA,QACR,YAAU;AAAA,QACV,WAAS;AAAA,QACT;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAEA,MAAI,SAAS,SAAS,UAAU;AAC9B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV;AAAA,QACA,SAAS,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,UAAU,CAAA;AAAA,QAC9D,eAAe;AAAA,QACf,cAAc,CAAC,aAAa,SAAS,OAAO,aAAa,WAAW,WAAW,EAAE;AAAA,QACjF,UAAU;AAAA,QACV,YAAU;AAAA,QACV,WAAS;AAAA,QACT;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAEA,MAAI,SAAS,SAAS,QAAQ;AAC5B,UAAM,SAAS,cAAc,IAAI,KAAK,WAAW,IAAI;AACrD,UAAM,aAAa,UAAU,CAAC,OAAO,MAAM,OAAO,QAAA,CAAS,IAAI,SAAS;AAExE,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,UAAU,CAAC,cAAc,SAAS,aAAa,EAAE;AAAA,QACjD;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAEA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,OAAO;AAAA,MACP,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,MAChD,WAAW,SAAS,SAAS,WAAW,YAAY;AAAA,MACpD;AAAA,MACA,OAAO,CAAC,CAAC;AAAA,MACT,cAAc,gBAAgB;AAAA,IAAA;AAAA,EAAA;AAGpC;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const isBulkValueEmpty: (value: unknown) => boolean;
|
|
2
|
+
/**
|
|
3
|
+
* Compares a cell draft with the row's current value. Editors produce strings
|
|
4
|
+
* while the document may store numbers, so both sides are compared in textual
|
|
5
|
+
* form; empty (null, '', []) is a single state, so editing and reverting does
|
|
6
|
+
* not count as a change.
|
|
7
|
+
*/
|
|
8
|
+
export declare const areBulkValuesEqual: (left: unknown, right: unknown) => boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const isBulkValueEmpty = (value) => Array.isArray(value) ? value.length === 0 : value == null || value === "";
|
|
2
|
+
const areBulkValuesEqual = (left, right) => {
|
|
3
|
+
if (isBulkValueEmpty(left) && isBulkValueEmpty(right)) return true;
|
|
4
|
+
if (isBulkValueEmpty(left) !== isBulkValueEmpty(right)) return false;
|
|
5
|
+
const leftEntries = (Array.isArray(left) ? left : [left]).map(String);
|
|
6
|
+
const rightEntries = (Array.isArray(right) ? right : [right]).map(String);
|
|
7
|
+
return leftEntries.length === rightEntries.length && leftEntries.every((entry, index) => entry === rightEntries[index]);
|
|
8
|
+
};
|
|
9
|
+
export {
|
|
10
|
+
areBulkValuesEqual,
|
|
11
|
+
isBulkValueEmpty
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=bulkEditValues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulkEditValues.js","sources":["../../../../src/components/UI/DataTable/bulkEditValues.ts"],"sourcesContent":["export const isBulkValueEmpty = (value: unknown): boolean =>\n Array.isArray(value) ? value.length === 0 : value == null || value === '';\n\n/**\n * Compares a cell draft with the row's current value. Editors produce strings\n * while the document may store numbers, so both sides are compared in textual\n * form; empty (null, '', []) is a single state, so editing and reverting does\n * not count as a change.\n */\nexport const areBulkValuesEqual = (left: unknown, right: unknown): boolean => {\n if (isBulkValueEmpty(left) && isBulkValueEmpty(right)) return true;\n if (isBulkValueEmpty(left) !== isBulkValueEmpty(right)) return false;\n\n const leftEntries = (Array.isArray(left) ? left : [left]).map(String);\n const rightEntries = (Array.isArray(right) ? right : [right]).map(String);\n\n return (\n leftEntries.length === rightEntries.length && leftEntries.every((entry, index) => entry === rightEntries[index])\n );\n};\n"],"names":[],"mappings":"AAAO,MAAM,mBAAmB,CAAC,UAC/B,MAAM,QAAQ,KAAK,IAAI,MAAM,WAAW,IAAI,SAAS,QAAQ,UAAU;AAQlE,MAAM,qBAAqB,CAAC,MAAe,UAA4B;AAC5E,MAAI,iBAAiB,IAAI,KAAK,iBAAiB,KAAK,EAAG,QAAO;AAC9D,MAAI,iBAAiB,IAAI,MAAM,iBAAiB,KAAK,EAAG,QAAO;AAE/D,QAAM,eAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,MAAM;AACpE,QAAM,gBAAgB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAG,IAAI,MAAM;AAExE,SACE,YAAY,WAAW,aAAa,UAAU,YAAY,MAAM,CAAC,OAAO,UAAU,UAAU,aAAa,KAAK,CAAC;AAEnH;"}
|
|
@@ -153,7 +153,19 @@ declare const _default: {
|
|
|
153
153
|
"selectAll_other": "Select the {{count}} visible",
|
|
154
154
|
"confirm_one": "Apply to {{count}} item",
|
|
155
155
|
"confirm_other": "Apply to {{count}} items",
|
|
156
|
-
"confirmEmpty": "Pick the items"
|
|
156
|
+
"confirmEmpty": "Pick the items",
|
|
157
|
+
"subtitlePerItem": "Pick the fields and adjust each item's value; only what changed will be saved.",
|
|
158
|
+
"modeLabel": "How to apply",
|
|
159
|
+
"modeSame": "Same value",
|
|
160
|
+
"modePerItem": "Values per item",
|
|
161
|
+
"fields": "Fields",
|
|
162
|
+
"confirmChanges_one": "Apply {{count}} change",
|
|
163
|
+
"confirmChanges_other": "Apply {{count}} changes",
|
|
164
|
+
"confirmNoChanges": "No changes",
|
|
165
|
+
"pickFieldsHint": "Pick at least one field to edit per item.",
|
|
166
|
+
"noTargets": "No item in the chosen scope.",
|
|
167
|
+
"perItemHint": "Change only the cells you want. The rest stay as they are.",
|
|
168
|
+
"itemColumn": "Item"
|
|
157
169
|
}
|
|
158
170
|
},
|
|
159
171
|
"dateRangePicker": {
|
|
@@ -5,7 +5,7 @@ const controlledDrawer = { "resizeHandle": "Drag to adjust the width" };
|
|
|
5
5
|
const controlledSelect = { "loadingOptions": "Loading options", "noDataToShow": "No data to show", "moreItems": "+{{count}}" };
|
|
6
6
|
const controlledTabs = { "renameTab": "Rename tab", "tabDisabled": "This tab is disabled", "enableTab": "Enable tab", "mustKeepOneEnabled": "At least one tab must remain enabled", "disableTab": "Disable tab", "enableTabShort": "Enable tab" };
|
|
7
7
|
const createTask = { "newTask": "New task", "createTask": "Create Task", "newTaskTitle": "New Task", "taskName": "Title", "createdSuccess": "Task created successfully for {{date}}", "taskNamePlaceholder": "What is the task?", "taskType": "Task Type", "deadline": "Due date", "description": "Description", "descriptionPlaceholder": "Add a description...", "assignees": "Assignees", "saveTask": "Save Task", "cancel": "Cancel", "createdSuccessNoDate": "Task created successfully", "titleRequired": "Title is required", "taskTypeRequired": "Task type is required", "sidebarTitle": "Details", "rich": { "bold": "Bold", "italic": "Italic", "strikethrough": "Strikethrough", "link": "Link", "bulletList": "Bullet list", "numberedList": "Numbered list", "quote": "Quote", "indent": "Increase indent", "outdent": "Decrease indent", "textColor": "Text colour", "colors": { "default": "Default", "brand": "Purple", "info": "Blue", "success": "Green", "warning": "Amber", "danger": "Red", "accent": "Pink", "secondary": "Teal", "muted": "Grey" } }, "newTaskSubtitle": "Set the assignee and the due date", "createError": "Could not create the task", "requiredHint": "Fill in the title and the task type to save", "deadlinePlaceholder": "Select a date" };
|
|
8
|
-
const dataTable = { "empty": "No records found", "error": "We could not load this list", "columns": "Columns", "viewList": "List", "viewCards": "Cards", "reorderColumn": "Reorder column", "restoreDefaultOrder": "Restore default order", "resizeColumn": "Resize column", "restoreDefaultWidths": "Restore default widths", "viewGrouped": "Grouping", "viewKanban": "Kanban", "addRow": "Add row", "removeRow": "Remove row", "editCell": "Edit", "save": "Save", "fillHandle": "Drag to fill", "editMode": { "enter": "Spreadsheet editing", "exit": "Exit editing", "inactiveHint": "Turns on cell editing: keyboard navigation, selection and drag to fill", "activeHint": "Cell editing is on. Clicking a row opens the record again once you exit" }, "bulkEdit": { "title": "Bulk edit", "subtitle": "Pick the field, the new value and which items it applies to.", "open": "Bulk edit", "hint": "Applies the same value to several items at once", "field": "Field", "newValue": "New value", "scopeLabel": "Apply to", "scopeLoaded": "All loaded", "scopePicked": "Pick items", "searchRows": "Search the list", "noRowsFound": "No item matches the search.", "selectAll_one": "Select the visible item", "selectAll_other": "Select the {{count}} visible", "confirm_one": "Apply to {{count}} item", "confirm_other": "Apply to {{count}} items", "confirmEmpty": "Pick the items" } };
|
|
8
|
+
const dataTable = { "empty": "No records found", "error": "We could not load this list", "columns": "Columns", "viewList": "List", "viewCards": "Cards", "reorderColumn": "Reorder column", "restoreDefaultOrder": "Restore default order", "resizeColumn": "Resize column", "restoreDefaultWidths": "Restore default widths", "viewGrouped": "Grouping", "viewKanban": "Kanban", "addRow": "Add row", "removeRow": "Remove row", "editCell": "Edit", "save": "Save", "fillHandle": "Drag to fill", "editMode": { "enter": "Spreadsheet editing", "exit": "Exit editing", "inactiveHint": "Turns on cell editing: keyboard navigation, selection and drag to fill", "activeHint": "Cell editing is on. Clicking a row opens the record again once you exit" }, "bulkEdit": { "title": "Bulk edit", "subtitle": "Pick the field, the new value and which items it applies to.", "open": "Bulk edit", "hint": "Applies the same value to several items at once", "field": "Field", "newValue": "New value", "scopeLabel": "Apply to", "scopeLoaded": "All loaded", "scopePicked": "Pick items", "searchRows": "Search the list", "noRowsFound": "No item matches the search.", "selectAll_one": "Select the visible item", "selectAll_other": "Select the {{count}} visible", "confirm_one": "Apply to {{count}} item", "confirm_other": "Apply to {{count}} items", "confirmEmpty": "Pick the items", "subtitlePerItem": "Pick the fields and adjust each item's value; only what changed will be saved.", "modeLabel": "How to apply", "modeSame": "Same value", "modePerItem": "Values per item", "fields": "Fields", "confirmChanges_one": "Apply {{count}} change", "confirmChanges_other": "Apply {{count}} changes", "confirmNoChanges": "No changes", "pickFieldsHint": "Pick at least one field to edit per item.", "noTargets": "No item in the chosen scope.", "perItemHint": "Change only the cells you want. The rest stay as they are.", "itemColumn": "Item" } };
|
|
9
9
|
const dateRangePicker = { "selectPeriod": "Select a period", "nowSelectEndDate": "Now select the end date" };
|
|
10
10
|
const dates = { "today": "Today", "yesterday": "Yesterday", "tomorrow": "Tomorrow", "thisWeek": "This week", "lastWeek": "Last week", "thisMonth": "This month", "lastMonth": "Last month", "selectStartDate": "Select start date", "selectEndDate": "Select end date", "clearSelection": "Clear selection", "clearPeriod": "Clear selected period", "periodSelectedSuccess": "Period selected successfully" };
|
|
11
11
|
const detailsTable = { "size": "Size", "quantity": "Qty" };
|
package/dist/locales/index.d.ts
CHANGED
|
@@ -156,6 +156,18 @@ export declare const uiLocales: {
|
|
|
156
156
|
confirm_one: string;
|
|
157
157
|
confirm_other: string;
|
|
158
158
|
confirmEmpty: string;
|
|
159
|
+
subtitlePerItem: string;
|
|
160
|
+
modeLabel: string;
|
|
161
|
+
modeSame: string;
|
|
162
|
+
modePerItem: string;
|
|
163
|
+
fields: string;
|
|
164
|
+
confirmChanges_one: string;
|
|
165
|
+
confirmChanges_other: string;
|
|
166
|
+
confirmNoChanges: string;
|
|
167
|
+
pickFieldsHint: string;
|
|
168
|
+
noTargets: string;
|
|
169
|
+
perItemHint: string;
|
|
170
|
+
itemColumn: string;
|
|
159
171
|
};
|
|
160
172
|
};
|
|
161
173
|
dateRangePicker: {
|
|
@@ -651,6 +663,18 @@ export declare const uiLocales: {
|
|
|
651
663
|
confirm_one: string;
|
|
652
664
|
confirm_other: string;
|
|
653
665
|
confirmEmpty: string;
|
|
666
|
+
subtitlePerItem: string;
|
|
667
|
+
modeLabel: string;
|
|
668
|
+
modeSame: string;
|
|
669
|
+
modePerItem: string;
|
|
670
|
+
fields: string;
|
|
671
|
+
confirmChanges_one: string;
|
|
672
|
+
confirmChanges_other: string;
|
|
673
|
+
confirmNoChanges: string;
|
|
674
|
+
pickFieldsHint: string;
|
|
675
|
+
noTargets: string;
|
|
676
|
+
perItemHint: string;
|
|
677
|
+
itemColumn: string;
|
|
654
678
|
};
|
|
655
679
|
};
|
|
656
680
|
dateRangePicker: {
|
|
@@ -153,7 +153,19 @@ declare const _default: {
|
|
|
153
153
|
"selectAll_other": "Selecionar os {{count}} visíveis",
|
|
154
154
|
"confirm_one": "Aplicar a {{count}} item",
|
|
155
155
|
"confirm_other": "Aplicar a {{count}} itens",
|
|
156
|
-
"confirmEmpty": "Selecione os itens"
|
|
156
|
+
"confirmEmpty": "Selecione os itens",
|
|
157
|
+
"subtitlePerItem": "Escolha os campos e ajuste o valor de cada item; só o que mudou será salvo.",
|
|
158
|
+
"modeLabel": "Como aplicar",
|
|
159
|
+
"modeSame": "Mesmo valor",
|
|
160
|
+
"modePerItem": "Valores por item",
|
|
161
|
+
"fields": "Campos",
|
|
162
|
+
"confirmChanges_one": "Aplicar {{count}} alteração",
|
|
163
|
+
"confirmChanges_other": "Aplicar {{count}} alterações",
|
|
164
|
+
"confirmNoChanges": "Nenhuma alteração",
|
|
165
|
+
"pickFieldsHint": "Selecione ao menos um campo para editar por item.",
|
|
166
|
+
"noTargets": "Nenhum item no alcance escolhido.",
|
|
167
|
+
"perItemHint": "Altere apenas as células desejadas. As demais permanecem como estão.",
|
|
168
|
+
"itemColumn": "Item"
|
|
157
169
|
}
|
|
158
170
|
},
|
|
159
171
|
"dateRangePicker": {
|
|
@@ -5,7 +5,7 @@ const controlledDrawer = { "resizeHandle": "Arraste para ajustar a largura" };
|
|
|
5
5
|
const controlledSelect = { "loadingOptions": "Carregando opções", "noDataToShow": "Não há dados para mostrar", "moreItems": "+{{count}}" };
|
|
6
6
|
const controlledTabs = { "renameTab": "Renomear aba", "tabDisabled": "Esta aba está desabilitada", "enableTab": "Habilitar aba", "mustKeepOneEnabled": "É necessário manter ao menos uma aba habilitada", "disableTab": "Desabilitar tab", "enableTabShort": "Habilitar tab" };
|
|
7
7
|
const createTask = { "newTask": "Nova tarefa", "createTask": "Criar Tarefa", "newTaskTitle": "Nova Tarefa", "taskName": "Título", "createdSuccess": "Tarefa criada com sucesso para {{date}}", "taskNamePlaceholder": "Qual é a tarefa?", "taskType": "Tipo de Tarefa", "deadline": "Prazo", "description": "Descrição", "descriptionPlaceholder": "Adicione uma descrição...", "assignees": "Responsáveis", "saveTask": "Gravar Tarefa", "cancel": "Cancelar", "createdSuccessNoDate": "Tarefa criada com sucesso", "titleRequired": "O título é obrigatório", "taskTypeRequired": "O tipo de tarefa é obrigatório", "sidebarTitle": "Detalhes", "rich": { "bold": "Negrito", "italic": "Itálico", "strikethrough": "Tachado", "link": "Link", "bulletList": "Lista com marcador", "numberedList": "Lista numerada", "quote": "Citação", "indent": "Aumentar recuo", "outdent": "Diminuir recuo", "textColor": "Cor do texto", "colors": { "default": "Padrão", "brand": "Roxo", "info": "Azul", "success": "Verde", "warning": "Âmbar", "danger": "Vermelho", "accent": "Rosa", "secondary": "Turquesa", "muted": "Cinza" } }, "newTaskSubtitle": "Defina o responsável e o prazo", "createError": "Não foi possível criar a tarefa", "requiredHint": "Informe o título e o tipo de tarefa para gravar", "deadlinePlaceholder": "Selecionar data" };
|
|
8
|
-
const dataTable = { "empty": "Nenhum registro encontrado", "error": "Não conseguimos carregar esta lista", "columns": "Colunas", "viewList": "Lista", "viewCards": "Cards", "reorderColumn": "Reordenar coluna", "restoreDefaultOrder": "Restaurar ordem padrão", "resizeColumn": "Redimensionar coluna", "restoreDefaultWidths": "Restaurar largura padrão", "viewGrouped": "Agrupamento", "viewKanban": "Kanban", "addRow": "Adicionar linha", "removeRow": "Remover linha", "editCell": "Editar", "save": "Salvar", "fillHandle": "Arraste para preencher", "editMode": { "enter": "Editar em planilha", "exit": "Sair da edição", "inactiveHint": "Ativa a edição em célula: navegação por teclado, seleção e preenchimento por arraste", "activeHint": "Edição em célula ativa. O clique na linha volta a abrir o registro quando você sair" }, "bulkEdit": { "title": "Editar em massa", "subtitle": "Escolha o campo, o novo valor e a quais itens aplicar.", "open": "Editar em massa", "hint": "Aplica um mesmo valor a vários itens de uma vez", "field": "Campo", "newValue": "Novo valor", "scopeLabel": "Aplicar a", "scopeLoaded": "Todos os carregados", "scopePicked": "Escolher itens", "searchRows": "Buscar na lista", "noRowsFound": "Nenhum item corresponde à busca.", "selectAll_one": "Selecionar o item visível", "selectAll_other": "Selecionar os {{count}} visíveis", "confirm_one": "Aplicar a {{count}} item", "confirm_other": "Aplicar a {{count}} itens", "confirmEmpty": "Selecione os itens" } };
|
|
8
|
+
const dataTable = { "empty": "Nenhum registro encontrado", "error": "Não conseguimos carregar esta lista", "columns": "Colunas", "viewList": "Lista", "viewCards": "Cards", "reorderColumn": "Reordenar coluna", "restoreDefaultOrder": "Restaurar ordem padrão", "resizeColumn": "Redimensionar coluna", "restoreDefaultWidths": "Restaurar largura padrão", "viewGrouped": "Agrupamento", "viewKanban": "Kanban", "addRow": "Adicionar linha", "removeRow": "Remover linha", "editCell": "Editar", "save": "Salvar", "fillHandle": "Arraste para preencher", "editMode": { "enter": "Editar em planilha", "exit": "Sair da edição", "inactiveHint": "Ativa a edição em célula: navegação por teclado, seleção e preenchimento por arraste", "activeHint": "Edição em célula ativa. O clique na linha volta a abrir o registro quando você sair" }, "bulkEdit": { "title": "Editar em massa", "subtitle": "Escolha o campo, o novo valor e a quais itens aplicar.", "open": "Editar em massa", "hint": "Aplica um mesmo valor a vários itens de uma vez", "field": "Campo", "newValue": "Novo valor", "scopeLabel": "Aplicar a", "scopeLoaded": "Todos os carregados", "scopePicked": "Escolher itens", "searchRows": "Buscar na lista", "noRowsFound": "Nenhum item corresponde à busca.", "selectAll_one": "Selecionar o item visível", "selectAll_other": "Selecionar os {{count}} visíveis", "confirm_one": "Aplicar a {{count}} item", "confirm_other": "Aplicar a {{count}} itens", "confirmEmpty": "Selecione os itens", "subtitlePerItem": "Escolha os campos e ajuste o valor de cada item; só o que mudou será salvo.", "modeLabel": "Como aplicar", "modeSame": "Mesmo valor", "modePerItem": "Valores por item", "fields": "Campos", "confirmChanges_one": "Aplicar {{count}} alteração", "confirmChanges_other": "Aplicar {{count}} alterações", "confirmNoChanges": "Nenhuma alteração", "pickFieldsHint": "Selecione ao menos um campo para editar por item.", "noTargets": "Nenhum item no alcance escolhido.", "perItemHint": "Altere apenas as células desejadas. As demais permanecem como estão.", "itemColumn": "Item" } };
|
|
9
9
|
const dateRangePicker = { "selectPeriod": "Selecione um período", "nowSelectEndDate": "Agora selecione a data final" };
|
|
10
10
|
const dates = { "today": "Hoje", "yesterday": "Ontem", "tomorrow": "Amanhã", "thisWeek": "Esta semana", "lastWeek": "Semana passada", "thisMonth": "Este mês", "lastMonth": "Mês passado", "selectStartDate": "Selecione a data inicial", "selectEndDate": "Selecione a data final", "clearSelection": "Limpar Seleção", "clearPeriod": "Limpar Período Selecionado", "periodSelectedSuccess": "Período selecionado com sucesso" };
|
|
11
11
|
const detailsTable = { "size": "Tam", "quantity": "Qtd" };
|