@vuu-ui/vuu-data-editing 5.0.0-alpha.1 → 5.0.0-alpha.3

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 (57) hide show
  1. package/README.md +36 -0
  2. package/package.json +7 -6
  3. package/src/DirectEditSession.js +8 -1
  4. package/src/EditButtons.js +6 -5
  5. package/src/EditModeProvider.js +14 -5
  6. package/src/EditSession.js +37 -6
  7. package/src/edit-control/EditControlCheckbox.js +25 -0
  8. package/src/edit-control/EditControlDropdown.js +34 -0
  9. package/src/edit-control/EditControlLookupDropdown.js +31 -0
  10. package/src/edit-control/get-data-item-edit-control.js +139 -0
  11. package/src/edit-control/index.js +4 -0
  12. package/src/edit-errors.js +7 -0
  13. package/src/edit-field/EditField.js +101 -19
  14. package/src/edit-field/useEditField.js +1 -0
  15. package/src/edit-form/CreateRowForm.js +37 -0
  16. package/src/edit-form/EditForm.css.js +32 -0
  17. package/src/edit-form/EditForm.js +85 -0
  18. package/src/edit-form/EditFormButtons.js +48 -0
  19. package/src/edit-form/EditFormContext.js +4 -0
  20. package/src/edit-form/index.js +4 -0
  21. package/src/form-fields.js +40 -0
  22. package/src/index.js +9 -0
  23. package/src/lookup-values/useLookupOptions.js +106 -0
  24. package/src/useConfirmDiscard.js +34 -0
  25. package/src/useCustomEditField.js +76 -0
  26. package/src/useEditForm.js +306 -0
  27. package/src/useEditSessionState.js +72 -0
  28. package/src/useEditable.js +26 -9
  29. package/src/useEditableColumns.js +105 -0
  30. package/src/useEditableTable.js +21 -8
  31. package/types/DirectEditSession.d.ts +6 -1
  32. package/types/EditButtons.d.ts +20 -3
  33. package/types/EditModeProvider.d.ts +10 -2
  34. package/types/EditSession.d.ts +15 -0
  35. package/types/edit-control/EditControlCheckbox.d.ts +8 -0
  36. package/types/edit-control/EditControlDropdown.d.ts +14 -0
  37. package/types/edit-control/EditControlLookupDropdown.d.ts +13 -0
  38. package/types/edit-control/get-data-item-edit-control.d.ts +40 -0
  39. package/types/edit-control/index.d.ts +4 -0
  40. package/types/edit-errors.d.ts +9 -0
  41. package/types/edit-field/EditField.d.ts +37 -3
  42. package/types/edit-field/useEditField.d.ts +1 -0
  43. package/types/edit-form/CreateRowForm.d.ts +29 -0
  44. package/types/edit-form/EditForm.d.ts +36 -0
  45. package/types/edit-form/EditFormButtons.d.ts +18 -0
  46. package/types/edit-form/EditFormContext.d.ts +59 -0
  47. package/types/edit-form/index.d.ts +4 -0
  48. package/types/form-fields.d.ts +32 -0
  49. package/types/index.d.ts +12 -2
  50. package/types/lookup-values/useLookupOptions.d.ts +35 -0
  51. package/types/useConfirmDiscard.d.ts +23 -0
  52. package/types/useCustomEditField.d.ts +51 -0
  53. package/types/useEditForm.d.ts +127 -0
  54. package/types/useEditSessionState.d.ts +27 -0
  55. package/types/useEditable.d.ts +30 -3
  56. package/types/useEditableColumns.d.ts +79 -0
  57. package/types/useEditableTable.d.ts +18 -3
