@onehat/ui 0.4.77 → 0.4.79

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 (59) hide show
  1. package/package.json +1 -1
  2. package/src/Components/Editor/AttachmentDirectoriesEditor.js +51 -0
  3. package/src/Components/Editor/AttachmentsEditor.js +81 -0
  4. package/src/Components/Form/Field/Combo/AttachmentDirectoriesCombo.js +20 -0
  5. package/src/Components/Form/Field/Combo/AttachmentDirectoriesComboEditor.js +22 -0
  6. package/src/Components/Form/Field/Combo/AttachmentsCombo.js +20 -0
  7. package/src/Components/Form/Field/Combo/AttachmentsComboEditor.js +22 -0
  8. package/src/Components/Form/Field/Tag/AttachmentDirectoriesTag.js +22 -0
  9. package/src/Components/Form/Field/Tag/AttachmentDirectoriesTagEditor.js +22 -0
  10. package/src/Components/Form/Field/Tag/AttachmentsTag.js +22 -0
  11. package/src/Components/Form/Field/Tag/AttachmentsTagEditor.js +22 -0
  12. package/src/Components/Form/Form.js +17 -17
  13. package/src/Components/Grid/AttachmentDirectoriesFilteredGrid.js +17 -0
  14. package/src/Components/Grid/AttachmentDirectoriesFilteredGridEditor.js +17 -0
  15. package/src/Components/Grid/AttachmentDirectoriesFilteredInlineGridEditor.js +17 -0
  16. package/src/Components/Grid/AttachmentDirectoriesFilteredSideGridEditor.js +17 -0
  17. package/src/Components/Grid/AttachmentDirectoriesGrid.js +20 -0
  18. package/src/Components/Grid/AttachmentDirectoriesGridEditor.js +27 -0
  19. package/src/Components/Grid/AttachmentDirectoriesInlineGridEditor.js +25 -0
  20. package/src/Components/Grid/AttachmentDirectoriesSideGridEditor.js +24 -0
  21. package/src/Components/Grid/AttachmentsFilteredGrid.js +17 -0
  22. package/src/Components/Grid/AttachmentsFilteredGridEditor.js +17 -0
  23. package/src/Components/Grid/AttachmentsFilteredInlineGridEditor.js +17 -0
  24. package/src/Components/Grid/AttachmentsFilteredSideGridEditor.js +17 -0
  25. package/src/Components/Grid/AttachmentsGrid.js +20 -0
  26. package/src/Components/Grid/AttachmentsGridEditor.js +27 -0
  27. package/src/Components/Grid/AttachmentsInlineGridEditor.js +25 -0
  28. package/src/Components/Grid/AttachmentsSideGridEditor.js +24 -0
  29. package/src/Components/Grid/Columns/AttachmentDirectoriesGridColumns.js +32 -0
  30. package/src/Components/Grid/Columns/AttachmentsGridColumns.js +133 -0
  31. package/src/Components/Grid/Grid.js +194 -21
  32. package/src/Components/Grid/GridHeaderRow.js +10 -17
  33. package/src/Components/Grid/GridRow.js +49 -22
  34. package/src/Components/Grid/RowHandle.js +8 -6
  35. package/src/Components/Hoc/withEditor.js +18 -1
  36. package/src/Components/Hoc/withModal.js +4 -0
  37. package/src/Components/Hoc/withPdfButtons.js +1 -1
  38. package/src/Components/Hoc/withSelection.js +26 -4
  39. package/src/Components/Layout/AsyncOperation.js +299 -195
  40. package/src/Components/Messages/GlobalModals.js +1 -2
  41. package/src/Components/Panel/Panel.js +14 -2
  42. package/src/Components/Panel/TabPanel.js +1 -1
  43. package/src/Components/Panel/TreePanel.js +1 -1
  44. package/src/Components/Report/Report.js +106 -17
  45. package/src/Components/Toolbar/PaginationToolbar.js +4 -3
  46. package/src/Components/Toolbar/Toolbar.js +6 -3
  47. package/src/Components/Tree/Tree.js +219 -148
  48. package/src/Components/Tree/TreeNode.js +20 -13
  49. package/src/Components/Window/AttachmentDirectoriesEditorWindow.js +34 -0
  50. package/src/Components/Window/AttachmentsEditorWindow.js +34 -0
  51. package/src/Components/index.js +92 -1
  52. package/src/Constants/Attachments.js +2 -0
  53. package/src/Constants/Dates.js +2 -2
  54. package/src/Constants/Progress.js +5 -1
  55. package/src/Models/Schemas/AttachmentDirectories.js +66 -0
  56. package/src/Models/Schemas/Attachments.js +88 -0
  57. package/src/Models/Slices/SystemSlice.js +220 -0
  58. package/src/PlatformImports/Web/Attachments.js +855 -161
  59. package/src/Styles/Global.css +7 -2
