@onehat/ui 0.4.78 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/ui",
3
- "version": "0.4.78",
3
+ "version": "0.4.79",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1277,7 +1277,7 @@ function GridComponent(props) {
1277
1277
  applySelectorSelected();
1278
1278
  Repository.resumeEvents();
1279
1279
 
1280
- if (((Repository.isRemote && !Repository.isLoaded) || forceLoadOnRender) && !disableLoadOnRender) { // default remote repositories to load on render, optionally force or disable load on render
1280
+ if (((Repository.isRemote && !Repository.isLoaded && !Repository.isLoading) || forceLoadOnRender) && !disableLoadOnRender) { // default remote repositories to load on render, optionally force or disable load on render
1281
1281
  Repository.load();
1282
1282
  }
1283
1283
 
@@ -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,
@@ -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);
@@ -609,7 +609,7 @@ function TreeComponent(props) {
609
609
  let nodes = [];
610
610
  if (Repository) {
611
611
  if (!Repository.isDestroyed) {
612
- if (!Repository.areRootNodesLoaded) {
612
+ if (!Repository.isLoaded) {
613
613
  nodes = await Repository.loadRootNodes(1);
614
614
  } else {
615
615
  nodes = Repository.getRootNodes();
@@ -58,8 +58,9 @@ import getSaved from '../../Functions/getSaved.js';
58
58
  import setSaved from '../../Functions/setSaved.js';
59
59
  import Folder from '../../Components/Icons/Folder.js';
60
60
  import Plus from '../../Components/Icons/Plus.js';
61
- import Minus from '../../Components/Icons/Minus.js';
61
+ import Trash from '../../Components/Icons/Trash.js';
62
62
  import Edit from '../../Components/Icons/Edit.js';
63
+ import Rotate from '../../Components/Icons/Rotate.js';
63
64
  import delay from '../../Functions/delay.js';
64
65
  import _ from 'lodash';
65
66
 
@@ -86,17 +87,29 @@ function FileCardCustom(props) {
86
87
  isDownloading = uploadStatus && inArray(uploadStatus, ['preparing', 'uploading', 'success']),
87
88
  isPdf = mimetype === 'application/pdf';
88
89
 
89
- let cardContent = <Pressable
90
- onPress={() => {
91
- downloadInBackground(downloadUrl);
92
- }}
93
- className="Pressable px-3 py-1 items-center flex-row rounded-[5px] border border-primary.700"
94
- >
95
- {isDownloading && <Spinner className="mr-2" />}
96
- {onSee && isPdf && <IconButton className="mr-1" icon={Eye} onPress={() => onSee(id)} />}
97
- <Text>{filename}</Text>
98
- {onDelete && <IconButton className="ml-1" icon={Xmark} onPress={() => onDelete(id)} />}
99
- </Pressable>;
90
+ let cardContent =
91
+ <Pressable
92
+ onPress={() => {
93
+ downloadInBackground(downloadUrl);
94
+ }}
95
+ className="Pressable px-3 py-1 items-center flex-row rounded-[5px] border border-primary.700"
96
+ >
97
+ {isDownloading &&
98
+ <Spinner className="mr-2" />}
99
+ {onSee && isPdf &&
100
+ <IconButton
101
+ className="mr-1"
102
+ icon={Eye}
103
+ onPress={() => onSee(id)}
104
+ />}
105
+ <Text>{filename}</Text>
106
+ {onDelete &&
107
+ <IconButton
108
+ className="ml-1"
109
+ icon={Xmark}
110
+ onPress={() => onDelete(id)}
111
+ />}
112
+ </Pressable>;
100
113
 
101
114
  // Wrap with drag source if needed
102
115
  if (isDragSource) {
@@ -211,7 +224,7 @@ function AttachmentsElement(props) {
211
224
  selectorSelectedField = 'id',
212
225
 
213
226
  // withData
214
- Repository,
227
+ Repository: Attachments,
215
228
 
216
229
  // withAlert
217
230
  showModal,
@@ -260,7 +273,7 @@ function AttachmentsElement(props) {
260
273
  return setFilesRaw.current;
261
274
  },
262
275
  buildFiles = () => {
263
- const files = _.map(Repository.entities, (entity) => {
276
+ const files = _.map(Attachments.entities, (entity) => {
264
277
  return {
265
278
  id: entity.id, // string | number The identifier of the file
266
279
  // file: null, // File The file object obtained from client drop or selection
@@ -301,11 +314,11 @@ function AttachmentsElement(props) {
301
314
  doDelete = (id) => {
302
315
  const
303
316
  files = getFiles(),
304
- file = Repository.getById(id);
317
+ file = Attachments.getById(id);
305
318
  if (file) {
306
319
  // if the file exists in the repository, delete it there
307
- Repository.deleteById(id);
308
- Repository.save();
320
+ Attachments.deleteById(id);
321
+ Attachments.save();
309
322
 
310
323
  } else {
311
324
  // simply remove it from the files array
@@ -388,7 +401,7 @@ function AttachmentsElement(props) {
388
401
  });
389
402
  if (!isError) {
390
403
  setIsUploading(false);
391
- Repository.reload();
404
+ Attachments.reload();
392
405
  if (onUpload) {
393
406
  onUpload(files);
394
407
  }
@@ -397,77 +410,23 @@ function AttachmentsElement(props) {
397
410
  },
398
411
 
399
412
  // Lightbox
400
- findFile = (id) => {
401
- const files = getFiles();
402
- if (useFileMosaic) {
403
- return _.find(files, (file) => file.id === id);
404
- }
405
- return _.find(files, { id });
406
- },
407
- findPrevFile = (id) => {
408
- const
409
- files = getFiles(),
410
- currentFile = findFile(id),
411
- currentIx = _.findIndex(files, currentFile);
412
- if (currentIx > 0) {
413
- return files[currentIx - 1];
414
- }
415
- return null;
416
- }
417
- findNextFile = (url, id) => {
418
- const
419
- files = getFiles(),
420
- currentFile = findFile(id),
421
- currentIx = _.findIndex(files, currentFile);
422
- if (currentIx < files.length - 1) {
423
- return files[currentIx + 1];
424
- }
425
- return null;
426
- },
427
413
  buildModalBody = (id) => {
428
- let isPdf = false,
429
- url = null,
430
- body = null,
431
- isPrevDisabled = false,
432
- isNextDisabled = false,
433
- onPrev,
434
- onNext;
435
- switch(viewMode) {
436
- case ATTACHMENTS_VIEW_MODES__ICON: {
437
- const
438
- currentFile = findFile(id),
439
- prevFile = findPrevFile(id),
440
- nextFile = findNextFile(id);
441
- isPrevDisabled = !prevFile;
442
- isNextDisabled = !nextFile;
443
- onPrev = () => {
444
- updateModalBody(buildModalBody(prevFile.id));
445
- };
446
- onNext = () => {
447
- updateModalBody(buildModalBody(nextFile.id));
448
- };
449
- url = currentFile.imageUrl;
450
- isPdf = url?.match(/\.pdf$/);
451
- break;
452
- }
453
- case ATTACHMENTS_VIEW_MODES__LIST: {
454
- const
455
- currentFile = Repository.getById(id),
456
- currentIx = Repository.getIxById(id),
457
- prevFile = Repository.getByIx(currentIx - 1),
458
- nextFile = Repository.getByIx(currentIx + 1);
459
- isPrevDisabled = !prevFile;
460
- isNextDisabled = !nextFile;
461
- onPrev = () => {
462
- updateModalBody(buildModalBody(prevFile.id));
463
- };
464
- onNext = () => {
465
- updateModalBody(buildModalBody(nextFile.id));
466
- };
467
- url = currentFile.attachments__uri;
468
- isPdf = currentFile.attachments__mimetype === 'application/pdf';
469
- }
470
- }
414
+ const
415
+ currentFile = Attachments.getById(id),
416
+ currentIx = Attachments.getIxById(id),
417
+ prevFile = Attachments.getByIx(currentIx - 1),
418
+ nextFile = Attachments.getByIx(currentIx + 1),
419
+ isPrevDisabled = !prevFile,
420
+ isNextDisabled = !nextFile,
421
+ onPrev = () => {
422
+ updateModalBody(buildModalBody(prevFile.id));
423
+ },
424
+ onNext = () => {
425
+ updateModalBody(buildModalBody(nextFile.id));
426
+ },
427
+ url = currentFile.attachments__uri,
428
+ isPdf = currentFile.attachments__mimetype === 'application/pdf';
429
+ let body = null;
471
430
  if (isPdf) {
472
431
  body = <iframe
473
432
  src={url}
@@ -555,9 +514,54 @@ function AttachmentsElement(props) {
555
514
  });
556
515
  },
557
516
  onDeleteDirectory = async () => {
558
- const attachmentDirectory = getTreeSelection()[0];
517
+
518
+ const
519
+ attachmentDirectory = getTreeSelection()[0],
520
+ isRoot = attachmentDirectory.isRoot;
521
+ if (isRoot) {
522
+ alert('Cannot delete the root directory.');
523
+ return;
524
+ }
525
+
526
+
527
+ // check if there are any attachments in this directory or its subdirectories
528
+ const
529
+ url = AttachmentDirectories.api.baseURL + 'AttachmentDirectories/hasAttachments',
530
+ data = {
531
+ attachment_directory_id: treeSelection[0].id,
532
+ },
533
+ result = await AttachmentDirectories._send('POST', url, data);
534
+
535
+ const {
536
+ root,
537
+ success,
538
+ total,
539
+ message
540
+ } = AttachmentDirectories._processServerResponse(result);
541
+
542
+ if (!success) {
543
+ alert(message);
544
+ return;
545
+ }
546
+
547
+ if (root.hasAttachments) {
548
+ alert('Cannot delete a directory that contains attachments somewhere down its hierarchy. Please move or delete the attachments first.');
549
+ return;
550
+ }
551
+
552
+
553
+ // transfer selection to the parent node
554
+ const
555
+ parentNode = attachmentDirectory.getParent(),
556
+ newSelection = [parentNode];
557
+ setTreeSelection(newSelection);
558
+ self.children.tree.setSelection(newSelection);
559
+
560
+
561
+ // now delete it
559
562
  await attachmentDirectory.delete();
560
563
  self.children.tree.buildAndSetTreeNodeData();
564
+
561
565
  },
562
566
  onRenameDirectory = () => {
563
567
  const attachmentDirectory = getTreeSelection()[0];
@@ -606,6 +610,14 @@ function AttachmentsElement(props) {
606
610
  }}
607
611
  />,
608
612
  });
613
+ },
614
+ onReloadDirectories = async () => {
615
+ await AttachmentDirectories.loadRootNodes(2);
616
+ const rootNodes = AttachmentDirectories.getRootNodes();
617
+ if (rootNodes) {
618
+ setTreeSelection(rootNodes);
619
+ self.children.tree.setSelection(rootNodes);
620
+ }
609
621
  };
610
622
 
611
623
  if (!_.isEqual(modelidCalc, modelid.current)) {
@@ -624,9 +636,9 @@ function AttachmentsElement(props) {
624
636
  setDirectoriesTrue = () => setIsDirectoriesLoading(true),
625
637
  setDirectoriesFalse = () => setIsDirectoriesLoading(false);
626
638
 
627
- Repository.on('beforeLoad', setTrue);
628
- Repository.on('load', setFalse);
629
- Repository.on('load', buildFiles);
639
+ Attachments.on('beforeLoad', setTrue);
640
+ Attachments.on('load', setFalse);
641
+ Attachments.on('load', buildFiles);
630
642
  if (usesDirectories) {
631
643
  AttachmentDirectories.on('beforeLoad', setDirectoriesTrue);
632
644
  AttachmentDirectories.on('loadRootNodes', setDirectoriesFalse);
@@ -636,12 +648,12 @@ function AttachmentsElement(props) {
636
648
 
637
649
  if (modelid.current && !_.isArray(modelid.current)) {
638
650
  const
639
- currentConditions = Repository.getBaseParamConditions() || {},
651
+ currentConditions = Attachments.getParamConditions() || {},
640
652
  newConditions = {
641
653
  'conditions[Attachments.model]': model,
642
654
  'conditions[Attachments.modelid]': modelid.current,
643
655
  },
644
- currentPageSize = Repository.pageSize,
656
+ currentPageSize = Attachments.pageSize,
645
657
  newPageSize = showAll ? expandedMax : collapsedMax;
646
658
 
647
659
  // figure out conditions
@@ -666,34 +678,34 @@ function AttachmentsElement(props) {
666
678
  }
667
679
  let doReload = false;
668
680
  if (!_.isEqual(currentConditions, newConditions)) {
669
- Repository.setBaseParams(newConditions);
681
+ Attachments.setParams(newConditions);
670
682
  doReload = true;
671
683
  }
672
684
 
673
685
  // figure out pageSize
674
686
  if (!_.isEqual(currentPageSize, newPageSize)) {
675
- Repository.setPageSize(newPageSize);
687
+ Attachments.setPageSize(newPageSize);
676
688
  doReload = true;
677
689
  }
678
690
  if (doReload) {
679
- await Repository.load();
691
+ await Attachments.load();
680
692
  }
681
693
  if (usesDirectories) {
682
694
  const
683
695
  wasAlreadyLoaded = AttachmentDirectories.areRootNodesLoaded,
684
- currentConditions = AttachmentDirectories.getBaseParamConditions() || {},
696
+ currentConditions = AttachmentDirectories.getParamConditions() || {},
685
697
  newConditions = {
686
698
  'conditions[AttachmentDirectories.model]': selectorSelected.repository.name,
687
699
  'conditions[AttachmentDirectories.modelid]': selectorSelected[selectorSelectedField],
688
700
  };
689
701
  let doReload = false;
690
702
  if (!_.isEqual(currentConditions, newConditions)) {
691
- AttachmentDirectories.setBaseParams(newConditions);
703
+ AttachmentDirectories.setParams(newConditions);
692
704
  doReload = true;
693
705
  }
694
706
  if (doReload) {
695
707
  // setTreeSelection([]); // clear it; otherwise we get stale nodes after reloading AttachmentDirectories
696
- await AttachmentDirectories.reload();
708
+ await AttachmentDirectories.loadRootNodes(2);
697
709
  if (wasAlreadyLoaded) {
698
710
  const rootNodes = AttachmentDirectories.getRootNodes();
699
711
  if (rootNodes) {
@@ -705,7 +717,7 @@ function AttachmentsElement(props) {
705
717
 
706
718
  buildFiles();
707
719
  } else {
708
- Repository.clear();
720
+ Attachments.clear();
709
721
  if (usesDirectories) {
710
722
  AttachmentDirectories.clear();
711
723
  }
@@ -728,9 +740,9 @@ function AttachmentsElement(props) {
728
740
  })();
729
741
 
730
742
  return () => {
731
- Repository.off('beforeLoad', setTrue);
732
- Repository.off('load', setFalse);
733
- Repository.off('load', buildFiles);
743
+ Attachments.off('beforeLoad', setTrue);
744
+ Attachments.off('load', setFalse);
745
+ Attachments.off('load', buildFiles);
734
746
  if (usesDirectories) {
735
747
  AttachmentDirectories.off('beforeLoad', setDirectoriesTrue);
736
748
  AttachmentDirectories.off('loadRootNodes', setDirectoriesFalse);
@@ -754,7 +766,7 @@ function AttachmentsElement(props) {
754
766
  const files = getFiles();
755
767
  let content = null;
756
768
  // icon or list view
757
- if (viewMode === ATTACHMENTS_VIEW_MODES__ICON) {
769
+ if (viewMode === ATTACHMENTS_VIEW_MODES__ICON || isUploading) {
758
770
  content = <VStack
759
771
  className={clsx(
760
772
  'AttachmentsElement-icon-VStack1',
@@ -771,13 +783,14 @@ function AttachmentsElement(props) {
771
783
  <HStack
772
784
  className={clsx(
773
785
  'AttachmentsElement-HStack',
774
- 'h-full',
775
- 'flex-1',
786
+ 'gap-2',
776
787
  'flex-wrap',
788
+ 'items-start',
777
789
  files.length === 0 ? [
778
790
  // So the 'No files' text is centered
779
791
  'justify-center',
780
792
  'items-center',
793
+ 'h-full',
781
794
  ] : null,
782
795
  )}
783
796
  >
@@ -786,28 +799,30 @@ function AttachmentsElement(props) {
786
799
  let eyeProps = {};
787
800
  if (file.type && (file.type.match(/^image\//) || file.type === 'application/pdf')) {
788
801
  eyeProps = {
789
- onSee: onViewLightbox,
802
+ onSee: () => {
803
+ onViewLightbox(file.id);
804
+ },
790
805
  };
791
806
  }
792
807
 
793
808
  // Create drag source item for this file
794
- const fileEntity = Repository.getById(file.id);
795
- console.log('Drag setup for file:', file.id, 'Entity:', fileEntity, 'canCrud:', canCrud, 'usesDirectories:', usesDirectories);
796
- const dragSourceItem = {
797
- item: fileEntity, // Get the actual entity
798
- sourceComponentRef: null, // Could be set to a ref if needed
799
- getDragProxy: () => {
800
- // Custom drag preview for file items
801
- return <VStack className="bg-white border border-gray-300 rounded-lg p-3 shadow-lg max-w-[200px]">
802
- <Text className="font-semibold text-gray-800">{file.name}</Text>
803
- <Text className="text-sm text-gray-600">File</Text>
804
- </VStack>;
805
- }
806
- };
809
+ const
810
+ fileEntity = Attachments.getById(file.id),
811
+ dragSourceItem = {
812
+ item: fileEntity, // Get the actual entity
813
+ sourceComponentRef: null, // Could be set to a ref if needed
814
+ getDragProxy: () => {
815
+ // Custom drag preview for file items
816
+ return <VStack className="bg-white border border-gray-300 rounded-lg p-3 shadow-lg max-w-[200px]">
817
+ <Text className="font-semibold text-gray-800">{file.name}</Text>
818
+ <Text className="text-sm text-gray-600">File</Text>
819
+ </VStack>;
820
+ }
821
+ };
807
822
 
808
823
  return <Box
809
824
  key={file.id}
810
- className="mr-2"
825
+ className="BoxHERE mr-2"
811
826
  >
812
827
  {useFileMosaic &&
813
828
  <DraggableFileMosaic
@@ -835,7 +850,7 @@ function AttachmentsElement(props) {
835
850
  isDragSource={canCrud && usesDirectories}
836
851
  dragSourceType="Attachments"
837
852
  dragSourceItem={dragSourceItem}
838
- item={Repository.getById(file.id)}
853
+ item={Attachments.getById(file.id)}
839
854
  onDragStart={() => {
840
855
  setTimeout(() => setIsDragging(true), 50); // Delay to avoid interfering with drag initialization
841
856
  }}
@@ -846,11 +861,11 @@ function AttachmentsElement(props) {
846
861
  </Box>;
847
862
  })}
848
863
  </HStack>
849
- {Repository.total <= collapsedMax ? null :
864
+ {Attachments.total <= collapsedMax ? null :
850
865
  <Button
851
866
  onPress={toggleShowAll}
852
867
  className="AttachmentsElement-toggleShowAll mt-2"
853
- text={'Show ' + (showAll ? ' Less' : ' All ' + Repository.total)}
868
+ text={'Show ' + (showAll ? ' Less' : ' All ' + Attachments.total)}
854
869
  _text={{
855
870
  className: `
856
871
  text-grey-600
@@ -864,7 +879,7 @@ function AttachmentsElement(props) {
864
879
  </VStack>;
865
880
  } else if (viewMode === ATTACHMENTS_VIEW_MODES__LIST) {
866
881
  content = <AttachmentsGridEditor
867
- Repository={Repository}
882
+ Repository={Attachments}
868
883
  selectionMode={SELECTION_MODE_MULTI}
869
884
  showSelectHandle={false}
870
885
  disableAdd={true}
@@ -884,7 +899,7 @@ function AttachmentsElement(props) {
884
899
  {
885
900
  id: 'view',
886
901
  header: 'View',
887
- w: 70,
902
+ w: 60,
888
903
  isSortable: false,
889
904
  isEditable: false,
890
905
  isReorderable: false,
@@ -892,7 +907,7 @@ function AttachmentsElement(props) {
892
907
  isHidable: false,
893
908
  renderer: (item) => {
894
909
  return <IconButton
895
- className="w-[70px]"
910
+ className="w-[60px]"
896
911
  icon={Eye}
897
912
  _icon={{
898
913
  size: 'xl',
@@ -902,6 +917,27 @@ function AttachmentsElement(props) {
902
917
  />;
903
918
  },
904
919
  },
920
+ {
921
+ id: 'download',
922
+ header: 'Get',
923
+ w: 60,
924
+ isSortable: false,
925
+ isEditable: false,
926
+ isReorderable: false,
927
+ isResizable: false,
928
+ isHidable: false,
929
+ renderer: (item) => {
930
+ return <IconButton
931
+ className="w-[60px]"
932
+ icon={Download}
933
+ _icon={{
934
+ size: 'xl',
935
+ }}
936
+ onPress={() => onDownload(item.id)}
937
+ tooltip="Download"
938
+ />;
939
+ },
940
+ },
905
941
  {
906
942
  "id": "attachments__filename",
907
943
  "header": "Filename",
@@ -910,7 +946,7 @@ function AttachmentsElement(props) {
910
946
  "isEditable": true,
911
947
  "isReorderable": true,
912
948
  "isResizable": true,
913
- "w": 150
949
+ "w": 250
914
950
  },
915
951
  {
916
952
  "id": "attachments__size_formatted",
@@ -920,7 +956,7 @@ function AttachmentsElement(props) {
920
956
  "isEditable": false,
921
957
  "isReorderable": true,
922
958
  "isResizable": true,
923
- "w": 200
959
+ "w": 100
924
960
  },
925
961
  ]}
926
962
  areRowsDragSource={canCrud}
@@ -997,9 +1033,9 @@ function AttachmentsElement(props) {
997
1033
  maxFileSize={styles.ATTACHMENTS_MAX_FILESIZE}
998
1034
  autoClean={true}
999
1035
  uploadConfig={{
1000
- url: Repository.api.baseURL + Repository.name + '/uploadAttachment',
1036
+ url: Attachments.api.baseURL + Attachments.schema.name + '/uploadAttachment',
1001
1037
  method: 'POST',
1002
- headers: Repository.headers,
1038
+ headers: Attachments.headers,
1003
1039
  autoUpload,
1004
1040
  }}
1005
1041
  headerConfig={{
@@ -1122,12 +1158,18 @@ function AttachmentsElement(props) {
1122
1158
  isDisabled: !treeSelection.length, // disabled if no selection
1123
1159
  },
1124
1160
  {
1125
- key: 'Minus',
1161
+ key: 'Trash',
1126
1162
  text: 'Delete Directory',
1127
1163
  handler: onDeleteDirectory,
1128
- icon: Minus,
1164
+ icon: Trash,
1129
1165
  isDisabled: !treeSelection.length || !treeSelection[0].parentId, // disabled if selection is root or none
1130
1166
  },
1167
+ {
1168
+ key: 'Reload',
1169
+ text: 'Reload Directories',
1170
+ handler: onReloadDirectories,
1171
+ icon: Rotate,
1172
+ },
1131
1173
  ] : [],
1132
1174
  }}
1133
1175
  />
@@ -1158,13 +1200,27 @@ function withAdditionalProps(WrappedComponent) {
1158
1200
  const {
1159
1201
  usesDirectories = false,
1160
1202
  } = props,
1161
- AttachmentDirectories = usesDirectories ? oneHatData.getRepository('AttachmentDirectories', true) : null; // put this here; otherwise a new unique repository will be created on every render!
1203
+ [isReady, setIsReady] = useState(false),
1204
+ [AttachmentDirectories] = useState(() => (usesDirectories ? oneHatData.getRepository('AttachmentDirectories', true) : null)), // lazy instantiator, so getRepository is called only once (it's unique, so otherwise, every time this renders, we'd get a new Repository!)
1205
+ [Attachments] = useState(() => oneHatData.getRepository('Attachments', true)); // same
1162
1206
 
1207
+ useEffect(() => {
1208
+ (async () => {
1209
+ Attachments.setBaseParams(props.baseParams || {}); // have to add the baseParams here, because we're bypassing withData
1210
+ if (!isReady) {
1211
+ setIsReady(true);
1212
+ }
1213
+ })();
1214
+ }, []);
1215
+
1216
+ if (!isReady) {
1217
+ return null;
1218
+ }
1219
+
1163
1220
  return <WrappedComponent
1164
- model="Attachments"
1165
- uniqueRepository={true}
1166
1221
  reference="attachments"
1167
1222
  {...props}
1223
+ Repository={Attachments}
1168
1224
  AttachmentDirectories={AttachmentDirectories}
1169
1225
  />;
1170
1226
  };