robobyte-front-builder 1.0.17 → 1.0.19

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.
@@ -0,0 +1,421 @@
1
+ /**
2
+ * Helper functions for managing updateRef in AG Grid
3
+ *
4
+ * updateRef structure: Array of row updates with original and updated data
5
+ * [
6
+ * {
7
+ * rowId: "123",
8
+ * originalData: {
9
+ * Id: 123,
10
+ * IsPublic: false, // original value
11
+ * Name: "Original Name",
12
+ * Product: { Name: "Original Product Name" }
13
+ * },
14
+ * data: {
15
+ * Id: 123,
16
+ * IsPublic: true, // updated field
17
+ * Name: "Original Name",
18
+ * Product: { Name: "Updated Name" } // updated nested field
19
+ * }
20
+ * },
21
+ * {
22
+ * rowId: "456",
23
+ * originalData: {
24
+ * Id: 456,
25
+ * Status: "Inactive",
26
+ * Name: "Original"
27
+ * },
28
+ * data: {
29
+ * Id: 456,
30
+ * Status: "Active", // updated field
31
+ * Name: "Original"
32
+ * }
33
+ * }
34
+ * ]
35
+ */
36
+
37
+ /**
38
+ * Get nested value from object using dot notation path
39
+ * @param {object} obj - The object to traverse
40
+ * @param {string} path - Dot-separated path (e.g., "Product.Name")
41
+ * @returns {any} The value at the path, or undefined if not found
42
+ */
43
+ export const getNestedValue = (obj, path) => {
44
+ if (!obj || !path) return undefined
45
+
46
+ const keys = path.split('.')
47
+ let current = obj
48
+
49
+ for (const key of keys) {
50
+ if (current == null) return undefined
51
+ current = current[key]
52
+ }
53
+
54
+ return current
55
+ }
56
+
57
+ /**
58
+ * Set nested value in object using dot notation path
59
+ * @param {object} obj - The object to modify
60
+ * @param {string} path - Dot-separated path (e.g., "Product.Name")
61
+ * @param {any} value - The value to set
62
+ */
63
+ export const setNestedValue = (obj, path, value) => {
64
+ if (!obj || !path) return
65
+
66
+ const keys = path.split('.')
67
+ const lastKey = keys.pop()
68
+ let current = obj
69
+
70
+ // Navigate to the parent object
71
+ for (const key of keys) {
72
+ if (!(key in current)) {
73
+ current[key] = {}
74
+ }
75
+ current = current[key]
76
+ }
77
+
78
+ // Set the value
79
+ current[lastKey] = value
80
+ }
81
+
82
+ /**
83
+ * Normalize rowId to string for comparison (handles both number and string)
84
+ * @param {string|number} rowId - The row ID to normalize
85
+ * @returns {string} Normalized row ID as string
86
+ */
87
+ const normalizeRowId = (rowId) => {
88
+ return rowId != null ? String(rowId) : null
89
+ }
90
+
91
+ /**
92
+ * Compare two rowIds (handles both number and string)
93
+ * @param {string|number} rowId1 - First row ID
94
+ * @param {string|number} rowId2 - Second row ID
95
+ * @returns {boolean} True if they match
96
+ */
97
+ const compareRowIds = (rowId1, rowId2) => {
98
+ return normalizeRowId(rowId1) === normalizeRowId(rowId2)
99
+ }
100
+
101
+ /**
102
+ * Get the unique row ID from row data based on rowUniqueId configuration
103
+ * @param {object} rowData - The AG Grid row data
104
+ * @param {array} rowUniqueId - Array of field paths that uniquely identify a row (from settings)
105
+ * @returns {string} Composite key string
106
+ */
107
+ export const getRowId = (rowData, rowUniqueId) => {
108
+ if (!rowData || !rowUniqueId || rowUniqueId.length === 0) {
109
+ return null
110
+ }
111
+
112
+ // If single field, return its value directly as string
113
+ if (rowUniqueId.length === 1) {
114
+ const value = getNestedValue(rowData, rowUniqueId[0])
115
+ return value != null ? String(value) : null
116
+ }
117
+
118
+ // If multiple fields, create composite key
119
+ const keyParts = rowUniqueId.map(fieldPath => {
120
+ const value = getNestedValue(rowData, fieldPath)
121
+ return value != null ? String(value) : ''
122
+ })
123
+
124
+ return keyParts.join('|')
125
+ }
126
+
127
+ /**
128
+ * Update a field value in updateRef
129
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
130
+ * @param {object} rowData - The AG Grid row data
131
+ * @param {array} rowUniqueId - Array of field paths for row identification
132
+ * @param {string} fieldPath - The field to update (supports dot notation)
133
+ * @param {any} newValue - The new value
134
+ */
135
+ export const setUpdateRefValue = (updateRef, rowData, rowUniqueId, fieldPath, newValue) => {
136
+ if (!updateRef || !updateRef.current) {
137
+ // console.warn('updateRef is not initialized')
138
+ return
139
+ }
140
+
141
+ const rowId = getRowId(rowData, rowUniqueId)
142
+ if (!rowId) {
143
+ // console.warn('Could not determine row ID from rowData', rowData, rowUniqueId)
144
+ return
145
+ }
146
+
147
+ // Ensure updateRef.current is an array
148
+ if (!Array.isArray(updateRef.current)) {
149
+ updateRef.current = []
150
+ }
151
+
152
+ // Find existing row update (use comparison to handle both string and number)
153
+ let rowUpdate = updateRef.current.find(item => compareRowIds(item.rowId, rowId))
154
+
155
+ if (!rowUpdate) {
156
+ // First edit: Create new row update entry with both originalData and data (deep clones)
157
+ rowUpdate = {
158
+ rowId: normalizeRowId(rowId),
159
+ originalData: JSON.parse(JSON.stringify(rowData)),
160
+ data: JSON.parse(JSON.stringify(rowData))
161
+ }
162
+ updateRef.current.push(rowUpdate)
163
+ } else {
164
+ // Row exists - merge with new rowData (...old, ...new)
165
+ // New fields are added, existing fields are updated, fields only in old are preserved
166
+ const clonedRowData = JSON.parse(JSON.stringify(rowData))
167
+ rowUpdate.data = {
168
+ ...rowUpdate.data,
169
+ ...clonedRowData
170
+ }
171
+ }
172
+
173
+ // Set the field value in the data object (supports nested paths)
174
+ setNestedValue(rowUpdate.data, fieldPath, newValue)
175
+
176
+ // console.log('updateRef updated:', rowId, fieldPath, newValue)
177
+ }
178
+
179
+ /**
180
+ * Update multiple fields in a row at once
181
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
182
+ * @param {object} rowData - The AG Grid row data
183
+ * @param {array} rowUniqueId - Array of field paths for row identification
184
+ * @param {object} updates - Object with fieldPath: newValue pairs
185
+ */
186
+ export const setUpdateRefRow = (updateRef, rowData, rowUniqueId, updates) => {
187
+ if (!updateRef || !updateRef.current) {
188
+ // console.warn('updateRef is not initialized')
189
+ return
190
+ }
191
+
192
+ const rowId = getRowId(rowData, rowUniqueId)
193
+ if (!rowId) {
194
+ // console.warn('Could not determine row ID from rowData', rowData, rowUniqueId)
195
+ return
196
+ }
197
+
198
+ // Ensure updateRef.current is an array
199
+ if (!Array.isArray(updateRef.current)) {
200
+ updateRef.current = []
201
+ }
202
+
203
+ // Find existing row update (use comparison to handle both string and number)
204
+ let rowUpdate = updateRef.current.find(item => compareRowIds(item.rowId, rowId))
205
+
206
+ if (!rowUpdate) {
207
+ // First edit: Create new row update entry with both originalData and data (deep clones)
208
+ rowUpdate = {
209
+ rowId: normalizeRowId(rowId),
210
+ originalData: JSON.parse(JSON.stringify(rowData)),
211
+ data: JSON.parse(JSON.stringify(rowData))
212
+ }
213
+ updateRef.current.push(rowUpdate)
214
+ } else {
215
+ // Row exists - merge with new rowData (...old, ...new)
216
+ // New fields are added, existing fields are updated, fields only in old are preserved
217
+ const clonedRowData = JSON.parse(JSON.stringify(rowData))
218
+ rowUpdate.data = {
219
+ ...rowUpdate.data,
220
+ ...clonedRowData
221
+ }
222
+ }
223
+
224
+ // Apply all updates to the data object (supports nested paths)
225
+ Object.entries(updates).forEach(([fieldPath, value]) => {
226
+ setNestedValue(rowUpdate.data, fieldPath, value)
227
+ })
228
+
229
+ // console.log('updateRef row updated:', rowId, updates)
230
+ }
231
+
232
+ /**
233
+ * Get a field value from updateRef or fallback to original row data
234
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
235
+ * @param {object} rowData - The AG Grid row data
236
+ * @param {array} rowUniqueId - Array of field paths for row identification
237
+ * @param {string} updateRefPath - The field path to get from updateRef.data (supports dot notation)
238
+ * @param {string} rowDataPath - The field path to get from original rowData if not in updateRef (optional, defaults to updateRefPath)
239
+ * @returns {any} The updated value if exists, otherwise the original value from rowData
240
+ */
241
+ export const getUpdateRefValue = (updateRef, rowData, rowUniqueId, updateRefPath, rowDataPath) => {
242
+ // Default rowDataPath to updateRefPath if not provided
243
+ const fallbackPath = rowDataPath !== undefined ? rowDataPath : updateRefPath
244
+
245
+ if (!updateRef || !updateRef.current || !Array.isArray(updateRef.current)) {
246
+ return getNestedValue(rowData, fallbackPath)
247
+ }
248
+
249
+ const rowId = getRowId(rowData, rowUniqueId)
250
+
251
+ if (!rowId) {
252
+ return getNestedValue(rowData, fallbackPath)
253
+ }
254
+
255
+ // Find row update (use comparison to handle both string and number)
256
+ const rowUpdate = updateRef.current.find(item => compareRowIds(item.rowId, rowId))
257
+
258
+ if (!rowUpdate || !rowUpdate.data) {
259
+ return getNestedValue(rowData, fallbackPath)
260
+ }
261
+
262
+ // Get value from the updated data object using updateRefPath
263
+ return getNestedValue(rowUpdate.data, updateRefPath)
264
+ }
265
+
266
+ /**
267
+ * Check if a row has been updated in updateRef
268
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
269
+ * @param {object} rowData - The AG Grid row data
270
+ * @param {array} rowUniqueId - Array of field paths for row identification
271
+ * @returns {boolean} True if the row has been updated
272
+ */
273
+ export const hasUpdateRefValue = (updateRef, rowData, rowUniqueId) => {
274
+ if (!updateRef || !updateRef.current || !Array.isArray(updateRef.current)) {
275
+ return false
276
+ }
277
+
278
+ const rowId = getRowId(rowData, rowUniqueId)
279
+ if (!rowId) {
280
+ return false
281
+ }
282
+
283
+ const rowUpdate = updateRef.current.find(item => compareRowIds(item.rowId, rowId))
284
+ return !!rowUpdate
285
+ }
286
+
287
+ /**
288
+ * Remove a specific row from updateRef by rowData and rowUniqueId
289
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
290
+ * @param {object} rowData - The AG Grid row data
291
+ * @param {array} rowUniqueId - Array of field paths for row identification
292
+ */
293
+ export const removeUpdateRefByRowId = (updateRef, rowData, rowUniqueId) => {
294
+ if (!updateRef || !updateRef.current || !Array.isArray(updateRef.current)) {
295
+ return
296
+ }
297
+
298
+ const rowId = getRowId(rowData, rowUniqueId)
299
+ if (!rowId) {
300
+ console.warn('Could not determine row ID from rowData', rowData, rowUniqueId)
301
+ return
302
+ }
303
+
304
+ const index = updateRef.current.findIndex(item => compareRowIds(item.rowId, rowId))
305
+ if (index !== -1) {
306
+ updateRef.current.splice(index, 1)
307
+ console.log('updateRef row removed:', rowId)
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Clear all updates for a specific row
313
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
314
+ * @param {object} rowData - The AG Grid row data
315
+ * @param {array} rowUniqueId - Array of field paths for row identification
316
+ */
317
+ export const clearUpdateRefRow = (updateRef, rowData, rowUniqueId) => {
318
+ if (!updateRef || !updateRef.current || !Array.isArray(updateRef.current)) {
319
+ return
320
+ }
321
+
322
+ const rowId = getRowId(rowData, rowUniqueId)
323
+ if (!rowId) {
324
+ return
325
+ }
326
+
327
+ removeUpdateRefByRowId(updateRef, rowId)
328
+ }
329
+
330
+ /**
331
+ * Normalize updateRef by merging duplicate entries (handles mixed number/string rowIds)
332
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
333
+ */
334
+ export const normalizeUpdateRef = (updateRef) => {
335
+ if (!updateRef || !updateRef.current || !Array.isArray(updateRef.current)) {
336
+ return
337
+ }
338
+
339
+ const normalized = []
340
+ const seen = new Set()
341
+
342
+ updateRef.current.forEach(item => {
343
+ const normalizedId = normalizeRowId(item.rowId)
344
+
345
+ if (seen.has(normalizedId)) {
346
+ // Find existing entry and merge data
347
+ const existing = normalized.find(n => compareRowIds(n.rowId, normalizedId))
348
+ if (existing) {
349
+ // Merge data from duplicate entry (later values override)
350
+ Object.assign(existing.data, item.data)
351
+ // Keep originalData if not present
352
+ if (!existing.originalData && item.originalData) {
353
+ existing.originalData = item.originalData
354
+ }
355
+ }
356
+ } else {
357
+ // Add new normalized entry
358
+ normalized.push({
359
+ ...item,
360
+ rowId: normalizedId
361
+ })
362
+ seen.add(normalizedId)
363
+ }
364
+ })
365
+
366
+ updateRef.current = normalized
367
+ console.log('updateRef normalized, merged duplicates')
368
+ }
369
+
370
+ /**
371
+ * Clone updateRef to originalRefData (backup before changes)
372
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
373
+ * @param {React.MutableRefObject} originalRefData - The ref to store the clone
374
+ */
375
+ export const cloneUpdateRefToOriginal = (updateRef, originalRefData) => {
376
+ if (!updateRef || !updateRef.current || !originalRefData) {
377
+ return
378
+ }
379
+
380
+ originalRefData.current = JSON.parse(JSON.stringify(updateRef.current))
381
+ console.log('updateRef cloned to originalRefData:', originalRefData.current.length, 'rows')
382
+ }
383
+
384
+ /**
385
+ * Restore updateRef from originalRefData (reset to backup)
386
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
387
+ * @param {React.MutableRefObject} originalRefData - The ref with the backup data
388
+ */
389
+ export const restoreUpdateRefFromOriginal = (updateRef, originalRefData) => {
390
+ if (!updateRef || !originalRefData || !originalRefData.current) {
391
+ return
392
+ }
393
+
394
+ updateRef.current = JSON.parse(JSON.stringify(originalRefData.current))
395
+ console.log('updateRef restored from originalRefData:', updateRef.current.length, 'rows')
396
+ }
397
+
398
+ /**
399
+ * Clear all updates
400
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
401
+ */
402
+ export const clearAllUpdateRef = (updateRef) => {
403
+ if (!updateRef || !updateRef.current) {
404
+ return
405
+ }
406
+
407
+ updateRef.current = []
408
+ }
409
+
410
+ /**
411
+ * Get all updated rows
412
+ * @param {React.MutableRefObject} updateRef - The ref object storing updates (array)
413
+ * @returns {array} The current updateRef.current array
414
+ */
415
+ export const getAllUpdates = (updateRef) => {
416
+ if (!updateRef || !updateRef.current) {
417
+ return []
418
+ }
419
+
420
+ return updateRef.current
421
+ }