@@ -1,4 +1,4 @@
1
- import { useMemo, useState, useEffect, } from 'react';
1
+ import { useMemo, useState, useEffect, forwardRef, isValidElement, cloneElement, } from 'react';
2
2
  import {
3
3
  Box,
4
4
  HStack,
@@ -25,25 +25,20 @@ import useAsyncRenderers from './useAsyncRenderers.js';
25
25
  import _ from 'lodash';
26
26
 
27
27
  // Conditional import for web only
28
- let getEmptyImage;
29
- if (CURRENT_MODE === UI_MODE_WEB) {
30
- import('react-dnd-html5-backend').then((module) => {
31
- getEmptyImage = module.getEmptyImage;
32
- }).catch(() => {
33
- getEmptyImage = null;
34
- });
35
- }
28
+ let getEmptyImage = null;
36
29
 
37
30
  // This was broken out from Grid simply so we can memoize it
38
31
 
39
- function GridRow(props) {
32
+ const GridRow = forwardRef(function GridRow(props, ref) {
40
33
  const {
41
34
  columnsConfig,
42
35
  columnProps,
43
36
  fields,
44
37
  rowProps,
45
38
  hideNavColumn,
46
- showSelectHandle,
39
+ showRowHandle,
40
+ rowCanSelect,
41
+ rowCanDrag,
47
42
  isRowHoverable,
48
43
  isSelected,
49
44
  isHovered,
@@ -79,8 +74,15 @@ function GridRow(props) {
79
74
  // Hide the default drag preview only when using custom drag proxy (and only on web)
80
75
  useEffect(() => {
81
76
  if (dragPreviewRef && typeof dragPreviewRef === 'function' && getDragProxy && CURRENT_MODE === UI_MODE_WEB) {
82
- // Only suppress default drag preview when we have a custom one and we're on web
83
- dragPreviewRef(getEmptyImage(), { captureDraggingState: true });
77
+ // Load getEmptyImage dynamically and apply it
78
+ import('react-dnd-html5-backend').then((module) => {
79
+ const getEmptyImage = module.getEmptyImage;
80
+ if (getEmptyImage) {
81
+ dragPreviewRef(getEmptyImage(), { captureDraggingState: true });
82
+ }
83
+ }).catch((error) => {
84
+ console.warn('Failed to load react-dnd-html5-backend:', error);
85
+ });
84
86
  }
85
87
  }, [dragPreviewRef, getDragProxy]);
86
88
 
@@ -126,12 +128,13 @@ function GridRow(props) {
126
128
  if (_.isArray(columnsConfig)) {
127
129
  return _.map(columnsConfig, (config, key, all) => {
128
130
  if (config.isHidden) {
129
- return null;
131
+ // every element needs a key, so return an empty element with a key
132
+ return <Box key={key} style={{ display: 'none' }} />;
130
133
  }
131
134
  const
132
135
  propsToPass = columnProps[key] || {},
133
136
  colStyle = {},
134
- whichCursor = showSelectHandle ? 'cursor-text' : 'cursor-pointer'; // when using rowSelectHandle, indicate that the row text is selectable, otherwise indicate that the row itself is selectable
137
+ whichCursor = showRowHandle ? 'cursor-text' : 'cursor-pointer'; // when using rowSelectHandle, indicate that the row text is selectable, otherwise indicate that the row itself is selectable
135
138
  let colClassName = clsx(
136
139
  'GridRow-column',
137
140
  'p-1',
@@ -161,7 +164,12 @@ function GridRow(props) {
161
164
 
162
165
  let value;
163
166
  if (_.isFunction(config)) {
164
- return config(item, key);
167
+ const element = config(item, key);
168
+ if (isValidElement(element)) {
169
+ // ensure element has a key
170
+ return element.key != null ? element : cloneElement(element, { key });
171
+ }
172
+ return <Box key={key}>{element}</Box>; // create a box (with key) to wrap non-elements
165
173
  }
166
174
  if (_.isPlainObject(config)) {
167
175
  if (config.renderer) {
@@ -219,7 +227,7 @@ function GridRow(props) {
219
227
  throw Error('Not yet working correctly!');
220
228
  // Async renderer
221
229
  if (isLoading) {
222
- content = <Loading />;
230
+ content = <Loading key={key} />;
223
231
  } else if (asyncResult) {
224
232
  if (asyncResult.error) {
225
233
  content = <Text key={key}>Render Error: {asyncResult.error.message || String(asyncResult.error)}</Text>;
@@ -240,7 +248,15 @@ function GridRow(props) {
240
248
  content = <Text key={key}>Render Error: {error}</Text>;
241
249
  }
242
250
  }
243
- return content;
251
+
252
+
253
+
254
+ // Ensure content has a key prop
255
+ if (isValidElement(content)) {
256
+ // ensure element has a key
257
+ return content.key != null ? content : cloneElement(content, { key });
258
+ }
259
+ return <Box key={key}>{content}</Box>; // create a box (with key) to wrap non-elements
244
260
  }
245
261
  if (config.fieldName) {
246
262
 
@@ -315,7 +331,13 @@ function GridRow(props) {
315
331
  }
316
332
  }
317
333
  if (_.isFunction(value)) {
318
- return value(key);
334
+ const result = value(key);
335
+ // Ensure the result has a key prop
336
+ if (isValidElement(result)) {
337
+ // Only clone if the result doesn't already have a key
338
+ return result.key != null ? result : cloneElement(result, { key });
339
+ }
340
+ return <Box key={key}>{result}</Box>;
319
341
  }
320
342
  const elementProps = {};
321
343
  if (config.getCellProps) {
@@ -353,12 +375,13 @@ function GridRow(props) {
353
375
  };
354
376
 
355
377
  let rowContents = <>
356
- {(isDragSource || isDraggable || showSelectHandle) &&
378
+ {showRowHandle &&
357
379
  <RowHandle
358
380
  ref={dragSourceRef}
359
381
  isDragSource={isDragSource}
360
382
  isDraggable={isDraggable}
361
- showSelectHandle={showSelectHandle}
383
+ canSelect={rowCanSelect}
384
+ canDrag={rowCanDrag}
362
385
  />}
363
386
 
364
387
  {isPhantom &&
@@ -418,6 +441,7 @@ function GridRow(props) {
418
441
  rowClassName += ' border-4 border-[#0ff]';
419
442
  }
420
443
  return <HStackNative
444
+ ref={ref}
421
445
  {...testProps('Row ' + (isSelected ? 'row-selected' : ''))}
422
446
  {...rowProps}
423
447
  key={hash}
@@ -449,8 +473,11 @@ function GridRow(props) {
449
473
  dragSourceRef,
450
474
  dragPreviewRef,
451
475
  dropTargetRef,
476
+ showRowHandle,
477
+ rowCanSelect,
478
+ rowCanDrag,
452
479
  ]);
453
- }
480
+ });
454
481
 
455
482
  // export default withDraggable(withDragSource(withDropTarget(GridRow)));
456
483
  export default GridRow;
@@ -7,7 +7,7 @@ import withTooltip from '@onehat/ui/src/Components/Hoc/withTooltip';
7
7
  import clsx from 'clsx';
8
8
  import Arcs from '../Icons/Arcs.js';
9
9
 
10
- const RowHandle = forwardRef(function RowHandle(props, ref) {
10
+ const RowHandle = forwardRef((props, ref) => {
11
11
  const {
12
12
  isDragSource,
13
13
  isDraggable
@@ -31,25 +31,27 @@ const RowHandle = forwardRef(function RowHandle(props, ref) {
31
31
  });
32
32
 
33
33
  function withAdditionalProps(WrappedComponent) {
34
- return (props) => {
34
+ return forwardRef((props, ref) => {
35
35
  const {
36
- showSelectHandle,
36
+ canSelect,
37
+ canDrag,
37
38
  isDragSource,
38
39
  isDraggable
39
40
  } = props;
40
41
  let tooltipParts = [];
41
- if (showSelectHandle) {
42
+ if (canSelect) {
42
43
  tooltipParts.push('Select');
43
44
  }
44
- if (isDragSource || isDraggable) {
45
+ if (canDrag) {
45
46
  tooltipParts.push('Drag');
46
47
  }
47
48
  const tooltip = tooltipParts.length === 2 ? tooltipParts.join(' or ') : tooltipParts[0];
48
49
  return <WrappedComponent
49
50
  tooltip={tooltip}
50
51
  {...props}
52
+ ref={ref}
51
53
  />;
52
- };
54
+ });
53
55
  }
54
56
 
55
57
  export default withAdditionalProps(withTooltip(RowHandle));
@@ -45,6 +45,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
45
45
  editorType,
46
46
  onAdd,
47
47
  onChange, // any kind of crud change
48
+ onBeforeDelete,
48
49
  onDelete,
49
50
  onSave, // this could also be called 'onEdit'
50
51
  onEditorClose,
@@ -106,7 +107,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
106
107
  setIsWaitModalShown = (bool) => {
107
108
  const
108
109
  dispatch = UiGlobals.redux?.dispatch,
109
- setIsWaitModalShownAction = UiGlobals.debugReducer?.setIsWaitModalShownAction;
110
+ setIsWaitModalShownAction = UiGlobals.systemReducer?.setIsWaitModalShownAction;
110
111
  if (setIsWaitModalShownAction) {
111
112
  console.log('withEditor:setIsWaitModalShownAction', bool);
112
113
  dispatch(setIsWaitModalShownAction(bool));
@@ -289,7 +290,15 @@ export default function withEditor(WrappedComponent, isTree = false) {
289
290
  if (_.isEmpty(selection) || (_.isArray(selection) && (selection.length > 1 || selection[0]?.isDestroyed))) {
290
291
  return;
291
292
  }
293
+ if (onBeforeDelete) {
294
+ // This listener is set by parent components using a prop
295
+ const listenerResult = await onBeforeDelete(selection);
296
+ if (listenerResult === false) {
297
+ return;
298
+ }
299
+ }
292
300
  if (getListeners().onBeforeDelete) {
301
+ // This listener is set by child components using setWithEditListeners()
293
302
  const listenerResult = await getListeners().onBeforeDelete();
294
303
  if (listenerResult === false) {
295
304
  return;
@@ -345,7 +354,15 @@ export default function withEditor(WrappedComponent, isTree = false) {
345
354
  return;
346
355
  }
347
356
  const selection = getSelection();
357
+ if (onBeforeDelete) {
358
+ // This listener is set by parent components using a prop
359
+ const listenerResult = await onBeforeDelete(selection);
360
+ if (listenerResult === false) {
361
+ return;
362
+ }
363
+ }
348
364
  if (getListeners().onBeforeDelete) {
365
+ // This listener is set by child components using setWithEditListeners()
349
366
  const listenerResult = await getListeners().onBeforeDelete(selection);
350
367
  if (listenerResult === false) {
351
368
  return;
@@ -72,6 +72,10 @@ export default function withModal(WrappedComponent) {
72
72
  throw new Error('withModal: body is required for showModal');
73
73
  }
74
74
 
75
+ if (_.isFunction(body)) {
76
+ body = body();
77
+ }
78
+
75
79
  setTitle(title);
76
80
  setBody(body);
77
81
  setCanClose(canClose);
@@ -332,7 +332,7 @@ export default function withPdfButtons(WrappedComponent) {
332
332
 
333
333
  const
334
334
  dispatch = UiGlobals.redux.dispatch,
335
- setIsWaitModalShownAction = UiGlobals.debugReducer.setIsWaitModalShownAction;
335
+ setIsWaitModalShownAction = UiGlobals.systemReducer.setIsWaitModalShownAction;
336
336
 
337
337
  dispatch(setIsWaitModalShownAction(true));
338
338
 
@@ -42,8 +42,24 @@ export default function withSelection(WrappedComponent) {
42
42
  forceUpdate = useForceUpdate(),
43
43
  selectionRef = useRef(initialSelection),
44
44
  RepositoryRef = useRef(Repository),
45
+ isSelectionChangesEnabledRef = useRef(true),
45
46
  [isReady, setIsReady] = useState(selection || false), // if selection is already defined, or value is not null and we don't need to load repository, it's ready
47
+ getIsSelectionChangesEnabled = () => {
48
+ return isSelectionChangesEnabledRef.current;
49
+ },
50
+ setIsSelectionChangesEnabled = (bool) => {
51
+ isSelectionChangesEnabledRef.current = bool;
52
+ },
53
+ disableSelectionChanges = () => {
54
+ setIsSelectionChangesEnabled(false);
55
+ },
56
+ enableSelectionChanges = () => {
57
+ setIsSelectionChangesEnabled(true);
58
+ },
46
59
  setSelection = (selection) => {
60
+ if (!getIsSelectionChangesEnabled()) {
61
+ return;
62
+ }
47
63
  if (_.isEqual(selection, getSelection())) {
48
64
  return;
49
65
  }
@@ -195,13 +211,16 @@ export default function withSelection(WrappedComponent) {
195
211
  },
196
212
  isInSelection = (item) => {
197
213
  const Repository = getRepository();
214
+ let found = null;
198
215
  if (Repository) {
199
- return inArray(item, getSelection());
200
- }
201
-
202
- const found = _.find(getSelection(), (selectedItem) => {
216
+ found = _.find(getSelection(), (selectedItem) => {
217
+ return selectedItem.id === item.id;
218
+ });
219
+ } else {
220
+ found = _.find(getSelection(), (selectedItem) => {
203
221
  return selectedItem[idIx] === item[idIx];
204
222
  });
223
+ }
205
224
  return !!found;
206
225
  },
207
226
  getIndexOfSelectedItem = (item) => {
@@ -436,6 +455,9 @@ export default function withSelection(WrappedComponent) {
436
455
  isInSelection={isInSelection}
437
456
  getIdsFromSelection={getIdsFromLocalSelection}
438
457
  getDisplayValuesFromSelection={getDisplayValuesFromSelection}
458
+ disableSelectionChanges={disableSelectionChanges}
459
+ enableSelectionChanges={enableSelectionChanges}
460
+ refreshSelection={refreshSelection}
439
461
  />;
440
462
  });
441
463
  }