@wavemaker-ai/react-runtime 1.0.0-rc.647684 → 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.
Files changed (215) hide show
  1. package/components/advanced/carousel/index.d.ts +1 -0
  2. package/components/advanced/carousel/template.d.ts +1 -0
  3. package/components/advanced/login/index.d.ts +1 -0
  4. package/components/advanced/marquee/index.d.ts +1 -0
  5. package/components/basic/anchor/index.d.ts +1 -0
  6. package/components/basic/anchor/index.js +3 -5
  7. package/components/basic/audio/index.d.ts +1 -0
  8. package/components/basic/html/index.d.ts +1 -0
  9. package/components/basic/icon/index.d.ts +1 -0
  10. package/components/basic/iframe/index.d.ts +1 -0
  11. package/components/basic/label/index.d.ts +1 -0
  12. package/components/basic/picture/index.d.ts +1 -0
  13. package/components/basic/progress-bar/index.d.ts +1 -0
  14. package/components/basic/progress-circle/index.d.ts +1 -0
  15. package/components/basic/richtexteditor/index.d.ts +3 -0
  16. package/components/basic/search/index.js +5 -4
  17. package/components/basic/search/utils.d.ts +1 -0
  18. package/components/basic/search/utils.js +9 -0
  19. package/components/basic/spinner/index.d.ts +1 -0
  20. package/components/basic/tile/index.d.ts +1 -0
  21. package/components/basic/tree/index.d.ts +1 -0
  22. package/components/basic/video/index.d.ts +1 -0
  23. package/components/chart/index.d.ts +1 -0
  24. package/components/common/index.d.ts +2 -0
  25. package/components/common/index.js +2 -0
  26. package/components/common/partial-content-wrapper.d.ts +15 -0
  27. package/components/common/partial-content-wrapper.js +11 -0
  28. package/components/container/accordion/accordion-pane/index.d.ts +1 -0
  29. package/components/container/accordion/index.d.ts +1 -0
  30. package/components/container/index.d.ts +4 -0
  31. package/components/container/index.js +38 -11
  32. package/components/container/layout-grid/grid-column/index.d.ts +1 -0
  33. package/components/container/layout-grid/grid-row/index.d.ts +1 -0
  34. package/components/container/layout-grid/index.d.ts +1 -0
  35. package/components/container/layout-grid/index.js +1 -1
  36. package/components/container/linear-layout/index.d.ts +1 -0
  37. package/components/container/linear-layout/linear-layout-item/index.d.ts +1 -0
  38. package/components/container/panel/index.d.ts +1 -0
  39. package/components/container/props.d.ts +1 -0
  40. package/components/container/repeat-template/index.d.ts +1 -0
  41. package/components/container/repeat-template/index.js +1 -1
  42. package/components/container/tabs/index.d.ts +1 -0
  43. package/components/container/tabs/index.js +12 -8
  44. package/components/container/tabs/tab-pane/index.d.ts +1 -0
  45. package/components/container/tabs/tab-pane/index.js +2 -2
  46. package/components/container/wizard/index.d.ts +2 -0
  47. package/components/container/wizard/index.js +92 -43
  48. package/components/container/wizard/props.d.ts +2 -2
  49. package/components/container/wizard/wizard-step/index.d.ts +1 -0
  50. package/components/data/card/card-actions/index.d.ts +1 -0
  51. package/components/data/card/card-content/index.d.ts +1 -0
  52. package/components/data/card/card-footer/index.d.ts +1 -0
  53. package/components/data/card/index.d.ts +1 -0
  54. package/components/data/form/base-form/hooks/useFormOperations.d.ts +1 -1
  55. package/components/data/form/base-form/hooks/useFormOperations.js +5 -2
  56. package/components/data/form/base-form/index.js +10 -14
  57. package/components/data/form/base-form/props.d.ts +1 -0
  58. package/components/data/form/dynamic-fields/WmxDynamicFormField.js +9 -23
  59. package/components/data/form/form-action/index.d.ts +2 -0
  60. package/components/data/form/form-controller/props.d.ts +2 -0
  61. package/components/data/form/form-controller/utils.js +3 -2
  62. package/components/data/form/form-controller/validation-contrustor.js +112 -1
  63. package/components/data/form/form-controller/withFormController.js +16 -3
  64. package/components/data/form/form-field/index.js +38 -11
  65. package/components/data/form/form-field/props.d.ts +8 -2
  66. package/components/data/list/components/ListItemWithTemplate.js +1 -1
  67. package/components/data/list/index.d.ts +2 -0
  68. package/components/data/list/index.js +5 -10
  69. package/components/data/pagination/hooks/usePagination.js +6 -6
  70. package/components/data/table/components/EditableCell.js +47 -24
  71. package/components/data/table/components/FieldValidationError.js +1 -1
  72. package/components/data/table/components/RowExpansionButton.js +5 -1
  73. package/components/data/table/components/TableBody.js +2 -0
  74. package/components/data/table/components/TableHeader.js +12 -4
  75. package/components/data/table/hooks/useFormWidget.js +13 -2
  76. package/components/data/table/hooks/usePanelStructure.js +6 -1
  77. package/components/data/table/hooks/useResponsiveColumns.d.ts +1 -0
  78. package/components/data/table/hooks/useRowHandlers.js +1 -1
  79. package/components/data/table/hooks/useRowSelection.js +65 -4
  80. package/components/data/table/hooks/useTableData.js +10 -2
  81. package/components/data/table/hooks/useTableEdit.d.ts +1 -1
  82. package/components/data/table/hooks/useTableEdit.js +115 -75
  83. package/components/data/table/hooks/useTableEditForms.d.ts +25 -0
  84. package/components/data/table/hooks/useTableEditForms.js +30 -0
  85. package/components/data/table/index.d.ts +2 -0
  86. package/components/data/table/index.js +82 -45
  87. package/components/data/table/live-table/index.d.ts +1 -0
  88. package/components/data/table/props.d.ts +43 -23
  89. package/components/data/table/table-action/index.d.ts +1 -0
  90. package/components/data/table/table-column/index.d.ts +1 -0
  91. package/components/data/table/table-row/index.d.ts +1 -0
  92. package/components/data/table/table-row-action/index.d.ts +1 -0
  93. package/components/data/table/utils/buildSelectionColumns.js +19 -24
  94. package/components/data/table/utils/columnProxy.d.ts +7 -1
  95. package/components/data/table/utils/columnProxy.js +21 -1
  96. package/components/data/table/utils/columnValidation.d.ts +19 -0
  97. package/components/data/table/utils/columnValidation.js +76 -0
  98. package/components/data/table/utils/constants.d.ts +2 -0
  99. package/components/data/table/utils/constants.js +2 -0
  100. package/components/data/table/utils/crud-handlers.js +3 -1
  101. package/components/data/table/utils/expansionColumn.js +1 -1
  102. package/components/data/table/utils/index.d.ts +14 -15
  103. package/components/data/table/utils/index.js +18 -85
  104. package/components/data/table/utils/selectionUtils.d.ts +12 -0
  105. package/components/data/table/utils/selectionUtils.js +23 -5
  106. package/components/dialogs/alert-dialog/index.d.ts +2 -0
  107. package/components/dialogs/confirm-dialog/index.d.ts +2 -0
  108. package/components/dialogs/dialog/index.d.ts +2 -0
  109. package/components/dialogs/dialog-actions/index.d.ts +1 -0
  110. package/components/dialogs/dialog-body/index.d.ts +1 -0
  111. package/components/dialogs/dialog-content/index.d.ts +1 -0
  112. package/components/dialogs/dialog-header/index.d.ts +1 -0
  113. package/components/dialogs/iframe-dialog/index.d.ts +2 -0
  114. package/components/dialogs/login-dialog/index.d.ts +2 -0
  115. package/components/dialogs/page-dialog/index.d.ts +2 -0
  116. package/components/dialogs/withDialogWrapper.js +12 -2
  117. package/components/form/button/index.d.ts +1 -0
  118. package/components/form/button-group/index.d.ts +1 -0
  119. package/components/input/calendar/index.d.ts +2 -0
  120. package/components/input/chips/index.d.ts +3 -0
  121. package/components/input/color-picker/index.d.ts +3 -0
  122. package/components/input/composite/index.d.ts +1 -0
  123. package/components/input/composite/index.js +32 -6
  124. package/components/input/currency/index.d.ts +1 -0
  125. package/components/input/default/checkbox/index.d.ts +3 -0
  126. package/components/input/default/checkboxset/index.d.ts +3 -0
  127. package/components/input/default/radioset/index.d.ts +3 -0
  128. package/components/input/default/switch/index.d.ts +3 -0
  129. package/components/input/epoch/date/index.d.ts +1 -0
  130. package/components/input/epoch/datetime/index.d.ts +1 -0
  131. package/components/input/epoch/datetime/index.js +13 -3
  132. package/components/input/epoch/time/index.d.ts +1 -0
  133. package/components/input/fileupload/index.d.ts +1 -0
  134. package/components/input/fileupload/index.js +2 -1
  135. package/components/input/number/index.d.ts +3 -0
  136. package/components/input/rating/index.d.ts +3 -0
  137. package/components/input/select/index.d.ts +1 -0
  138. package/components/input/slider/index.d.ts +3 -0
  139. package/components/input/text/index.d.ts +3 -0
  140. package/components/input/text/index.js +9 -2
  141. package/components/input/textarea/index.d.ts +3 -0
  142. package/components/input/upload/index.d.ts +3 -0
  143. package/components/layout/footer/index.js +4 -2
  144. package/components/layout/footer/props.d.ts +1 -0
  145. package/components/layout/header/index.js +29 -3
  146. package/components/layout/header/props.d.ts +1 -0
  147. package/components/layout/leftnav/index.js +22 -6
  148. package/components/layout/leftnav/props.d.ts +1 -0
  149. package/components/layout/leftnav/utils/page-class-util.d.ts +1 -0
  150. package/components/layout/leftnav/utils/page-class-util.js +4 -3
  151. package/components/layout/rightnav/index.js +4 -2
  152. package/components/layout/rightnav/props.d.ts +1 -0
  153. package/components/layout/topnav/index.js +4 -2
  154. package/components/layout/topnav/props.d.ts +1 -0
  155. package/components/navbar/index.d.ts +1 -0
  156. package/components/navbar/index.js +32 -7
  157. package/components/navbar/nav-item/index.d.ts +1 -0
  158. package/components/navigation/breadcrumb/index.d.ts +1 -0
  159. package/components/navigation/menu/index.d.ts +1 -0
  160. package/components/navigation/menu/index.js +19 -11
  161. package/components/navigation/menu/props.d.ts +1 -0
  162. package/components/navigation/popover/index.d.ts +1 -0
  163. package/components/navigation/popover/index.js +15 -3
  164. package/components/page/index.js +60 -19
  165. package/components/page/page-context.d.ts +4 -0
  166. package/components/page/partial/index.d.ts +1 -0
  167. package/context/PopoverContext.d.ts +5 -0
  168. package/context/PopoverContext.js +5 -0
  169. package/context/WidgetProvider.js +9 -2
  170. package/core/proxy-service.d.ts +2 -0
  171. package/core/proxy-service.js +132 -22
  172. package/higherOrder/BaseApp.js +3 -2
  173. package/higherOrder/BasePage.js +4 -10
  174. package/higherOrder/props.d.ts +1 -0
  175. package/higherOrder/useActivePageReactivity.js +1 -4
  176. package/higherOrder/withBaseWrapper.js +12 -3
  177. package/hooks/useHttp.d.ts +14 -4
  178. package/hooks/useHttp.js +47 -9
  179. package/package-lock.json +332 -325
  180. package/package.json +3 -3
  181. package/runtime-dynamic/App.js +21 -2
  182. package/runtime-dynamic/app-initializer.js +8 -1
  183. package/runtime-dynamic/components/PageWrapper.js +1 -1
  184. package/runtime-dynamic/components/PrefabPreview.js +1 -1
  185. package/runtime-dynamic/components/partial-content.js +17 -1
  186. package/runtime-dynamic/factories/build-base-page-like-component.js +11 -7
  187. package/runtime-dynamic/factories/dynamic-component.js +5 -10
  188. package/runtime-dynamic/main.d.ts +1 -0
  189. package/runtime-dynamic/main.js +9 -1
  190. package/runtime-dynamic/routes.js +17 -8
  191. package/runtime-dynamic/services/compile-render.js +7 -0
  192. package/runtime-dynamic/services/css-scoping.d.ts +1 -1
  193. package/runtime-dynamic/services/css-scoping.js +2 -2
  194. package/runtime-dynamic/services/variable-factory.js +2 -1
  195. package/runtime-dynamic/services/variable-transpiler.js +4 -1
  196. package/runtime-dynamic/wmx-shared.d.ts +40 -0
  197. package/runtime-dynamic/wmx-shared.js +33 -0
  198. package/utils/page-params-util.js +22 -1
  199. package/utils/partial-host-util.d.ts +40 -0
  200. package/utils/partial-host-util.js +41 -0
  201. package/utils/style-utils.js +1 -1
  202. package/utils/wmx.utils.d.ts +0 -1
  203. package/utils/wmx.utils.js +0 -25
  204. package/variables/crud-variable.d.ts +10 -0
  205. package/variables/crud-variable.js +53 -2
  206. package/variables/live-variable.d.ts +1 -0
  207. package/variables/live-variable.js +2 -1
  208. package/variables/model-variable.d.ts +2 -0
  209. package/variables/model-variable.js +14 -0
  210. package/variables/operation-params.d.ts +29 -0
  211. package/variables/operation-params.js +69 -0
  212. package/variables/service-variable.d.ts +9 -0
  213. package/variables/service-variable.js +21 -2
  214. package/components/data/table/utils/validation.d.ts +0 -13
  215. package/components/data/table/utils/validation.js +0 -82