@@ -0,0 +1,76 @@
1
+ import { isRpcError } from "@vuu-ui/vuu-utils";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { useEditSession } from "./DataEditingProvider.js";
4
+ import { toError } from "./edit-errors.js";
5
+ import { useEditSessionState } from "./useEditSessionState.js";
6
+ const useCustomEditField = ({ editSession: editSessionProp, equals = Object.is, name, onError, originalValue, rowKey, serialize })=>{
7
+ const contextEditSession = useEditSession();
8
+ const editSession = editSessionProp ?? contextEditSession;
9
+ const { inEditMode } = useEditSessionState(editSession);
10
+ const [value, setValueState] = useState(originalValue);
11
+ const [error, setError] = useState();
12
+ const [committing, setCommitting] = useState(false);
13
+ const onErrorRef = useRef(onError);
14
+ onErrorRef.current = onError;
15
+ const latestCommitRef = useRef(0);
16
+ useEffect(()=>{
17
+ if (!inEditMode) {
18
+ setValueState(originalValue);
19
+ setError(void 0);
20
+ }
21
+ }, [
22
+ inEditMode,
23
+ originalValue
24
+ ]);
25
+ const setValue = useCallback(async (nextValue)=>{
26
+ if (!editSession) throw Error("[useCustomEditField] no EditSession, pass editSession or use a DataEditingProvider");
27
+ const original = originalValue ?? nextValue;
28
+ const committedValue = void 0 !== originalValue && equals(nextValue, originalValue) ? originalValue : nextValue;
29
+ const commitId = ++latestCommitRef.current;
30
+ setValueState(committedValue);
31
+ setCommitting(true);
32
+ setError(void 0);
33
+ try {
34
+ const response = await editSession.commit(rowKey, name, original, committedValue, true, serialize ? {
35
+ dataSourceValue: serialize(committedValue)
36
+ } : {});
37
+ if (isRpcError(response) && commitId === latestCommitRef.current) {
38
+ const err = new Error(response.errorMessage);
39
+ setError(err);
40
+ onErrorRef.current?.(err);
41
+ }
42
+ } catch (cause) {
43
+ if (commitId === latestCommitRef.current) {
44
+ const err = toError(cause);
45
+ setError(err);
46
+ if (onErrorRef.current) onErrorRef.current(err);
47
+ else console.error("[useCustomEditField] commit %s failed", name, err);
48
+ }
49
+ } finally{
50
+ if (commitId === latestCommitRef.current) setCommitting(false);
51
+ }
52
+ }, [
53
+ editSession,
54
+ equals,
55
+ name,
56
+ originalValue,
57
+ rowKey,
58
+ serialize
59
+ ]);
60
+ const reset = useCallback(()=>{
61
+ setValueState(originalValue);
62
+ setError(void 0);
63
+ }, [
64
+ originalValue
65
+ ]);
66
+ const isDirty = void 0 !== value && void 0 !== originalValue && !equals(value, originalValue);
67
+ return {
68
+ committing,
69
+ error,
70
+ isDirty,
71
+ reset,
72
+ setValue,
73
+ value
74
+ };
75
+ };
76
+ export { useCustomEditField };
@@ -0,0 +1,306 @@
1
+ import { isRpcError, isRpcSuccess } from "@vuu-ui/vuu-utils";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { EditSession } from "./EditSession.js";
4
+ import { DirectEditSession } from "./DirectEditSession.js";
5
+ import { useEditMode } from "./EditModeProvider.js";
6
+ import { reportEditError, toError } from "./edit-errors.js";
7
+ import { useEditSessionState } from "./useEditSessionState.js";
8
+ const EMPTY_ERRORS = {};
9
+ const NEW_ROW = {
10
+ key: EditSession.newRowKey
11
+ };
12
+ const getDataRowValues = (dataRow)=>{
13
+ const { toJSON } = dataRow;
14
+ const values = "function" == typeof toJSON ? toJSON.call(dataRow) : {
15
+ ...dataRow
16
+ };
17
+ return {
18
+ ...values,
19
+ key: dataRow.key
20
+ };
21
+ };
22
+ const isEmptyValue = (value)=>null == value || "string" == typeof value && "" === value.trim();
23
+ const toRowSnapshot = (key, values)=>({
24
+ ...values,
25
+ key
26
+ });
27
+ class FormDirectEditSession extends DirectEditSession {
28
+ #onCommitted;
29
+ constructor(props, onCommitted){
30
+ super(props);
31
+ this.#onCommitted = onCommitted;
32
+ }
33
+ async commit(key, columnName, originalValue, typedValue, isValid) {
34
+ const response = await super.commit(key, columnName, originalValue, typedValue, isValid);
35
+ if (isRpcSuccess(response)) this.#onCommitted(columnName, typedValue);
36
+ return response;
37
+ }
38
+ }
39
+ const hasErrors = (errors)=>null != errors && Object.keys(errors).length > 0;
40
+ const useEditForm = ({ columns, dataRow, dataSource, deleteMode, editModel = "session", editSession: sharedEditSession, editSessionApi, isEditMode: isEditModeProp, mode = "edit", onCancelled, onError, onSaved, requiredColumns, rowDefaults, validate })=>{
41
+ const { isEditMode: isEditModeContext } = useEditMode();
42
+ const isCreate = "create" === mode;
43
+ const isDirect = !isCreate && "direct" === editModel;
44
+ const isShared = isCreate && void 0 !== sharedEditSession;
45
+ const ownsSession = !isDirect && !isShared;
46
+ const isEditMode = isCreate || (isEditModeProp ?? isEditModeContext);
47
+ const [saving, setSaving] = useState(false);
48
+ const [error, setError] = useState();
49
+ const [validationErrors, setValidationErrors] = useState(EMPTY_ERRORS);
50
+ const savingRef = useRef(false);
51
+ const rowAddedRef = useRef(false);
52
+ const [rowAdded, setRowAdded] = useState(false);
53
+ const markRowAdded = useCallback((added)=>{
54
+ rowAddedRef.current = added;
55
+ setRowAdded(added);
56
+ }, []);
57
+ const callbacksRef = useRef({
58
+ onCancelled,
59
+ onError,
60
+ onSaved,
61
+ validate
62
+ });
63
+ callbacksRef.current = {
64
+ onCancelled,
65
+ onError,
66
+ onSaved,
67
+ validate
68
+ };
69
+ const editSession = useMemo(()=>sharedEditSession ?? new EditSession({
70
+ dataSource,
71
+ deleteMode,
72
+ editSessionApi,
73
+ rowDefaults
74
+ }), [
75
+ dataSource,
76
+ deleteMode,
77
+ editSessionApi,
78
+ rowDefaults,
79
+ sharedEditSession
80
+ ]);
81
+ const [directValues, setDirectValues] = useState({});
82
+ const directRowKey = isDirect ? dataRow?.key : void 0;
83
+ useEffect(()=>{
84
+ setDirectValues({});
85
+ }, [
86
+ directRowKey
87
+ ]);
88
+ const directValuesRef = useRef(directValues);
89
+ directValuesRef.current = directValues;
90
+ const dataRowRef = useRef(dataRow);
91
+ dataRowRef.current = dataRow;
92
+ const requiredRef = useRef(requiredColumns);
93
+ requiredRef.current = requiredColumns;
94
+ const directEditSession = useMemo(()=>isDirect ? new FormDirectEditSession({
95
+ dataSource,
96
+ validate: (_key, columnName, value)=>{
97
+ if (requiredRef.current?.includes(columnName) && isEmptyValue(value)) return "A value is required";
98
+ const { current: row } = dataRowRef;
99
+ const values = {
100
+ ...row ? getDataRowValues(row) : void 0,
101
+ ...directValuesRef.current,
102
+ [columnName]: value
103
+ };
104
+ return callbacksRef.current.validate?.(values)?.[columnName];
105
+ }
106
+ }, (columnName, value)=>setDirectValues((values)=>({
107
+ ...values,
108
+ [columnName]: value
109
+ }))) : void 0, [
110
+ dataSource,
111
+ isDirect
112
+ ]);
113
+ const sessionState = useEditSessionState(editSession);
114
+ const [, setValuesRevision] = useState(0);
115
+ const rowKey = dataRow?.key;
116
+ useEffect(()=>{
117
+ if (isCreate || void 0 === rowKey) return;
118
+ const onCellEditChanged = (key)=>{
119
+ if (key === rowKey) setValuesRevision((n)=>n + 1);
120
+ };
121
+ editSession.on("cellEditChanged", onCellEditChanged);
122
+ return ()=>{
123
+ editSession.removeListener("cellEditChanged", onCellEditChanged);
124
+ };
125
+ }, [
126
+ editSession,
127
+ isCreate,
128
+ rowKey
129
+ ]);
130
+ const fail = useCallback((cause, operation)=>{
131
+ const err = toError(cause);
132
+ setError(err);
133
+ reportEditError("useEditForm", callbacksRef.current.onError, err, operation);
134
+ }, []);
135
+ useEffect(()=>{
136
+ if (isCreate && columns) editSession.configureNewRow(columns, requiredColumns);
137
+ }, [
138
+ columns,
139
+ editSession,
140
+ isCreate,
141
+ requiredColumns
142
+ ]);
143
+ useEffect(()=>{
144
+ setError(void 0);
145
+ setValidationErrors(EMPTY_ERRORS);
146
+ markRowAdded(false);
147
+ if (!ownsSession) return;
148
+ const transition = isEditMode ? editSession.begin(isCreate ? "Empty" : "Selected") : editSession.end();
149
+ transition.catch((cause)=>fail(cause, isEditMode ? "begin" : "cancel"));
150
+ }, [
151
+ editSession,
152
+ fail,
153
+ isCreate,
154
+ isEditMode,
155
+ markRowAdded,
156
+ ownsSession
157
+ ]);
158
+ useEffect(()=>()=>{
159
+ if (isShared) editSession.clearNewRow();
160
+ else if (ownsSession) editSession.end().catch(()=>void 0);
161
+ }, [
162
+ editSession,
163
+ isShared,
164
+ ownsSession
165
+ ]);
166
+ const getValues = useCallback(()=>{
167
+ if (isCreate) return editSession.newRowState.values;
168
+ if (isDirect && dataRow) return {
169
+ ...getDataRowValues(dataRow),
170
+ ...directValuesRef.current
171
+ };
172
+ if (dataRow) return {
173
+ ...getDataRowValues(dataRow),
174
+ ...editSession.getEditedValues(dataRow.key)
175
+ };
176
+ return {};
177
+ }, [
178
+ dataRow,
179
+ editSession,
180
+ isCreate,
181
+ isDirect
182
+ ]);
183
+ const submit = useCallback(async (force = false)=>{
184
+ if (savingRef.current) return false;
185
+ setError(void 0);
186
+ if (isDirect) {
187
+ callbacksRef.current.onSaved?.();
188
+ return true;
189
+ }
190
+ if (!rowAddedRef.current) {
191
+ const errors = callbacksRef.current.validate?.(getValues());
192
+ if (hasErrors(errors)) {
193
+ setValidationErrors(errors);
194
+ return false;
195
+ }
196
+ setValidationErrors(EMPTY_ERRORS);
197
+ }
198
+ savingRef.current = true;
199
+ setSaving(true);
200
+ try {
201
+ if (isCreate && !rowAddedRef.current) {
202
+ const response = await editSession.addNewRow();
203
+ if (isRpcError(response)) {
204
+ setError(new Error(response.errorMessage));
205
+ return false;
206
+ }
207
+ if (hasErrors(editSession.newRowState.errors)) return false;
208
+ if (isShared) {
209
+ callbacksRef.current.onSaved?.();
210
+ return true;
211
+ }
212
+ markRowAdded(true);
213
+ }
214
+ await editSession.end(true, force);
215
+ markRowAdded(false);
216
+ callbacksRef.current.onSaved?.();
217
+ return true;
218
+ } catch (cause) {
219
+ fail(cause, "save");
220
+ return false;
221
+ } finally{
222
+ savingRef.current = false;
223
+ setSaving(false);
224
+ }
225
+ }, [
226
+ editSession,
227
+ fail,
228
+ getValues,
229
+ isCreate,
230
+ isDirect,
231
+ isShared,
232
+ markRowAdded
233
+ ]);
234
+ const cancel = useCallback(async ()=>{
235
+ try {
236
+ if (isShared) editSession.clearNewRow();
237
+ else if (ownsSession) await editSession.end();
238
+ markRowAdded(false);
239
+ setValidationErrors(EMPTY_ERRORS);
240
+ setError(void 0);
241
+ callbacksRef.current.onCancelled?.();
242
+ } catch (cause) {
243
+ fail(cause, "cancel");
244
+ }
245
+ }, [
246
+ editSession,
247
+ fail,
248
+ isShared,
249
+ markRowAdded,
250
+ ownsSession
251
+ ]);
252
+ const setValue = useCallback((name, value)=>{
253
+ editSession.setNewRowValue(name, value);
254
+ setValidationErrors((errors)=>{
255
+ if (void 0 === errors[name]) return errors;
256
+ const { [name]: _removed, ...rest } = errors;
257
+ return rest;
258
+ });
259
+ }, [
260
+ editSession
261
+ ]);
262
+ const { errors: newRowErrors, rowErrorColumn } = sessionState.newRowState;
263
+ const fieldErrors = useMemo(()=>{
264
+ if (!isCreate) return validationErrors;
265
+ const { [rowErrorColumn ?? ""]: _rowError, ...errors } = newRowErrors;
266
+ return {
267
+ ...errors,
268
+ ...validationErrors
269
+ };
270
+ }, [
271
+ isCreate,
272
+ newRowErrors,
273
+ rowErrorColumn,
274
+ validationErrors
275
+ ]);
276
+ const isDirty = isCreate ? Object.keys(sessionState.newRowState.values).length > 0 : isDirect ? false : sessionState.isDirty;
277
+ const formDataRow = useMemo(()=>isCreate ? NEW_ROW : dataRow && Object.keys(directValues).length > 0 ? toRowSnapshot(dataRow.key, {
278
+ ...getDataRowValues(dataRow),
279
+ ...directValues
280
+ }) : dataRow, [
281
+ dataRow,
282
+ directValues,
283
+ isCreate
284
+ ]);
285
+ return {
286
+ ...sessionState,
287
+ canSave: !saving && (isDirect ? isEditMode : isCreate ? rowAdded ? sessionState.inEditMode : sessionState.isActive && editSession.isNewRowComplete() : sessionState.canSave),
288
+ cancel,
289
+ dataRow: formDataRow,
290
+ editModel: isDirect ? "direct" : "session",
291
+ editSession,
292
+ isSharedSession: isShared,
293
+ fieldEditSession: directEditSession ?? editSession,
294
+ error: error ?? sessionState.error,
295
+ fieldErrors,
296
+ getValues,
297
+ isDirty,
298
+ isEditMode,
299
+ mode,
300
+ rowAdded,
301
+ saving,
302
+ setValue,
303
+ submit
304
+ };
305
+ };
306
+ export { getDataRowValues, useEditForm };
@@ -0,0 +1,72 @@
1
+ import { useCallback, useRef, useSyncExternalStore } from "react";
2
+ const IDLE_STATE = {
3
+ canCancel: false,
4
+ canSave: false,
5
+ editState: "clean",
6
+ inEditMode: false,
7
+ isActive: false,
8
+ isDirty: false,
9
+ isEnding: false,
10
+ isStarting: false,
11
+ lifecycle: {
12
+ status: "idle"
13
+ },
14
+ newRowState: {
15
+ columns: [],
16
+ draftRevision: 0,
17
+ errors: {},
18
+ submitting: false,
19
+ values: {}
20
+ }
21
+ };
22
+ const createSnapshot = (editSession)=>{
23
+ const { editState, lifecycle, newRowState } = editSession;
24
+ return {
25
+ canCancel: editSession.canCancel,
26
+ canSave: editSession.canSave,
27
+ editState,
28
+ error: "error" === lifecycle.status ? lifecycle.error : void 0,
29
+ inEditMode: editSession.inEditMode,
30
+ isActive: "active" === lifecycle.status,
31
+ isDirty: "clean" !== editState,
32
+ isEnding: "ending" === lifecycle.status,
33
+ isStarting: "starting" === lifecycle.status,
34
+ lifecycle,
35
+ newRowState
36
+ };
37
+ };
38
+ const useEditSessionState = (editSession)=>{
39
+ const snapshotRef = useRef({
40
+ snapshot: IDLE_STATE
41
+ });
42
+ const subscribe = useCallback((onStoreChange)=>{
43
+ if (!editSession) return ()=>void 0;
44
+ editSession.on("editState", onStoreChange);
45
+ editSession.on("lifecycle", onStoreChange);
46
+ editSession.on("newRow", onStoreChange);
47
+ return ()=>{
48
+ editSession.removeListener("editState", onStoreChange);
49
+ editSession.removeListener("lifecycle", onStoreChange);
50
+ editSession.removeListener("newRow", onStoreChange);
51
+ };
52
+ }, [
53
+ editSession
54
+ ]);
55
+ const getSnapshot = useCallback(()=>{
56
+ if (!editSession) return IDLE_STATE;
57
+ const cached = snapshotRef.current;
58
+ const { editState, lifecycle, newRowState } = editSession;
59
+ if (cached.editSession !== editSession || cached.editState !== editState || cached.lifecycle !== lifecycle || cached.newRowState !== newRowState) snapshotRef.current = {
60
+ editSession,
61
+ editState,
62
+ lifecycle,
63
+ newRowState,
64
+ snapshot: createSnapshot(editSession)
65
+ };
66
+ return snapshotRef.current.snapshot;
67
+ }, [
68
+ editSession
69
+ ]);
70
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
71
+ };
72
+ export { useEditSessionState };
@@ -1,19 +1,31 @@
1
+ import { useCallback, useEffect, useMemo, useRef } from "react";
1
2
  import { EditSession } from "./EditSession.js";
