@wavemaker-ai/react-runtime 1.0.0-rc.647712 → 12.0.0-rc.335
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/components/basic/richtexteditor/index.d.ts +1 -0
- package/components/basic/search/index.js +5 -4
- package/components/basic/search/utils.d.ts +1 -0
- package/components/basic/search/utils.js +9 -0
- package/components/common/index.d.ts +2 -0
- package/components/common/index.js +2 -0
- package/components/common/partial-content-wrapper.d.ts +15 -0
- package/components/common/partial-content-wrapper.js +11 -0
- package/components/container/index.js +5 -2
- package/components/container/tabs/index.js +5 -3
- package/components/container/wizard/index.js +36 -3
- package/components/container/wizard/props.d.ts +2 -2
- package/components/data/form/dynamic-fields/WmxDynamicFormField.js +9 -23
- package/components/data/form/form-controller/props.d.ts +2 -0
- package/components/data/form/form-controller/utils.js +3 -2
- package/components/data/form/form-controller/validation-contrustor.js +112 -1
- package/components/data/form/form-controller/withFormController.js +1 -1
- package/components/data/form/form-field/index.js +6 -3
- package/components/data/form/form-field/props.d.ts +1 -0
- package/components/data/list/components/ListItemWithTemplate.js +1 -1
- package/components/data/list/index.js +1 -1
- package/components/data/table/components/EditableCell.js +47 -24
- package/components/data/table/components/FieldValidationError.js +1 -1
- package/components/data/table/components/TableHeader.js +9 -3
- package/components/data/table/hooks/useFormWidget.js +11 -0
- package/components/data/table/hooks/useRowSelection.js +58 -1
- package/components/data/table/hooks/useTableEdit.d.ts +1 -1
- package/components/data/table/hooks/useTableEdit.js +115 -75
- package/components/data/table/hooks/useTableEditForms.d.ts +25 -0
- package/components/data/table/hooks/useTableEditForms.js +30 -0
- package/components/data/table/index.js +68 -32
- package/components/data/table/props.d.ts +42 -23
- package/components/data/table/utils/buildSelectionColumns.js +3 -3
- package/components/data/table/utils/columnProxy.d.ts +7 -1
- package/components/data/table/utils/columnProxy.js +21 -1
- package/components/data/table/utils/columnValidation.d.ts +19 -0
- package/components/data/table/utils/columnValidation.js +76 -0
- package/components/data/table/utils/constants.d.ts +2 -0
- package/components/data/table/utils/constants.js +2 -0
- package/components/data/table/utils/index.d.ts +4 -12
- package/components/data/table/utils/index.js +13 -77
- package/components/data/table/utils/selectionUtils.js +13 -5
- package/components/input/chips/index.d.ts +1 -0
- package/components/input/color-picker/index.d.ts +1 -0
- package/components/input/currency/index.d.ts +1 -0
- package/components/input/default/checkbox/index.d.ts +1 -0
- package/components/input/default/checkboxset/index.d.ts +1 -0
- package/components/input/default/radioset/index.d.ts +1 -0
- package/components/input/default/switch/index.d.ts +1 -0
- package/components/input/epoch/datetime/index.js +10 -2
- package/components/input/number/index.d.ts +1 -0
- package/components/input/rating/index.d.ts +1 -0
- package/components/input/select/index.d.ts +1 -0
- package/components/input/slider/index.d.ts +1 -0
- package/components/input/text/index.d.ts +1 -0
- package/components/input/textarea/index.d.ts +1 -0
- package/components/input/upload/index.d.ts +1 -0
- package/components/layout/footer/index.js +4 -2
- package/components/layout/footer/props.d.ts +1 -0
- package/components/layout/header/index.js +29 -3
- package/components/layout/header/props.d.ts +1 -0
- package/components/layout/leftnav/index.js +22 -6
- package/components/layout/leftnav/props.d.ts +1 -0
- package/components/layout/leftnav/utils/page-class-util.d.ts +1 -0
- package/components/layout/leftnav/utils/page-class-util.js +4 -3
- package/components/layout/rightnav/index.js +4 -2
- package/components/layout/rightnav/props.d.ts +1 -0
- package/components/layout/topnav/index.js +4 -2
- package/components/layout/topnav/props.d.ts +1 -0
- package/components/navbar/index.js +32 -7
- package/components/navigation/menu/index.js +6 -2
- package/components/page/index.js +60 -19
- package/components/page/page-context.d.ts +4 -0
- package/core/proxy-service.js +86 -19
- package/higherOrder/BaseApp.js +1 -1
- package/higherOrder/BasePage.js +2 -1
- package/higherOrder/withBaseWrapper.js +10 -2
- package/hooks/useHttp.d.ts +14 -4
- package/hooks/useHttp.js +47 -9
- package/package-lock.json +314 -308
- package/package.json +3 -3
- package/runtime-dynamic/App.js +21 -2
- package/runtime-dynamic/app-initializer.js +8 -1
- package/runtime-dynamic/factories/dynamic-component.js +4 -3
- package/runtime-dynamic/main.d.ts +1 -0
- package/runtime-dynamic/main.js +9 -1
- package/runtime-dynamic/services/compile-render.js +7 -0
- package/runtime-dynamic/services/css-scoping.d.ts +1 -1
- package/runtime-dynamic/services/css-scoping.js +2 -2
- package/runtime-dynamic/services/variable-factory.js +2 -1
- package/runtime-dynamic/services/variable-transpiler.js +4 -1
- package/runtime-dynamic/wmx-shared.d.ts +40 -0
- package/runtime-dynamic/wmx-shared.js +33 -0
- package/utils/style-utils.js +1 -1
- package/utils/wmx.utils.d.ts +0 -1
- package/utils/wmx.utils.js +0 -25
- package/variables/crud-variable.d.ts +10 -0
- package/variables/crud-variable.js +53 -2
- package/variables/live-variable.d.ts +1 -0
- package/variables/live-variable.js +2 -1
- package/variables/operation-params.d.ts +29 -0
- package/variables/operation-params.js +69 -0
- package/variables/service-variable.d.ts +9 -0
- package/variables/service-variable.js +21 -2
- package/components/data/table/utils/validation.d.ts +0 -13
- package/components/data/table/utils/validation.js +0 -82
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useCallback, useEffect } from "react";
|
|
3
3
|
import { Box } from "@mui/material";
|
|
4
|
+
import { useFormState } from "react-hook-form";
|
|
5
|
+
import { get, isString } from "lodash-es";
|
|
4
6
|
import { FieldValidationError } from "./FieldValidationError";
|
|
7
|
+
import { isColumnValidatable } from "../utils/columnValidation";
|
|
8
|
+
const CELL_SX = {
|
|
9
|
+
position: "relative",
|
|
10
|
+
"& > .wi-error": {
|
|
11
|
+
position: "absolute",
|
|
12
|
+
// Centres the 16px glyph on the editor without depending on the editor's height.
|
|
13
|
+
top: "calc(50% - 10px)",
|
|
14
|
+
right: 0,
|
|
15
|
+
// Above the input, which would otherwise paint over the icon.
|
|
16
|
+
zIndex: 3
|
|
17
|
+
}
|
|
18
|
+
};
|
|
5
19
|
const EditableCell = ({
|
|
6
20
|
column,
|
|
7
21
|
rowData,
|
|
@@ -9,8 +23,9 @@ const EditableCell = ({
|
|
|
9
23
|
fieldName,
|
|
10
24
|
widgetType,
|
|
11
25
|
editValue,
|
|
12
|
-
|
|
13
|
-
|
|
26
|
+
formRef,
|
|
27
|
+
cellErrorsRef,
|
|
28
|
+
columnValidatorsRef,
|
|
14
29
|
fieldRefs,
|
|
15
30
|
renderFormWidget,
|
|
16
31
|
updateFieldValue,
|
|
@@ -19,31 +34,32 @@ const EditableCell = ({
|
|
|
19
34
|
editMode = "inline"
|
|
20
35
|
}) => {
|
|
21
36
|
var _a;
|
|
22
|
-
const cellRef = useRef(null);
|
|
23
|
-
const [, setForceUpdate] = useState(0);
|
|
24
37
|
const fieldKey = `${rowId}_${fieldName}`;
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
const { errors } = useFormState({ control: formRef.control, name: fieldName, exact: true });
|
|
39
|
+
const error = get(errors, fieldName);
|
|
40
|
+
const showError = Boolean(error);
|
|
41
|
+
const cellRefCallback = useCallback(
|
|
42
|
+
(element) => {
|
|
43
|
+
if (!fieldRefs.current) return;
|
|
44
|
+
if (element) fieldRefs.current[fieldKey] = element;
|
|
45
|
+
else delete fieldRefs.current[fieldKey];
|
|
46
|
+
},
|
|
47
|
+
[fieldKey, fieldRefs]
|
|
48
|
+
);
|
|
31
49
|
useEffect(() => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
const cellErrors = cellErrorsRef == null ? void 0 : cellErrorsRef.current;
|
|
51
|
+
if (!cellErrors) return;
|
|
52
|
+
if (showError) cellErrors[fieldKey] = true;
|
|
53
|
+
else delete cellErrors[fieldKey];
|
|
35
54
|
return () => {
|
|
36
|
-
|
|
37
|
-
delete cellUpdateCallbacksRef.current[fieldKey];
|
|
38
|
-
}
|
|
55
|
+
delete cellErrors[fieldKey];
|
|
39
56
|
};
|
|
40
|
-
}, [fieldKey,
|
|
41
|
-
const showError = ((_a = fieldValidationErrorsRef.current) == null ? void 0 : _a[fieldKey]) === true;
|
|
57
|
+
}, [showError, fieldKey, cellErrorsRef]);
|
|
42
58
|
const handleKeyDown = (e) => {
|
|
43
|
-
|
|
44
|
-
onKeyDown(e, rowId);
|
|
45
|
-
}
|
|
59
|
+
onKeyDown == null ? void 0 : onKeyDown(e, rowId);
|
|
46
60
|
};
|
|
61
|
+
const ignoreInForm = !isColumnValidatable(column);
|
|
62
|
+
const columnValidators = (_a = columnValidatorsRef == null ? void 0 : columnValidatorsRef.current) == null ? void 0 : _a[fieldName];
|
|
47
63
|
return /* @__PURE__ */ jsxs(
|
|
48
64
|
Box,
|
|
49
65
|
{
|
|
@@ -51,6 +67,7 @@ const EditableCell = ({
|
|
|
51
67
|
onKeyDown: editMode === "quickedit" || editMode === "inline" ? handleKeyDown : void 0,
|
|
52
68
|
"data-field-name": fieldName,
|
|
53
69
|
"data-row-id": rowId,
|
|
70
|
+
sx: CELL_SX,
|
|
54
71
|
children: [
|
|
55
72
|
renderFormWidget(
|
|
56
73
|
fieldName,
|
|
@@ -64,14 +81,20 @@ const EditableCell = ({
|
|
|
64
81
|
defaultvalue: column.defaultvalue,
|
|
65
82
|
sessionKey: `${sessionKey}_${rowId}_${fieldName}`,
|
|
66
83
|
rowData,
|
|
67
|
-
column
|
|
84
|
+
column,
|
|
85
|
+
formRef,
|
|
86
|
+
// formKey stays stable; widget name keeps session-unique value
|
|
87
|
+
formKey: fieldName,
|
|
88
|
+
validators: columnValidators == null ? void 0 : columnValidators.validators,
|
|
89
|
+
asyncValidators: columnValidators == null ? void 0 : columnValidators.asyncValidators,
|
|
90
|
+
ignoreInForm
|
|
68
91
|
}
|
|
69
92
|
),
|
|
70
93
|
showError && /* @__PURE__ */ jsx(
|
|
71
94
|
FieldValidationError,
|
|
72
95
|
{
|
|
73
96
|
showError: true,
|
|
74
|
-
title:
|
|
97
|
+
title: isString(error == null ? void 0 : error.message) && error.message || column.validationmessage || (column.required ? "This field is required" : "Field validation failed")
|
|
75
98
|
}
|
|
76
99
|
)
|
|
77
100
|
]
|
|
@@ -36,6 +36,7 @@ import { flexRender } from "@tanstack/react-table";
|
|
|
36
36
|
import { TABLE_CSS_CLASSES, getDomHiddenCellStyle } from "../utils";
|
|
37
37
|
import { TableFilterRow } from "./TableFilters";
|
|
38
38
|
import { calculateMaxDepth, generateHeaderRows, hasTableGroups } from "../utils/groupHeaderUtils";
|
|
39
|
+
import { getWidgetForCallback } from "../utils/selectionUtils";
|
|
39
40
|
const StandardHeaderCell = memo(
|
|
40
41
|
(_a) => {
|
|
41
42
|
var _b = _a, {
|
|
@@ -169,6 +170,7 @@ const TableHeaderComponent = memo(
|
|
|
169
170
|
onColumnFilterChange,
|
|
170
171
|
renderFormWidget,
|
|
171
172
|
listener,
|
|
173
|
+
viewParent,
|
|
172
174
|
tableStructure,
|
|
173
175
|
onColumnSelect,
|
|
174
176
|
onColumnDeselect,
|
|
@@ -342,7 +344,7 @@ const TableHeaderComponent = memo(
|
|
|
342
344
|
const columnId = header.column.id;
|
|
343
345
|
const isSystemColumn = columnId === "multiSelect" || columnId === "radioSelect" || columnId === "row-index" || columnId === "rowIndex" || columnId === "actions";
|
|
344
346
|
const handleHeaderClick = (event) => {
|
|
345
|
-
var _a2
|
|
347
|
+
var _a2;
|
|
346
348
|
if (onHeaderclick) {
|
|
347
349
|
const colDef = header.column.columnDef;
|
|
348
350
|
const columnData = {
|
|
@@ -350,7 +352,11 @@ const TableHeaderComponent = memo(
|
|
|
350
352
|
caption: colDef.header || columnId,
|
|
351
353
|
colDef
|
|
352
354
|
};
|
|
353
|
-
onHeaderclick(
|
|
355
|
+
onHeaderclick(
|
|
356
|
+
event,
|
|
357
|
+
getWidgetForCallback(listener, viewParent, tableName || ""),
|
|
358
|
+
columnData
|
|
359
|
+
);
|
|
354
360
|
}
|
|
355
361
|
if (enablecolumnselection && !isSystemColumn) {
|
|
356
362
|
const target = event.currentTarget;
|
|
@@ -376,7 +382,7 @@ const TableHeaderComponent = memo(
|
|
|
376
382
|
return;
|
|
377
383
|
}
|
|
378
384
|
if (enablesort && header.column.getCanSort()) {
|
|
379
|
-
(
|
|
385
|
+
(_a2 = header.column.getToggleSortingHandler()) == null ? void 0 : _a2(event);
|
|
380
386
|
}
|
|
381
387
|
};
|
|
382
388
|
const isClickable = enablecolumnselection && !isSystemColumn || enablesort && header.column.getCanSort();
|
|
@@ -66,6 +66,17 @@ const useFormWidget = ({ listener }) => {
|
|
|
66
66
|
key: uniqueName,
|
|
67
67
|
// Add key to force component recreation
|
|
68
68
|
name: uniqueName,
|
|
69
|
+
// Wiring the cell into the table's own react-hook-form instance. withFormController
|
|
70
|
+
// prefers this `formRef` over the page form's context, keys the field by `formKey`, and
|
|
71
|
+
// turns `validators` (plus the column's required/regexp/maxchars/min/max) into rules —
|
|
72
|
+
// including the date/time bounds it hands straight back to the picker. `ignoreInForm`
|
|
73
|
+
// opts a cell out entirely, leaving the widget uncontrolled.
|
|
74
|
+
formRef: widgetProps == null ? void 0 : widgetProps.formRef,
|
|
75
|
+
formKey: widgetProps == null ? void 0 : widgetProps.formKey,
|
|
76
|
+
validators: widgetProps == null ? void 0 : widgetProps.validators,
|
|
77
|
+
asyncValidators: widgetProps == null ? void 0 : widgetProps.asyncValidators,
|
|
78
|
+
ignoreInForm: widgetProps == null ? void 0 : widgetProps.ignoreInForm,
|
|
79
|
+
hideValidationMessage: true,
|
|
69
80
|
datavalue: effectiveValue,
|
|
70
81
|
required: (widgetProps == null ? void 0 : widgetProps.required) || false,
|
|
71
82
|
disabled,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import isEqual from "lodash-es/isEqual";
|
|
2
3
|
import {
|
|
3
4
|
isInteractiveElement,
|
|
4
5
|
isSelectionCheckboxRenamed,
|
|
@@ -12,6 +13,36 @@ import {
|
|
|
12
13
|
import { saveTableState, getTableState } from "../utils/table-helpers";
|
|
13
14
|
import { cleanRowData, UNSUPPORTED_STATE_PERSISTENCE_TYPES } from "../utils";
|
|
14
15
|
const getRowFallbackId = (row) => row._wmTableRowId || String(row.id) || String(0);
|
|
16
|
+
const omitEmptyArrays = (row) => {
|
|
17
|
+
const result = {};
|
|
18
|
+
Object.keys(row).forEach((key) => {
|
|
19
|
+
const value = row[key];
|
|
20
|
+
if (Array.isArray(value) && value.length === 0) return;
|
|
21
|
+
if (typeof value === "function") return;
|
|
22
|
+
result[key] = value;
|
|
23
|
+
});
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
const resolveRowFromDataset = (dataset, itemOrIndex) => {
|
|
27
|
+
var _a, _b, _c;
|
|
28
|
+
const rows = dataset || [];
|
|
29
|
+
if (typeof itemOrIndex === "number") {
|
|
30
|
+
return (_a = rows[itemOrIndex]) != null ? _a : null;
|
|
31
|
+
}
|
|
32
|
+
if (!itemOrIndex || typeof itemOrIndex !== "object") {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (rows.indexOf(itemOrIndex) >= 0) {
|
|
36
|
+
return itemOrIndex;
|
|
37
|
+
}
|
|
38
|
+
const candidateId = (_b = itemOrIndex._wmTableRowId) != null ? _b : itemOrIndex.id;
|
|
39
|
+
if (candidateId !== void 0 && candidateId !== null) {
|
|
40
|
+
const byId = rows.find((row) => row._wmTableRowId === candidateId || row.id === candidateId);
|
|
41
|
+
if (byId) return byId;
|
|
42
|
+
}
|
|
43
|
+
const needle = omitEmptyArrays(itemOrIndex);
|
|
44
|
+
return (_c = rows.find((row) => isEqual(omitEmptyArrays(row), needle))) != null ? _c : null;
|
|
45
|
+
};
|
|
15
46
|
const getSelectedIndices = (selectedIds, dataset) => selectedIds.reduce((indices, rowId) => {
|
|
16
47
|
const index = dataset.findIndex((row) => row._wmTableRowId === rowId || row.id === rowId);
|
|
17
48
|
if (index >= 0) {
|
|
@@ -354,6 +385,30 @@ const useRowSelection = ({
|
|
|
354
385
|
useMultiSelect,
|
|
355
386
|
applySelectionForMode
|
|
356
387
|
]);
|
|
388
|
+
const selectItem = useCallback(
|
|
389
|
+
(itemOrIndex) => {
|
|
390
|
+
const row = resolveRowFromDataset(datasetRef.current, itemOrIndex);
|
|
391
|
+
if (!row) return;
|
|
392
|
+
const rowId = getRowFallbackId(row);
|
|
393
|
+
if (selectionStateHelpers.isRowSelected(cellState, rowId)) return;
|
|
394
|
+
if (useMultiSelect) {
|
|
395
|
+
handleMultiSelection(rowId, row, true);
|
|
396
|
+
} else {
|
|
397
|
+
handleRadioSelection(rowId, row);
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
[cellState, useMultiSelect, handleMultiSelection, handleRadioSelection]
|
|
401
|
+
);
|
|
402
|
+
const deselectItem = useCallback(
|
|
403
|
+
(itemOrIndex) => {
|
|
404
|
+
const row = resolveRowFromDataset(datasetRef.current, itemOrIndex);
|
|
405
|
+
if (!row) return;
|
|
406
|
+
const rowId = getRowFallbackId(row);
|
|
407
|
+
if (!selectionStateHelpers.isRowSelected(cellState, rowId)) return;
|
|
408
|
+
handleMultiSelection(rowId, row, false);
|
|
409
|
+
},
|
|
410
|
+
[cellState, handleMultiSelection]
|
|
411
|
+
);
|
|
357
412
|
return {
|
|
358
413
|
selectedRowIds,
|
|
359
414
|
useMultiSelect,
|
|
@@ -363,7 +418,9 @@ const useRowSelection = ({
|
|
|
363
418
|
handleSelectAll,
|
|
364
419
|
handleRowSelectionClick,
|
|
365
420
|
isRowSelected,
|
|
366
|
-
isInteractiveElement
|
|
421
|
+
isInteractiveElement,
|
|
422
|
+
selectItem,
|
|
423
|
+
deselectItem
|
|
367
424
|
};
|
|
368
425
|
};
|
|
369
426
|
export {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { UseFormWidgetReturn, UseTableEditProps, UseTableEditReturn } from "../props";
|
|
2
|
-
export declare const useTableEdit: ({ editMode, internalDataset, setInternalDataset, wmTableColumns, cellState, renderFormWidget, listener, onRowUpdate, onRowDelete, onNewRowAdded, showrowindex, radioselect, multiselect, rowActions, formposition, insertmessage, updatemessage, errormessage, showToast, hasRowExpansion, expansionPosition, datasource, binddataset, onSuccess, onError, onRowinsert, onRowupdate, tableRef, isServerSidePagination, tableName, }: UseTableEditProps & {
|
|
2
|
+
export declare const useTableEdit: ({ editMode, internalDataset, setInternalDataset, wmTableColumns, cellState, renderFormWidget, listener, onRowUpdate, onRowDelete, onNewRowAdded, showrowindex, radioselect, multiselect, rowActions, formposition, insertmessage, updatemessage, errormessage, showToast, hasRowExpansion, expansionPosition, datasource, binddataset, onSuccess, onError, onRowinsert, onRowupdate, tableRef, isServerSidePagination, tableName, columnValidatorsRef, }: UseTableEditProps & {
|
|
3
3
|
renderFormWidget: UseFormWidgetReturn["renderFormWidget"];
|
|
4
4
|
}) => UseTableEditReturn;
|
|
@@ -19,22 +19,38 @@ var __spreadValues = (a, b) => {
|
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
20
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
21
21
|
import { useCallback, useRef, useEffect } from "react";
|
|
22
|
-
import {
|
|
22
|
+
import { get } from "lodash-es";
|
|
23
23
|
import { EditableCell } from "../components";
|
|
24
24
|
import { AddNewRow } from "../components/AddNewRow";
|
|
25
|
-
import {
|
|
25
|
+
import { TABLE_MESSAGES, INTERNAL_PROPERTIES } from "../utils";
|
|
26
26
|
import { useEditingState } from "./useEditingState";
|
|
27
27
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
} from "../utils/
|
|
28
|
+
isColumnValidatable,
|
|
29
|
+
collectNativeValidityErrors,
|
|
30
|
+
focusFirstInvalidCell
|
|
31
|
+
} from "../utils/columnValidation";
|
|
32
|
+
import { useTableEditForms } from "./useTableEditForms";
|
|
32
33
|
import { handleServerOperation } from "../utils/crud-handlers";
|
|
33
34
|
import { useUpdateEditedCell, useEditedRowsContext } from "./use-edited-rows";
|
|
34
35
|
import {
|
|
35
36
|
useFormContext,
|
|
36
37
|
useParentFormData
|
|
37
38
|
} from "../../form/form-context";
|
|
39
|
+
const DEFERRED_COMMIT_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
40
|
+
"text",
|
|
41
|
+
"number",
|
|
42
|
+
"email",
|
|
43
|
+
"url",
|
|
44
|
+
"tel",
|
|
45
|
+
"search",
|
|
46
|
+
"password"
|
|
47
|
+
]);
|
|
48
|
+
const readPendingInputValue = (element) => {
|
|
49
|
+
if (element.tagName === "TEXTAREA") return element.value;
|
|
50
|
+
if (element.tagName !== "INPUT") return void 0;
|
|
51
|
+
const input = element;
|
|
52
|
+
return DEFERRED_COMMIT_INPUT_TYPES.has(input.type) ? input.value : void 0;
|
|
53
|
+
};
|
|
38
54
|
const MODE_CONFIGS = {
|
|
39
55
|
inline: {
|
|
40
56
|
showNewRowFormByDefault: false,
|
|
@@ -97,7 +113,8 @@ const useTableEdit = ({
|
|
|
97
113
|
onRowupdate,
|
|
98
114
|
tableRef,
|
|
99
115
|
isServerSidePagination,
|
|
100
|
-
tableName
|
|
116
|
+
tableName,
|
|
117
|
+
columnValidatorsRef
|
|
101
118
|
}) => {
|
|
102
119
|
const updateCell = useUpdateEditedCell();
|
|
103
120
|
const { getEdits, removeRowEdits } = useEditedRowsContext();
|
|
@@ -118,20 +135,24 @@ const useTableEdit = ({
|
|
|
118
135
|
const parentFormDataContext = useParentFormData();
|
|
119
136
|
const parentDataPath = (parentFormDataContext == null ? void 0 : parentFormDataContext.parentDataPath) !== void 0 ? parentFormDataContext.parentDataPath : void 0;
|
|
120
137
|
const fieldRefs = useRef({});
|
|
121
|
-
const
|
|
122
|
-
const cellUpdateCallbacksRef = useRef({});
|
|
138
|
+
const cellErrorsRef = useRef({});
|
|
123
139
|
const originalRowDataRef = useRef({});
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
140
|
+
const { editForm, newRowForm } = useTableEditForms(tableName);
|
|
141
|
+
const usesSeparateNewRowSession = editMode === "quickedit";
|
|
142
|
+
const formFor = useCallback(
|
|
143
|
+
(isNewRowSession) => isNewRowSession && usesSeparateNewRowSession ? newRowForm : editForm,
|
|
144
|
+
[editForm, newRowForm, usesSeparateNewRowSession]
|
|
145
|
+
);
|
|
146
|
+
const dataRefFor = useCallback(
|
|
147
|
+
(isNewRowSession) => isNewRowSession && usesSeparateNewRowSession ? newRowDataRef : editingRowDataRef,
|
|
148
|
+
[usesSeparateNewRowSession, newRowDataRef, editingRowDataRef]
|
|
149
|
+
);
|
|
129
150
|
const config = MODE_CONFIGS[editMode] || MODE_CONFIGS.none;
|
|
130
151
|
const resetValidationState = useCallback(
|
|
131
|
-
(
|
|
132
|
-
|
|
152
|
+
(isNewRowSession = false) => {
|
|
153
|
+
formFor(isNewRowSession).reset();
|
|
133
154
|
},
|
|
134
|
-
[
|
|
155
|
+
[formFor]
|
|
135
156
|
);
|
|
136
157
|
useEffect(() => {
|
|
137
158
|
setIsAddingNewRow(config.showNewRowFormByDefault);
|
|
@@ -150,7 +171,7 @@ const useTableEdit = ({
|
|
|
150
171
|
setEditingRowId(rowId);
|
|
151
172
|
const existingEdits = getEdits(rowId) || {};
|
|
152
173
|
editingRowDataRef.current = __spreadValues(__spreadValues({}, rowData), existingEdits);
|
|
153
|
-
resetValidationState(
|
|
174
|
+
resetValidationState();
|
|
154
175
|
incrementSessionKey();
|
|
155
176
|
},
|
|
156
177
|
[
|
|
@@ -177,7 +198,7 @@ const useTableEdit = ({
|
|
|
177
198
|
editingRowDataRef.current = {};
|
|
178
199
|
}
|
|
179
200
|
resetEditingData();
|
|
180
|
-
resetValidationState(
|
|
201
|
+
resetValidationState();
|
|
181
202
|
incrementSessionKey();
|
|
182
203
|
}, [
|
|
183
204
|
editMode,
|
|
@@ -191,27 +212,43 @@ const useTableEdit = ({
|
|
|
191
212
|
cellState
|
|
192
213
|
]);
|
|
193
214
|
const saveEditingInternal = useCallback(
|
|
194
|
-
async (rowId, isNewRow, rowDataRef, onSaveSuccess) => {
|
|
215
|
+
async (rowId, isNewRow, rowDataRef, form, onSaveSuccess) => {
|
|
216
|
+
var _a, _b;
|
|
195
217
|
const currentRowId = rowId || (isNewRow ? "new-row" : null);
|
|
196
218
|
if (!currentRowId) return Promise.resolve(false);
|
|
197
219
|
const activeElement = document.activeElement;
|
|
198
220
|
if (activeElement) {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
if (activeElement.blur) {
|
|
210
|
-
activeElement.blur();
|
|
221
|
+
const fieldName = (_a = activeElement.closest("[data-field-name]")) == null ? void 0 : _a.getAttribute("data-field-name");
|
|
222
|
+
const pendingValue = readPendingInputValue(activeElement);
|
|
223
|
+
if (fieldName && pendingValue !== void 0) {
|
|
224
|
+
rowDataRef.current = __spreadProps(__spreadValues({}, rowDataRef.current), {
|
|
225
|
+
[fieldName]: pendingValue
|
|
226
|
+
});
|
|
227
|
+
form.setFieldValue(fieldName, pendingValue);
|
|
211
228
|
}
|
|
229
|
+
(_b = activeElement.blur) == null ? void 0 : _b.call(activeElement);
|
|
212
230
|
}
|
|
213
231
|
const currentEditingData = rowDataRef.current;
|
|
214
232
|
const originalData = originalRowDataRef.current;
|
|
233
|
+
const validatableFields = wmTableColumns.filter(isColumnValidatable).map((column) => column.field);
|
|
234
|
+
if (!await form.validate()) {
|
|
235
|
+
focusFirstInvalidCell(
|
|
236
|
+
fieldRefs.current,
|
|
237
|
+
currentRowId,
|
|
238
|
+
form.getInvalidFields(validatableFields)
|
|
239
|
+
);
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
const nativeErrors = collectNativeValidityErrors(fieldRefs.current, currentRowId);
|
|
243
|
+
if (nativeErrors.length > 0) {
|
|
244
|
+
nativeErrors.forEach(({ fieldName, message }) => form.setFieldError(fieldName, message));
|
|
245
|
+
focusFirstInvalidCell(
|
|
246
|
+
fieldRefs.current,
|
|
247
|
+
currentRowId,
|
|
248
|
+
nativeErrors.map(({ fieldName }) => fieldName)
|
|
249
|
+
);
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
215
252
|
if (!isNewRow && originalData && currentEditingData) {
|
|
216
253
|
const hasChanges = wmTableColumns.some((column) => {
|
|
217
254
|
if (!column.field || column.readonly) return false;
|
|
@@ -230,17 +267,6 @@ const useTableEdit = ({
|
|
|
230
267
|
return true;
|
|
231
268
|
}
|
|
232
269
|
}
|
|
233
|
-
const relevantFieldRefs = {};
|
|
234
|
-
forEach(entries(fieldRefs.current), ([fieldKey, fieldElement]) => {
|
|
235
|
-
if (fieldKey.startsWith(`${currentRowId}_`)) {
|
|
236
|
-
relevantFieldRefs[fieldKey] = fieldElement;
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
const validationResult = validateEditingFields(relevantFieldRefs, currentRowId);
|
|
240
|
-
if (!validationResult.isValid) {
|
|
241
|
-
updateValidationErrors(validationResult, relevantFieldRefs, validationState);
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
270
|
const result = await handleServerOperation({
|
|
245
271
|
isNewRow,
|
|
246
272
|
rowId,
|
|
@@ -315,19 +341,23 @@ const useTableEdit = ({
|
|
|
315
341
|
const saveEditing = useCallback(async () => {
|
|
316
342
|
const isNewRow = editMode === "inline" ? isAddingNewRow : false;
|
|
317
343
|
const rowId = editingRowId || (isNewRow ? "new-row" : null);
|
|
318
|
-
const
|
|
319
|
-
const success = await saveEditingInternal(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
344
|
+
const isNewRowSession = usesSeparateNewRowSession ? !editingRowId : isAddingNewRow;
|
|
345
|
+
const success = await saveEditingInternal(
|
|
346
|
+
rowId,
|
|
347
|
+
isNewRow,
|
|
348
|
+
dataRefFor(isNewRowSession),
|
|
349
|
+
formFor(isNewRowSession),
|
|
350
|
+
() => {
|
|
351
|
+
setEditingRowId(null);
|
|
352
|
+
if (editMode === "inline") {
|
|
353
|
+
setIsAddingNewRow(false);
|
|
354
|
+
editingRowDataRef.current = {};
|
|
355
|
+
}
|
|
356
|
+
resetEditingData();
|
|
357
|
+
resetValidationState(isNewRowSession);
|
|
358
|
+
incrementSessionKey();
|
|
324
359
|
}
|
|
325
|
-
|
|
326
|
-
resetValidationState(
|
|
327
|
-
editMode === "quickedit" ? editingRowId ? "editing" : "new-row" : void 0
|
|
328
|
-
);
|
|
329
|
-
incrementSessionKey();
|
|
330
|
-
});
|
|
360
|
+
);
|
|
331
361
|
return success;
|
|
332
362
|
}, [
|
|
333
363
|
editMode,
|
|
@@ -338,7 +368,10 @@ const useTableEdit = ({
|
|
|
338
368
|
setEditingRowId,
|
|
339
369
|
setIsAddingNewRow,
|
|
340
370
|
incrementSessionKey,
|
|
341
|
-
saveEditingInternal
|
|
371
|
+
saveEditingInternal,
|
|
372
|
+
formFor,
|
|
373
|
+
dataRefFor,
|
|
374
|
+
usesSeparateNewRowSession
|
|
342
375
|
]);
|
|
343
376
|
const updateFieldValue = useCallback(
|
|
344
377
|
(fieldName, newValue, rowId) => {
|
|
@@ -361,26 +394,22 @@ const useTableEdit = ({
|
|
|
361
394
|
newRowDataRef.current = __spreadProps(__spreadValues({}, newRowDataRef.current), {
|
|
362
395
|
[fieldName]: newValue
|
|
363
396
|
});
|
|
364
|
-
const column = find(wmTableColumns, (col) => col.field === fieldName);
|
|
365
|
-
validateField("new-row", fieldName, newValue, column, validationState);
|
|
366
397
|
} else {
|
|
367
398
|
editingRowDataRef.current = __spreadProps(__spreadValues({}, editingRowDataRef.current), {
|
|
368
399
|
[fieldName]: newValue
|
|
369
400
|
});
|
|
370
|
-
const column = find(wmTableColumns, (col) => col.field === fieldName);
|
|
371
|
-
validateField(effectiveRowId, fieldName, newValue, column, validationState);
|
|
372
401
|
}
|
|
373
402
|
},
|
|
374
|
-
[editMode, editingRowId,
|
|
403
|
+
[editMode, editingRowId, cellState, setInternalDataset]
|
|
375
404
|
);
|
|
376
405
|
const saveNewRow = useCallback(() => {
|
|
377
|
-
const success = saveEditingInternal(null, true, newRowDataRef, () => {
|
|
406
|
+
const success = saveEditingInternal(null, true, newRowDataRef, formFor(true), () => {
|
|
378
407
|
newRowDataRef.current = {};
|
|
379
|
-
resetValidationState(
|
|
408
|
+
resetValidationState(true);
|
|
380
409
|
incrementSessionKey();
|
|
381
410
|
});
|
|
382
411
|
return success;
|
|
383
|
-
}, [resetValidationState, incrementSessionKey, saveEditingInternal]);
|
|
412
|
+
}, [resetValidationState, incrementSessionKey, saveEditingInternal, formFor]);
|
|
384
413
|
const handleKeyDown = useCallback(
|
|
385
414
|
(e, sourceRowId) => {
|
|
386
415
|
if (editMode === "inline" && e.key === "Enter" && !e.shiftKey) {
|
|
@@ -407,7 +436,7 @@ const useTableEdit = ({
|
|
|
407
436
|
e.stopPropagation();
|
|
408
437
|
if (isFromNewRow) {
|
|
409
438
|
newRowDataRef.current = {};
|
|
410
|
-
resetValidationState(
|
|
439
|
+
resetValidationState(true);
|
|
411
440
|
incrementSessionKey();
|
|
412
441
|
} else if (isEditingRow) {
|
|
413
442
|
cancelEditing();
|
|
@@ -441,8 +470,9 @@ const useTableEdit = ({
|
|
|
441
470
|
fieldName,
|
|
442
471
|
widgetType,
|
|
443
472
|
editValue,
|
|
444
|
-
|
|
445
|
-
|
|
473
|
+
formRef: formFor(false).formRef,
|
|
474
|
+
cellErrorsRef,
|
|
475
|
+
columnValidatorsRef,
|
|
446
476
|
fieldRefs,
|
|
447
477
|
renderFormWidget,
|
|
448
478
|
updateFieldValue,
|
|
@@ -466,17 +496,22 @@ const useTableEdit = ({
|
|
|
466
496
|
updateFieldValue,
|
|
467
497
|
sessionKey,
|
|
468
498
|
handleKeyDown,
|
|
469
|
-
config.hasKeyboardNavigation
|
|
499
|
+
config.hasKeyboardNavigation,
|
|
500
|
+
columnValidatorsRef,
|
|
501
|
+
formFor
|
|
470
502
|
]
|
|
471
503
|
);
|
|
472
504
|
const handleRowClick = useCallback(
|
|
473
505
|
(rowData, rowId) => {
|
|
474
506
|
if (!config.startEditOnRowClick) return;
|
|
475
|
-
|
|
507
|
+
const editingRowHasErrors = !!editingRowId && Object.keys(cellErrorsRef.current).some(
|
|
508
|
+
(fieldKey) => fieldKey.startsWith(`${editingRowId}_`)
|
|
509
|
+
);
|
|
510
|
+
if (!isRowEditing(rowId) && !editingRowHasErrors) {
|
|
476
511
|
startEditing(rowData, rowId);
|
|
477
512
|
}
|
|
478
513
|
},
|
|
479
|
-
[config.startEditOnRowClick, isRowEditing, startEditing]
|
|
514
|
+
[config.startEditOnRowClick, isRowEditing, startEditing, editingRowId]
|
|
480
515
|
);
|
|
481
516
|
const handleAddNewRowClick = useCallback(() => {
|
|
482
517
|
if (editMode !== "inline") {
|
|
@@ -515,7 +550,8 @@ const useTableEdit = ({
|
|
|
515
550
|
const renderAddNewRow = useCallback(() => {
|
|
516
551
|
const shouldShowNewRow = editMode === "inline" && isAddingNewRow || editMode === "quickedit" && config.showNewRowFormByDefault;
|
|
517
552
|
if (!shouldShowNewRow) return null;
|
|
518
|
-
const dataRef =
|
|
553
|
+
const dataRef = dataRefFor(true);
|
|
554
|
+
const newRowFormRef = formFor(true).formRef;
|
|
519
555
|
const onKeyDownHandler = config.hasKeyboardNavigation ? (e) => handleKeyDown(e, "new-row") : void 0;
|
|
520
556
|
const renderCell = (column, fieldName, widgetType, editValue, rowId) => {
|
|
521
557
|
return /* @__PURE__ */ jsx(
|
|
@@ -527,8 +563,9 @@ const useTableEdit = ({
|
|
|
527
563
|
fieldName,
|
|
528
564
|
widgetType,
|
|
529
565
|
editValue,
|
|
530
|
-
|
|
531
|
-
|
|
566
|
+
formRef: newRowFormRef,
|
|
567
|
+
cellErrorsRef,
|
|
568
|
+
columnValidatorsRef,
|
|
532
569
|
fieldRefs,
|
|
533
570
|
renderFormWidget,
|
|
534
571
|
updateFieldValue,
|
|
@@ -557,7 +594,7 @@ const useTableEdit = ({
|
|
|
557
594
|
handleCancel: () => {
|
|
558
595
|
if (editMode === "quickedit") {
|
|
559
596
|
newRowDataRef.current = {};
|
|
560
|
-
resetValidationState(
|
|
597
|
+
resetValidationState(true);
|
|
561
598
|
incrementSessionKey();
|
|
562
599
|
} else {
|
|
563
600
|
cancelEditing();
|
|
@@ -588,7 +625,10 @@ const useTableEdit = ({
|
|
|
588
625
|
incrementSessionKey,
|
|
589
626
|
sessionKey,
|
|
590
627
|
hasRowExpansion,
|
|
591
|
-
expansionPosition
|
|
628
|
+
expansionPosition,
|
|
629
|
+
columnValidatorsRef,
|
|
630
|
+
formFor,
|
|
631
|
+
dataRefFor
|
|
592
632
|
]);
|
|
593
633
|
return {
|
|
594
634
|
editingRowId,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Control, FieldValues, UseFormTrigger } from "react-hook-form";
|
|
2
|
+
export interface TableEditFormRef {
|
|
3
|
+
control: Control<FieldValues>;
|
|
4
|
+
trigger: UseFormTrigger<FieldValues>;
|
|
5
|
+
name?: string;
|
|
6
|
+
validationtype: "default";
|
|
7
|
+
}
|
|
8
|
+
export interface TableEditForm {
|
|
9
|
+
/** Spread onto every edit widget of the session this form backs. */
|
|
10
|
+
formRef: TableEditFormRef;
|
|
11
|
+
/** Validate every registered cell; resolves false when any rule fails. */
|
|
12
|
+
validate: () => Promise<boolean>;
|
|
13
|
+
/** Which of fieldNames have errors. */
|
|
14
|
+
getInvalidFields: (fieldNames: string[]) => string[];
|
|
15
|
+
/** Update field value without re-validating. */
|
|
16
|
+
setFieldValue: (fieldName: string, value: unknown) => void;
|
|
17
|
+
/** Set error from browser validity. */
|
|
18
|
+
setFieldError: (fieldName: string, message: string) => void;
|
|
19
|
+
/** Clear all values and errors. */
|
|
20
|
+
reset: () => void;
|
|
21
|
+
}
|
|
22
|
+
export declare const useTableEditForms: (tableName?: string) => {
|
|
23
|
+
editForm: TableEditForm;
|
|
24
|
+
newRowForm: TableEditForm;
|
|
25
|
+
};
|