@@ -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, {
@@ -147,6 +148,8 @@ const ExpansionHeaderCell = memo(
147
148
  rowSpan,
148
149
  style: __spreadValues({
149
150
  width: columnwidth || "50px",
151
+ minWidth: columnwidth || "50px",
152
+ maxWidth: columnwidth || "50px",
150
153
  padding: "8px",
151
154
  userSelect: "none"
152
155
  }, rest.style)
@@ -167,6 +170,7 @@ const TableHeaderComponent = memo(
167
170
  onColumnFilterChange,
168
171
  renderFormWidget,
169
172
  listener,
173
+ viewParent,
170
174
  tableStructure,
171
175
  onColumnSelect,
172
176
  onColumnDeselect,
@@ -330,7 +334,7 @@ const TableHeaderComponent = memo(
330
334
  role: "columnheader",
331
335
  "data-col-id": headerIndex,
332
336
  tabIndex: 0,
333
- "data-col-field": header.column.id,
337
+ "data-col-field": "row-expansion",
334
338
  columnwidth: rowExpansionConfig == null ? void 0 : rowExpansionConfig.columnwidth
335
339
  },
336
340
  `expansion-header-${headerIndex}`
@@ -340,7 +344,7 @@ const TableHeaderComponent = memo(
340
344
  const columnId = header.column.id;
341
345
  const isSystemColumn = columnId === "multiSelect" || columnId === "radioSelect" || columnId === "row-index" || columnId === "rowIndex" || columnId === "actions";
342
346
  const handleHeaderClick = (event) => {
343
- var _a2, _b2;
347
+ var _a2;
344
348
  if (onHeaderclick) {
345
349
  const colDef = header.column.columnDef;
346
350
  const columnData = {
@@ -348,7 +352,11 @@ const TableHeaderComponent = memo(
348
352
  caption: colDef.header || columnId,
349
353
  colDef
350
354
  };
351
- onHeaderclick(event, (_a2 = listener == null ? void 0 : listener.Widgets) == null ? void 0 : _a2[tableName || ""], columnData);
355
+ onHeaderclick(
356
+ event,
357
+ getWidgetForCallback(listener, viewParent, tableName || ""),
358
+ columnData
359
+ );
352
360
  }
353
361
  if (enablecolumnselection && !isSystemColumn) {
354
362
  const target = event.currentTarget;
@@ -374,7 +382,7 @@ const TableHeaderComponent = memo(
374
382
  return;
375
383
  }
376
384
  if (enablesort && header.column.getCanSort()) {
377
- (_b2 = header.column.getToggleSortingHandler()) == null ? void 0 : _b2(event);
385
+ (_a2 = header.column.getToggleSortingHandler()) == null ? void 0 : _a2(event);
378
386
  }
379
387
  };
380
388
  const isClickable = enablecolumnselection && !isSystemColumn || enablesort && header.column.getCanSort();
@@ -62,10 +62,21 @@ const useFormWidget = ({ listener }) => {
62
62
  const sessionSuffix = (widgetProps == null ? void 0 : widgetProps.sessionKey) ? `_${widgetProps.sessionKey}` : "";
63
63
  const uniqueName = `edit_${fieldName}${sessionSuffix}`;
64
64
  const disabled = getDisabledValue(widgetProps == null ? void 0 : widgetProps.disabled, widgetProps == null ? void 0 : widgetProps.rowData);
65
- const commonProps = __spreadValues({
65
+ const commonProps = __spreadProps(__spreadValues({}, widgetProps == null ? void 0 : widgetProps.column), {
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,
@@ -84,7 +95,7 @@ const useFormWidget = ({ listener }) => {
84
95
  column: widgetProps == null ? void 0 : widgetProps.column,
85
96
  updateon: "blur",
86
97
  regexp: ((_a = widgetProps == null ? void 0 : widgetProps.column) == null ? void 0 : _a.regexp) || ".*"
87
- }, widgetProps == null ? void 0 : widgetProps.column);
98
+ });
88
99
  const wrapEventHandler = (handler) => {
89
100
  return (event) => {
90
101
  handler(event, {}, widgetProps == null ? void 0 : widgetProps.rowData);
@@ -39,9 +39,14 @@ const usePanelStructure = ({
39
39
  showrecordcount
40
40
  ]
41
41
  );
42
+ const showFooter = useMemo(
43
+ () => showPagination || footerActions.length > 0,
44
+ [showPagination, footerActions]
45
+ );
42
46
  return {
43
47
  showPanelHeading,
44
- showPagination
48
+ showPagination,
49
+ showFooter
45
50
  };
46
51
  };
47
52
  export {
@@ -52,6 +52,7 @@ export declare const useResponsiveColumns: (columns: WmTableColumnProps[]) => {
52
52
  padding?: string;
53
53
  margin?: string;
54
54
  height?: number | string;
55
+ class?: string;
55
56
  conditionalstyle?: import("react").CSSProperties;
56
57
  conditionalclass?: string;
57
58
  showindevice?: ("xs" | "sm" | "md" | "lg" | "all")[];
@@ -35,6 +35,7 @@ const useRowHandlers = ({
35
35
  const handleRowClick = useCallback(
36
36
  (event, rowData, rowId) => {
37
37
  const hasSelectionMode = useRadioSelect || useMultiSelect;
38
+ invokeRowClick(event, rowData);
38
39
  let isSelectionHandled = false;
39
40
  if (hasSelectionMode && isrowselectable) {
40
41
  isSelectionHandled = handleRowSelectionClick(event, rowId, rowData);
@@ -47,7 +48,6 @@ const useRowHandlers = ({
47
48
  return;
48
49
  }
49
50
  handleRowActiveClick(rowId, isSelectionHandled, isEditingOrAdding);
50
- invokeRowClick(event, rowData);
51
51
  },
52
52
  [
53
53
  editingRowId,
@@ -1,6 +1,8 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import isEqual from "lodash-es/isEqual";
2
3
  import {
3
4
  isInteractiveElement,
5
+ isSelectionCheckboxRenamed,
4
6
  getRowIdsFromDataset,
5
7
  rowExistsInDataset,
6
8
  selectionStateHelpers,
@@ -11,6 +13,36 @@ import {
11
13
  import { saveTableState, getTableState } from "../utils/table-helpers";
12
14
  import { cleanRowData, UNSUPPORTED_STATE_PERSISTENCE_TYPES } from "../utils";
13
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
+ };
14
46
  const getSelectedIndices = (selectedIds, dataset) => selectedIds.reduce((indices, rowId) => {
15
47
  const index = dataset.findIndex((row) => row._wmTableRowId === rowId || row.id === rowId);
16
48
  if (index >= 0) {
@@ -217,10 +249,13 @@ const useRowSelection = ({
217
249
  }
218
250
  if (useMultiSelect) {
219
251
  const isCurrentlySelected = selectionStateHelpers.isRowSelected(cellState, rowId);
220
- const willBeSelected = !isCurrentlySelected;
221
- selectionStateHelpers.toggleSelection(cellState, rowId);
252
+ const isToggled = !isSelectionCheckboxRenamed(event);
253
+ if (isToggled) {
254
+ selectionStateHelpers.toggleSelection(cellState, rowId);
255
+ }
256
+ const isSelected = isToggled ? !isCurrentlySelected : isCurrentlySelected;
222
257
  commitSelectionChange();
223
- triggerSelectionCallback(event, rowData, willBeSelected ? onRowselect : onRowdeselect);
258
+ triggerSelectionCallback(event, rowData, isSelected ? onRowselect : onRowdeselect);
224
259
  return true;
225
260
  }
226
261
  handleRadioSelection(rowId, rowData);
@@ -350,6 +385,30 @@ const useRowSelection = ({
350
385
  useMultiSelect,
351
386
  applySelectionForMode
352
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
+ );
353
412
  return {
354
413
  selectedRowIds,
355
414
  useMultiSelect,
@@ -359,7 +418,9 @@ const useRowSelection = ({
359
418
  handleSelectAll,
360
419
  handleRowSelectionClick,
361
420
  isRowSelected,
362
- isInteractiveElement
421
+ isInteractiveElement,
422
+ selectItem,
423
+ deselectItem
363
424
  };
364
425
  };
365
426
  export {
@@ -59,8 +59,13 @@ const useTableData = ({
59
59
  widgetName
60
60
  }) => {
61
61
  const rowIdCacheRef = useRef([]);
62
+ const rawDatasetRef = useRef(void 0);
63
+ const processedDatasetRef = useRef([]);
62
64
  const processedDataset = useMemo(() => {
63
65
  const extracted = extractDataArray(dataset);
66
+ if (isEqual(rawDatasetRef.current, extracted)) {
67
+ return processedDatasetRef.current;
68
+ }
64
69
  const cachedIds = rowIdCacheRef.current;
65
70
  const prestamped = extracted.map((item, i) => {
66
71
  if (!item || typeof item !== "object" || item._wmTableRowId || item.id || item.ID || item.Id || !cachedIds[i]) {
@@ -70,8 +75,11 @@ const useTableData = ({
70
75
  });
71
76
  const withIds = addUniqueRowIds(prestamped);
72
77
  rowIdCacheRef.current = withIds.map((r) => r == null ? void 0 : r._wmTableRowId);
73
- return removeEmptyRecords(withIds);
74
- }, [dataset]);
78
+ const result = removeEmptyRecords(withIds);
79
+ rawDatasetRef.current = extracted;
80
+ processedDatasetRef.current = result;
81
+ return result;
82
+ }, [dataset, dataset == null ? void 0 : dataset.length]);
75
83
  const [internalDataset, setInternalDataset] = useState(processedDataset);
76
84
  useEffect(() => {
77
85
  if (!isEqual(internalDataset, processedDataset)) {
@@ -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 { find, forEach, entries, get, every } from "lodash-es";
22
+ import { get } from "lodash-es";
23
23
  import { EditableCell } from "../components";
24
24
  import { AddNewRow } from "../components/AddNewRow";
25
- import { validateEditingFields, TABLE_MESSAGES, INTERNAL_PROPERTIES } from "../utils";
25
+ import { TABLE_MESSAGES, INTERNAL_PROPERTIES } from "../utils";
26
26
  import { useEditingState } from "./useEditingState";
27
27
  import {
28
- validateField,
29
- resetValidationState as resetValidation,
30
- updateValidationErrors
31
- } from "../utils/validation";
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 fieldValidationErrorsRef = useRef({});
122
- const cellUpdateCallbacksRef = useRef({});
138
+ const cellErrorsRef = useRef({});
123
139
  const originalRowDataRef = useRef({});
124
- const validationState = {
125
- fieldRefs,
126
- fieldValidationErrors: fieldValidationErrorsRef,
127
- cellUpdateCallbacks: cellUpdateCallbacksRef
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
- (context) => {
132
- resetValidation(context, editingRowId, validationState);
152
+ (isNewRowSession = false) => {
153
+ formFor(isNewRowSession).reset();
133
154
  },
134
- [editingRowId]
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(editMode === "quickedit" ? "all" : void 0);
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(editMode === "quickedit" ? "all" : void 0);
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 editableCellContainer = activeElement.closest("[data-field-name]");
200
- if (editableCellContainer) {
201
- const fieldName = editableCellContainer.getAttribute("data-field-name");
202
- const inputValue = activeElement.value;
203
- if (fieldName && inputValue !== void 0) {
204
- rowDataRef.current = __spreadProps(__spreadValues({}, rowDataRef.current), {
205
- [fieldName]: inputValue
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 dataRef = editMode === "quickedit" && !editingRowId ? newRowDataRef : editingRowDataRef;
319
- const success = await saveEditingInternal(rowId, isNewRow, dataRef, () => {
320
- setEditingRowId(null);
321
- if (editMode === "inline") {
322
- setIsAddingNewRow(false);
323
- editingRowDataRef.current = {};
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
- resetEditingData();
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, wmTableColumns, cellState, setInternalDataset]
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("new-row");
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("new-row");
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
- fieldValidationErrorsRef,
445
- cellUpdateCallbacksRef,
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
- if (!isRowEditing(rowId) && (editMode === "quickedit" || editMode === "inline" ? every(validationState.fieldValidationErrors.current, (error) => error === false) : true)) {
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 = editMode === "quickedit" ? newRowDataRef : editingRowDataRef;
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
- fieldValidationErrorsRef,
531
- cellUpdateCallbacksRef,
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("new-row");
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
+ };