2
3
  import { useEditMode } from "./EditModeProvider.js";
3
- import { useCallback, useEffect, useMemo } from "react";
4
- const useEditable = ({ copyOption = "Selected", dataSource, onCancel, onSave })=>{
5
- const { isEditMode } = useEditMode();
4
+ import { reportEditError } from "./edit-errors.js";
5
+ import { useEditSessionState } from "./useEditSessionState.js";
6
+ const useEditable = ({ copyOption = "Selected", dataSource, deleteMode, editSessionApi, isEditMode: isEditModeProp, onCancel, onError, onSave, rowDefaults })=>{
7
+ const { isEditMode: isEditModeContext } = useEditMode();
8
+ const isEditMode = isEditModeProp ?? isEditModeContext;
9
+ const onErrorRef = useRef(onError);
10
+ onErrorRef.current = onError;
6
11
  const editSession = useMemo(()=>new EditSession({
7
- dataSource
12
+ dataSource,
13
+ deleteMode,
14
+ editSessionApi,
15
+ rowDefaults
8
16
  }), [
9
- dataSource
17
+ dataSource,
18
+ deleteMode,
19
+ editSessionApi,
20
+ rowDefaults
10
21
  ]);
22
+ const sessionState = useEditSessionState(editSession);
11
23
  const handleCancel = useCallback(async ()=>{
12
24
  try {
13
25
  await editSession.end();
14
26
  onCancel();
15
27
  } catch (error) {
16
- console.error("[useEditable] cancel edit session failed", error);
28
+ reportEditError("useEditable", onErrorRef.current, error, "cancel");
17
29
  }
18
30
  }, [
19
31
  editSession,
@@ -24,7 +36,7 @@ const useEditable = ({ copyOption = "Selected", dataSource, onCancel, onSave })=
24
36
  await editSession.end(true, force);
25
37
  onSave();
26
38
  } catch (error) {
27
- console.error("[useEditable] save edit session failed", error);
39
+ reportEditError("useEditable", onErrorRef.current, error, "save");
28
40
  }
29
41
  }, [
30
42
  editSession,
@@ -34,9 +46,9 @@ const useEditable = ({ copyOption = "Selected", dataSource, onCancel, onSave })=
34
46
  const transition = isEditMode ? editSession.begin(copyOption) : editSession.end();
35
47
  transition.catch((error)=>{
36
48
  if (isEditMode) {
37
- console.error("[useEditable] begin edit session failed", error);
49
+ reportEditError("useEditable", onErrorRef.current, error, "begin");
38
50
  handleCancel();
39
- } else console.error("[useEditable] end edit session failed", error);
51
+ } else reportEditError("useEditable", onErrorRef.current, error, "cancel");
40
52
  });
41
53
  }, [
42
54
  copyOption,
@@ -45,7 +57,12 @@ const useEditable = ({ copyOption = "Selected", dataSource, onCancel, onSave })=
45
57
  handleCancel
46
58
  ]);
47
59
  return {
60
+ canCancel: sessionState.canCancel,
61
+ canSave: sessionState.canSave,
48
62
  editSession,
63
+ editState: sessionState.editState,
64
+ isEditMode,
65
+ lifecycle: sessionState.lifecycle,
49
66
  onCancel: handleCancel,
50
67
  onSave: handleSave
51
68
  };
@@ -0,0 +1,105 @@
1
+ import { isDataValueEditable } from "@vuu-ui/vuu-utils";
2
+ import { useMemo } from "react";
3
+ import { UNDO_CELL_RENDERER } from "./UndoCellRenderer.js";
4
+ const DEFAULT_READ_ONLY_COLUMNS = [
5
+ "vuuCreatedTimestamp",
6
+ "vuuUpdatedTimestamp",
7
+ "vuuMsg"
8
+ ];
9
+ const UNDO_COLUMN = {
10
+ name: "undo",
11
+ source: "client",
12
+ width: 80,
13
+ type: {
14
+ name: "string",
15
+ renderer: {
16
+ name: UNDO_CELL_RENDERER
17
+ }
18
+ }
19
+ };
20
+ const typeName = (column)=>{
21
+ if ("string" == typeof column.type) return column.type;
22
+ if (column.type) return column.type.name;
23
+ return "string";
24
+ };
25
+ const isReadOnlyColumn = (name)=>DEFAULT_READ_ONLY_COLUMNS.includes(name);
26
+ const narrowEditable = (schemaEditable = true, columnEditable = true)=>{
27
+ if (true === schemaEditable) return columnEditable;
28
+ if (false === schemaEditable || false === columnEditable) return false;
29
+ const insert = isDataValueEditable({
30
+ editable: schemaEditable
31
+ }, "insert") && isDataValueEditable({
32
+ editable: columnEditable
33
+ }, "insert");
34
+ const update = isDataValueEditable({
35
+ editable: schemaEditable
36
+ }, "update") && isDataValueEditable({
37
+ editable: columnEditable
38
+ }, "update");
39
+ return insert === update ? insert : {
40
+ insert,
41
+ update
42
+ };
43
+ };
44
+ const withEditor = (column, editor)=>{
45
+ if (void 0 === editor || "typeahead" === editor.name) return column;
46
+ return {
47
+ ...column,
48
+ type: {
49
+ name: typeName(column),
50
+ renderer: editor
51
+ }
52
+ };
53
+ };
54
+ const toViewColumn = (column)=>void 0 === column.editable || false === column.editable ? column : {
55
+ ...column,
56
+ editable: false
57
+ };
58
+ const getEditableColumns = ({ actionColumn = false, columns, columnsDiverge = false, editSchema, isEditing, schema = editSchema, undoColumn = false })=>{
59
+ if (!isEditing) return columns.some((c)=>void 0 !== c.editable && false !== c.editable) ? columns.map(toViewColumn) : columns;
60
+ const baseColumns = columnsDiverge && editSchema ? editSchema.columns.filter(({ name })=>"vuuAction" !== name).map((schemaColumn)=>columns.find(({ name })=>name === schemaColumn.name) ?? {
61
+ name: schemaColumn.name,
62
+ serverDataType: schemaColumn.serverDataType
63
+ }) : columns;
64
+ const editColumns = baseColumns.map((column)=>{
65
+ if ("client" === column.source) return column;
66
+ if (isReadOnlyColumn(column.name)) return void 0 === column.editable ? column : {
67
+ ...column,
68
+ editable: false
69
+ };
70
+ const schemaColumn = schema?.columns.find(({ name })=>name === column.name);
71
+ const editable = narrowEditable(schemaColumn?.editable, column.editable);
72
+ return withEditor({
73
+ ...column,
74
+ editable
75
+ }, column.editor);
76
+ });
77
+ if (actionColumn) editColumns.push({
78
+ hidden: "hidden" === actionColumn,
79
+ name: "vuuAction",
80
+ serverDataType: "string"
81
+ });
82
+ if (undoColumn) editColumns.push(true === undoColumn ? UNDO_COLUMN : {
83
+ ...UNDO_COLUMN,
84
+ ...undoColumn
85
+ });
86
+ return editColumns;
87
+ };
88
+ const useEditableColumns = ({ actionColumn, columns, columnsDiverge, editSchema, isEditing, schema, undoColumn })=>useMemo(()=>getEditableColumns({
89
+ actionColumn,
90
+ columns,
91
+ columnsDiverge,
92
+ editSchema,
93
+ isEditing,
94
+ schema,
95
+ undoColumn
96
+ }), [
97
+ actionColumn,
98
+ columns,
99
+ columnsDiverge,
100
+ editSchema,
101
+ isEditing,
102
+ schema,
103
+ undoColumn
104
+ ]);
105
+ export { DEFAULT_READ_ONLY_COLUMNS, UNDO_COLUMN, getEditableColumns, narrowEditable, useEditableColumns };