@firecms/core 3.0.0-alpha.27 → 3.0.0-alpha.29

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 (91) hide show
  1. package/dist/components/Popover.d.ts +3 -1
  2. package/dist/core/FireCMS.d.ts +2 -2
  3. package/dist/core/builders.d.ts +5 -5
  4. package/dist/core/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +4 -2
  5. package/dist/core/components/EntityCollectionTable/filters/ReferenceFilterField.d.ts +3 -3
  6. package/dist/core/components/EntityCollectionTable/internal/EntityCollectionRowActions.d.ts +15 -11
  7. package/dist/core/components/EntityCollectionTable/internal/default_entity_actions.d.ts +5 -0
  8. package/dist/core/components/EntityCollectionTable/types.d.ts +1 -37
  9. package/dist/core/components/FieldConfigBadge.d.ts +3 -3
  10. package/dist/core/components/VirtualTable/VirtualTableHeader.d.ts +2 -2
  11. package/dist/core/components/VirtualTable/VirtualTableProps.d.ts +1 -1
  12. package/dist/core/contexts/DialogsProvider.d.ts +4 -0
  13. package/dist/core/form_field_configs.d.ts +3 -3
  14. package/dist/core/index.d.ts +1 -1
  15. package/dist/core/internal/useBuildNavigationContext.d.ts +3 -3
  16. package/dist/core/util/property_utils.d.ts +5 -5
  17. package/dist/core/util/references.d.ts +2 -2
  18. package/dist/core/util/resolutions.d.ts +6 -6
  19. package/dist/hooks/index.d.ts +1 -0
  20. package/dist/hooks/useBuildDataSource.d.ts +23 -0
  21. package/dist/hooks/useDialogsController.d.ts +11 -0
  22. package/dist/index.es.js +8996 -8583
  23. package/dist/index.es.js.map +1 -1
  24. package/dist/index.umd.js +67 -67
  25. package/dist/index.umd.js.map +1 -1
  26. package/dist/styles.d.ts +1 -1
  27. package/dist/types/collections.d.ts +36 -13
  28. package/dist/types/datasource.d.ts +120 -11
  29. package/dist/types/dialogs_controller.d.ts +30 -0
  30. package/dist/types/entity_actions.d.ts +33 -0
  31. package/dist/types/fields.d.ts +1 -1
  32. package/dist/types/firecms.d.ts +8 -8
  33. package/dist/types/firecms_context.d.ts +10 -3
  34. package/dist/types/index.d.ts +2 -1
  35. package/dist/types/navigation.d.ts +3 -3
  36. package/dist/types/plugins.d.ts +11 -10
  37. package/dist/types/properties.d.ts +1 -1
  38. package/dist/types/{field_config.d.ts → property_config.d.ts} +3 -4
  39. package/package.json +3 -3
  40. package/src/components/Popover.tsx +10 -3
  41. package/src/components/Sheet.tsx +1 -0
  42. package/src/components/TextareaAutosize.tsx +2 -1
  43. package/src/core/FireCMS.tsx +9 -6
  44. package/src/core/builders.ts +11 -11
  45. package/src/core/components/ArrayContainer.tsx +1 -1
  46. package/src/core/components/EntityCollectionTable/EntityCollectionTable.tsx +20 -13
  47. package/src/core/components/EntityCollectionTable/EntityCollectionTableProps.tsx +5 -2
  48. package/src/core/components/EntityCollectionTable/filters/ReferenceFilterField.tsx +11 -6
  49. package/src/core/components/EntityCollectionTable/internal/EntityCollectionRowActions.tsx +96 -79
  50. package/src/core/components/EntityCollectionTable/internal/PropertyTableCell.tsx +2 -2
  51. package/src/core/components/EntityCollectionTable/internal/default_entity_actions.tsx +107 -0
  52. package/src/core/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +6 -2
  53. package/src/core/components/EntityCollectionTable/types.tsx +1 -56
  54. package/src/core/components/EntityCollectionView/EntityCollectionView.tsx +67 -43
  55. package/src/core/components/FieldConfigBadge.tsx +4 -4
  56. package/src/core/components/ReferenceSelectionInner.tsx +2 -2
  57. package/src/core/components/VirtualTable/VirtualTable.tsx +4 -6
  58. package/src/core/components/VirtualTable/VirtualTableHeader.tsx +21 -18
  59. package/src/core/components/VirtualTable/VirtualTableProps.tsx +1 -1
  60. package/src/core/contexts/DialogsProvider.tsx +50 -0
  61. package/src/core/form_field_configs.tsx +5 -5
  62. package/src/core/index.tsx +1 -1
  63. package/src/core/internal/useBuildNavigationContext.tsx +13 -13
  64. package/src/core/internal/useBuildSideEntityController.tsx +9 -1
  65. package/src/core/util/property_utils.tsx +5 -5
  66. package/src/core/util/references.ts +2 -2
  67. package/src/core/util/resolutions.ts +16 -11
  68. package/src/form/EntityForm.tsx +64 -7
  69. package/src/form/PropertyFieldBinding.tsx +4 -4
  70. package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
  71. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +4 -6
  72. package/src/hooks/index.tsx +1 -0
  73. package/src/hooks/useBuildDataSource.ts +351 -0
  74. package/src/hooks/useDialogsController.tsx +14 -0
  75. package/src/hooks/useFireCMSContext.tsx +5 -11
  76. package/src/preview/property_previews/StringPropertyPreview.tsx +4 -2
  77. package/src/styles.ts +1 -1
  78. package/src/types/collections.ts +36 -17
  79. package/src/types/datasource.ts +174 -10
  80. package/src/types/dialogs_controller.tsx +31 -0
  81. package/src/types/entity_actions.tsx +36 -0
  82. package/src/types/fields.tsx +1 -1
  83. package/src/types/firecms.tsx +8 -8
  84. package/src/types/firecms_context.tsx +11 -3
  85. package/src/types/index.ts +2 -1
  86. package/src/types/navigation.ts +3 -3
  87. package/src/types/plugins.tsx +11 -10
  88. package/src/types/properties.ts +1 -1
  89. package/src/types/{field_config.tsx → property_config.tsx} +3 -4
  90. /package/dist/core/contexts/{SnackbarContext.d.ts → SnackbarProvider.d.ts} +0 -0
  91. /package/src/core/contexts/{SnackbarContext.tsx → SnackbarProvider.tsx} +0 -0
@@ -0,0 +1,107 @@
1
+ import { EntityAction } from "../../../../types";
2
+ import { DeleteEntityDialog } from "./DeleteEntityDialog";
3
+ import { ArchiveIcon, DeleteIcon, FileCopyIcon, KeyboardTabIcon } from "../../../../icons";
4
+
5
+ export const editEntityAction: EntityAction = {
6
+ icon: <KeyboardTabIcon/>,
7
+ name: "Edit",
8
+ collapsed: false,
9
+ onClick({
10
+ entity,
11
+ collection,
12
+ context,
13
+ highlightEntity,
14
+ unhighlightEntity
15
+ }): Promise<void> {
16
+ highlightEntity?.(entity);
17
+ context.onAnalyticsEvent?.("entity_click", {
18
+ path: entity.path,
19
+ entityId: entity.id
20
+ });
21
+ context.sideEntityController.open({
22
+ entityId: entity.id,
23
+ path: entity.path,
24
+ collection,
25
+ updateUrl: true,
26
+ onClose: () => unhighlightEntity?.(entity)
27
+ });
28
+ return Promise.resolve(undefined);
29
+ }
30
+ }
31
+
32
+ export const copyEntityAction: EntityAction = {
33
+ icon: <FileCopyIcon/>,
34
+ name: "Copy",
35
+ onClick({
36
+ entity,
37
+ collection,
38
+ context,
39
+ highlightEntity,
40
+ unhighlightEntity
41
+ }): Promise<void> {
42
+ highlightEntity?.(entity);
43
+ context.onAnalyticsEvent?.("copy_entity_click", {
44
+ path: entity.path,
45
+ entityId: entity.id
46
+ });
47
+ context.sideEntityController.open({
48
+ entityId: entity.id,
49
+ path: entity.path,
50
+ copy: true,
51
+ collection,
52
+ updateUrl: true,
53
+ onClose: () => unhighlightEntity?.(entity)
54
+ });
55
+ return Promise.resolve(undefined);
56
+ }
57
+ }
58
+ export const archiveEntityAction: EntityAction = {
59
+ icon: <ArchiveIcon/>,
60
+ name: "Archive",
61
+ onClick({
62
+ entity,
63
+ collection,
64
+ context,
65
+ }): Promise<void> {
66
+ // Add your code here
67
+ return Promise.resolve(undefined);
68
+ }
69
+ }
70
+
71
+ export const deleteEntityAction: EntityAction = {
72
+ icon: <DeleteIcon/>,
73
+ name: "Delete",
74
+ onClick({
75
+ entity,
76
+ fullPath,
77
+ collection,
78
+ context,
79
+ selectionController,
80
+ onCollectionChange,
81
+ sideEntityController
82
+ }): Promise<void> {
83
+ const { closeDialog } = context.dialogsController.open({
84
+ key: "delete_entity_dialog_" + entity.id,
85
+ Component: ({ open }) => {
86
+ if (!collection || !fullPath)
87
+ throw new Error("deleteEntityAction: Collection is undefined");
88
+ return <DeleteEntityDialog
89
+ entityOrEntitiesToDelete={entity}
90
+ path={fullPath}
91
+ collection={collection}
92
+ callbacks={collection.callbacks}
93
+ open={open}
94
+ onEntityDelete={() => {
95
+ context.onAnalyticsEvent?.("single_entity_deleted", {
96
+ path: fullPath
97
+ });
98
+ selectionController?.setSelectedEntities(selectionController.selectedEntities.filter(e => e.id !== entity.id));
99
+ onCollectionChange?.();
100
+ sideEntityController?.close();
101
+ }}
102
+ onClose={closeDialog}/>;
103
+ }
104
+ })
105
+ return Promise.resolve(undefined);
106
+ }
107
+ }
@@ -82,13 +82,16 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
82
82
  path,
83
83
  entityId,
84
84
  collection: inputCollection,
85
- onUpdate: setEntity
85
+ onUpdate: (e) => {
86
+ setEntity(e);
87
+ setInternalValue(e?.values);
88
+ }
86
89
  });
87
90
  } else {
88
91
  return () => {
89
92
  };
90
93
  }
91
- }, [dataSource, entityId, inputCollection, path]);
94
+ }, [dataSource, entityId, inputCollection, path, open]);
92
95
 
93
96
  const [internalValue, setInternalValue] = useState<EntityValues<M> | undefined>(entity?.values);
94
97
 
@@ -221,6 +224,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
221
224
  className={`text-gray-900 dark:text-white overflow-auto rounded rounded-md bg-white dark:bg-gray-950 ${!open ? "hidden" : ""} cursor-grab max-w-[100vw]`}>
222
225
  <Formik
223
226
  initialValues={(entity?.values ?? {}) as EntityValues<M>}
227
+ enableReinitialize={true}
224
228
  validationSchema={validationSchema}
225
229
  validateOnMount={true}
226
230
  validate={(values) => console.debug("Validating", values)}
@@ -1,59 +1,4 @@
1
- import {
2
- CollectionSize,
3
- DataSource,
4
- Entity,
5
- EntityCollection,
6
- FireCMSContext,
7
- ResolvedProperty, SelectedCellProps,
8
- User
9
- } from "../../../types";
10
-
11
- export type EntityCollectionTableProviderProps<M extends Record<string, any>, AdditionalKey extends string, UserType extends User> = {
12
-
13
- /**
14
- * Absolute collection path
15
- */
16
- path: string;
17
-
18
- /**
19
- * Use to resolve the collection properties for specific path, entity id or values
20
- */
21
- collection: EntityCollection<M>
22
-
23
- /**
24
- * List of entities that will be displayed on top, no matter the ordering.
25
- * This is used for reference fields selection
26
- */
27
- entitiesDisplayedFirst?: Entity<M>[];
28
-
29
- /**
30
- * Callback when anywhere on the table is clicked
31
- */
32
- onEntityClick?(entity: Entity<M>): void;
33
-
34
- /**
35
- * Callback when the selected size of the table is changed
36
- */
37
- onSizeChanged?(size: CollectionSize): void;
38
-
39
- /**
40
- * Can the table be edited inline
41
- */
42
- inlineEditing: ((entity: Entity<any>) => boolean) | boolean;
43
-
44
- /**
45
- * Builder for creating the buttons in each row
46
- * @param entity
47
- * @param size
48
- */
49
- tableRowActionsBuilder?: ({
50
- entity,
51
- size
52
- }: {
53
- entity: Entity<M>,
54
- size: CollectionSize
55
- }) => React.ReactNode;
56
- };
1
+ import { CollectionSize, Entity, FireCMSContext, ResolvedProperty, SelectedCellProps } from "../../../types";
57
2
 
58
3
  export type EntityCollectionTableController<M extends Record<string, any>> = {
59
4
 
@@ -6,6 +6,7 @@ import {
6
6
  AdditionalFieldDelegate,
7
7
  CollectionSize,
8
8
  Entity,
9
+ EntityAction,
9
10
  EntityCollection,
10
11
  FilterValues,
11
12
  PartialEntityCollection,
@@ -40,14 +41,14 @@ import {
40
41
  useAuthController,
41
42
  useDataSource,
42
43
  useFireCMSContext,
44
+ useLargeLayout,
43
45
  useNavigationContext,
44
46
  useSideEntityController
45
47
  } from "../../../hooks";
46
48
  import { useUserConfigurationPersistence } from "../../../hooks/useUserConfigurationPersistence";
47
49
  import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
48
50
  import { useEntityCollectionTableController } from "../EntityCollectionTable/useEntityCollectionTableController";
49
- import { Button, cn, IconButton, TextField, Tooltip, Typography } from "../../../components";
50
- import { Popover } from "../../../components/Popover";
51
+ import { Button, cn, IconButton, Popover, TextField, Tooltip, Typography } from "../../../components";
51
52
  import { Skeleton } from "../../../components/Skeleton";
52
53
  import { setIn } from "formik";
53
54
  import { getSubcollectionColumnId } from "../EntityCollectionTable/internal/common";
@@ -55,6 +56,11 @@ import { KeyboardTabIcon, SearchIcon } from "../../../icons";
55
56
  import { useColumnIds } from "./useColumnsIds";
56
57
  import { PopupFormField } from "../EntityCollectionTable/internal/popup_field/PopupFormField";
57
58
  import { GetPropertyForProps } from "../EntityCollectionTable/EntityCollectionTableProps";
59
+ import {
60
+ copyEntityAction,
61
+ deleteEntityAction,
62
+ editEntityAction
63
+ } from "../EntityCollectionTable/internal/default_entity_actions";
58
64
 
59
65
  const COLLECTION_GROUP_PARENT_ID = "collectionGroupParent";
60
66
 
@@ -116,6 +122,7 @@ export const EntityCollectionView = React.memo(
116
122
  return (userOverride ? mergeDeep(collectionProp, userOverride) : collectionProp) as EntityCollection<M>;
117
123
  }, [collectionProp, fullPath, userConfigPersistence?.getCollectionConfig]);
118
124
 
125
+ const canCreateEntities = canCreateEntity(collection, authController, fullPathToCollectionSegments(fullPath), null);
119
126
  const [selectedNavigationEntity, setSelectedNavigationEntity] = useState<Entity<M> | undefined>(undefined);
120
127
  const [deleteEntityClicked, setDeleteEntityClicked] = React.useState<Entity<M> | Entity<M>[] | undefined>(undefined);
121
128
 
@@ -271,7 +278,7 @@ export const EntityCollectionView = React.memo(
271
278
  value,
272
279
  property,
273
280
  entityId
274
- }) => dataSource.checkUniqueField(fullPath, name, value, property, entityId),
281
+ }) => dataSource.checkUniqueField(fullPath, name, value, entityId),
275
282
  [fullPath]);
276
283
 
277
284
  const onValueChange: OnCellValueChange<any, any> = ({
@@ -343,39 +350,8 @@ export const EntityCollectionView = React.memo(
343
350
 
344
351
  const displayedColumnIds = useColumnIds(resolvedCollection, true);
345
352
 
346
- const onCopyClicked = useCallback((clickedEntity: Entity<M>) => {
347
- setSelectedNavigationEntity(clickedEntity);
348
- context.onAnalyticsEvent?.("copy_entity_click", {
349
- path: clickedEntity.path,
350
- entityId: clickedEntity.id
351
- });
352
- sideEntityController.open({
353
- entityId: clickedEntity.id,
354
- path: clickedEntity.path,
355
- copy: true,
356
- collection,
357
- updateUrl: true,
358
- onClose: unselectNavigatedEntity
359
- });
360
- }, [sideEntityController, collection, fullPath, unselectNavigatedEntity]);
361
-
362
- const onEditClicked = useCallback((clickedEntity: Entity<M>) => {
363
- setSelectedNavigationEntity(clickedEntity);
364
- context.onAnalyticsEvent?.("entity_click", {
365
- path: clickedEntity.path,
366
- entityId: clickedEntity.id
367
- });
368
- sideEntityController.open({
369
- entityId: clickedEntity.id,
370
- path: clickedEntity.path,
371
- collection,
372
- updateUrl: true,
373
- onClose: unselectNavigatedEntity
374
- });
375
- }, [sideEntityController, collection, fullPath, unselectNavigatedEntity]);
376
-
377
353
  const additionalFields = useMemo(() => {
378
- const subcollectionColumns: AdditionalFieldDelegate<M, any, any>[] = collection.subcollections?.map((subcollection) => {
354
+ const subcollectionColumns: AdditionalFieldDelegate<M, any>[] = collection.subcollections?.map((subcollection) => {
379
355
  return {
380
356
  id: getSubcollectionColumnId(subcollection),
381
357
  name: subcollection.name,
@@ -401,7 +377,7 @@ export const EntityCollectionView = React.memo(
401
377
  };
402
378
  }) ?? [];
403
379
 
404
- const collectionGroupParentCollections: AdditionalFieldDelegate<M, any, any>[] = collection.collectionGroup
380
+ const collectionGroupParentCollections: AdditionalFieldDelegate<M, any>[] = collection.collectionGroup
405
381
  ? [{
406
382
  id: COLLECTION_GROUP_PARENT_ID,
407
383
  name: "Parent entities",
@@ -432,6 +408,32 @@ export const EntityCollectionView = React.memo(
432
408
  ];
433
409
  }, [collection, fullPath]);
434
410
 
411
+ const updateLastDeleteTimestamp = useCallback(() => {
412
+ setLastDeleteTimestamp(Date.now());
413
+ }, []);
414
+
415
+ const largeLayout = useLargeLayout();
416
+
417
+ const getActionsForEntity = useCallback(({ entity, customEntityActions }: { entity?: Entity<M>, customEntityActions?: EntityAction[] }): EntityAction[] => {
418
+ const deleteEnabled = entity ? canDeleteEntity(collection, authController, fullPathToCollectionSegments(fullPath), entity) : true;
419
+ const actions: EntityAction[] = [editEntityAction];
420
+ if (createEnabled)
421
+ actions.push(copyEntityAction);
422
+ if (deleteEnabled)
423
+ actions.push(deleteEntityAction);
424
+ if (customEntityActions)
425
+ actions.push(...customEntityActions);
426
+ return actions;
427
+ }, [authController, collection, createEnabled, fullPath]);
428
+
429
+ const getIdColumnWidth = useCallback(() => {
430
+ const entityActions = getActionsForEntity({});
431
+ const collapsedActions = entityActions.filter(a => a.collapsed !== false);
432
+ const uncollapsedActions = entityActions.filter(a => a.collapsed === false);
433
+ const actionsWidth = uncollapsedActions.length * (largeLayout ? 40 : 30);
434
+ return (largeLayout ? (80 + actionsWidth) : (70 + actionsWidth)) + (collapsedActions.length > 0 ? (largeLayout ? 40 : 30) : 0);
435
+ }, [largeLayout, getActionsForEntity]);
436
+
435
437
  const tableRowActionsBuilder = useCallback(({
436
438
  entity,
437
439
  size,
@@ -446,7 +448,7 @@ export const EntityCollectionView = React.memo(
446
448
 
447
449
  const isSelected = isEntitySelected(entity);
448
450
 
449
- const deleteEnabled = canDeleteEntity(collection, authController, fullPathToCollectionSegments(fullPath), entity);
451
+ const actions = getActionsForEntity({ entity, customEntityActions: collection.entityActions });
450
452
 
451
453
  return (
452
454
  <EntityCollectionRowActions
@@ -456,15 +458,18 @@ export const EntityCollectionView = React.memo(
456
458
  isSelected={isSelected}
457
459
  selectionEnabled={selectionEnabled}
458
460
  size={size}
459
- toggleEntitySelection={toggleEntitySelection}
460
- onEditClicked={onEditClicked}
461
- onCopyClicked={createEnabled ? onCopyClicked : undefined}
462
- onDeleteClicked={deleteEnabled ? onSingleDeleteClick : undefined}
461
+ highlightEntity={setSelectedNavigationEntity}
462
+ unhighlightEntity={unselectNavigatedEntity}
463
+ collection={collection}
464
+ fullPath={fullPath}
465
+ actions={actions}
463
466
  hideId={collection?.hideIdFromCollection}
467
+ onCollectionChange={updateLastDeleteTimestamp}
468
+ selectionController={usedSelectionController}
464
469
  />
465
470
  );
466
471
 
467
- }, [isEntitySelected, collection, authController, fullPath, selectionEnabled, toggleEntitySelection, onEditClicked, createEnabled, onCopyClicked]);
472
+ }, [isEntitySelected, collection, authController, fullPath, selectionEnabled, toggleEntitySelection, createEnabled]);
468
473
 
469
474
  const title = <Popover
470
475
  open={popOverOpen}
@@ -522,6 +527,7 @@ export const EntityCollectionView = React.memo(
522
527
  propertyKey={propertyKey}
523
528
  property={property}
524
529
  fullPath={fullPath}
530
+ collection={collection}
525
531
  parentPathSegments={parentPathSegments ?? []}/>;
526
532
  })}
527
533
  </>;
@@ -569,10 +575,25 @@ export const EntityCollectionView = React.memo(
569
575
  selectionEnabled={selectionEnabled}
570
576
  collectionEntitiesCount={docsCount}
571
577
  />}
578
+ emptyComponent={canCreateEntities
579
+ ? <div className="flex flex-col items-center justify-center">
580
+ <Typography variant={"subtitle2"}>So empty...</Typography>
581
+ <Button
582
+ color={"primary"}
583
+ variant={"outlined"}
584
+ onClick={onNewClick}
585
+ className="mt-4"
586
+ >
587
+ Create your first entity
588
+ </Button>
589
+ </div>
590
+ : undefined
591
+ }
572
592
  hoverRow={hoverRow}
573
593
  inlineEditing={checkInlineEditing()}
574
594
  AdditionalHeaderWidget={buildAdditionalHeaderWidget}
575
595
  AddColumnComponent={addColumnComponentInternal}
596
+ getIdColumnWidth={getIdColumnWidth}
576
597
  additionalIDHeaderWidget={<EntityIdHeaderWidget
577
598
  path={fullPath}
578
599
  collection={collection}/>}
@@ -690,7 +711,10 @@ function buildPropertyWidthOverwrite(key: string, width: number): PartialEntityC
690
711
  return { properties: { [key]: { columnWidth: width } } } as PartialEntityCollection;
691
712
  }
692
713
 
693
- function EntityIdHeaderWidget({ collection, path }: {
714
+ function EntityIdHeaderWidget({
715
+ collection,
716
+ path
717
+ }: {
694
718
  collection: EntityCollection,
695
719
  path: string
696
720
  }) {
@@ -1,14 +1,14 @@
1
1
  import { getIconForWidget } from "../util";
2
- import { FieldConfig } from "../../types";
2
+ import { PropertyConfig } from "../../types";
3
3
 
4
- export function FieldConfigBadge({ fieldConfig }: { fieldConfig: FieldConfig | undefined }): React.ReactNode {
4
+ export function FieldConfigBadge({ propertyConfig }: { propertyConfig: PropertyConfig | undefined }): React.ReactNode {
5
5
  const classes = "h-8 w-8 p-1 rounded-full shadow text-white";
6
6
 
7
7
  return <div
8
8
  className={classes}
9
9
  style={{
10
- background: fieldConfig?.color ?? "#888"
10
+ background: propertyConfig?.color ?? "#888"
11
11
  }}>
12
- {getIconForWidget(fieldConfig, "medium")}
12
+ {getIconForWidget(propertyConfig, "medium")}
13
13
  </div>
14
14
  }
@@ -230,9 +230,9 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
230
230
  size={size}
231
231
  isSelected={isSelected}
232
232
  selectionEnabled={multiselect}
233
- toggleEntitySelection={toggleEntitySelection}
234
233
  hideId={collection?.hideIdFromCollection}
235
- />;
234
+ fullPath={fullPath}
235
+ selectionController={selectionController}/>;
236
236
 
237
237
  }, [multiselect, selectionController.selectedEntities, toggleEntitySelection, collection?.hideIdFromCollection]);
238
238
 
@@ -102,7 +102,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
102
102
  onFilterUpdate,
103
103
  sortBy,
104
104
  error,
105
- emptyMessage,
105
+ emptyComponent,
106
106
  onSortByUpdate,
107
107
  loading,
108
108
  cellRenderer,
@@ -249,13 +249,11 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
249
249
  if (loading)
250
250
  return <CircularProgressCenter/>;
251
251
  return <div
252
- className="flex overflow-auto items-center justify-center p-2 gap-2 h-full">
252
+ className="flex flex-col overflow-auto items-center justify-center p-2 gap-2 h-full">
253
253
  <AssignmentIcon/>
254
- <Typography>
255
- {emptyMessage}
256
- </Typography>
254
+ {emptyComponent}
257
255
  </div>;
258
- }, [emptyMessage, loading]);
256
+ }, [emptyComponent, loading]);
259
257
 
260
258
  const empty = !loading && (data?.length ?? 0) === 0;
261
259
  const customView = error ? buildErrorView() : (empty ? buildEmptyView() : undefined);
@@ -1,4 +1,4 @@
1
- import React, { RefObject, useCallback, useEffect, useState } from "react";
1
+ import React, { RefObject, useCallback, useEffect, useRef, useState } from "react";
2
2
  import equal from "react-fast-compare";
3
3
 
4
4
  import { VirtualTableColumn, VirtualTableSort, VirtualTableWhereFilterOp } from "./VirtualTableProps";
@@ -13,8 +13,8 @@ interface FilterFormProps<T> {
13
13
  filter?: [VirtualTableWhereFilterOp, any];
14
14
  onHover: boolean,
15
15
  createFilterField: (props: FilterFormFieldProps<T>) => React.ReactNode;
16
- popupOpen: boolean;
17
- setPopupOpen: (open: boolean) => void;
16
+ hidden: boolean;
17
+ setHidden: (hidden: boolean) => void;
18
18
  }
19
19
 
20
20
  export type FilterFormFieldProps<CustomProps> = {
@@ -22,8 +22,8 @@ export type FilterFormFieldProps<CustomProps> = {
22
22
  filterValue: [VirtualTableWhereFilterOp, any] | undefined,
23
23
  setFilterValue: (filterValue?: [VirtualTableWhereFilterOp, any]) => void;
24
24
  column: VirtualTableColumn<CustomProps>;
25
- popupOpen: boolean;
26
- setPopupOpen: (open: boolean) => void;
25
+ hidden: boolean;
26
+ setHidden: (hidden: boolean) => void;
27
27
  };
28
28
 
29
29
  type VirtualTableHeaderProps<M extends Record<string, any>> = {
@@ -58,6 +58,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
58
58
  const [onHover, setOnHover] = useState(false);
59
59
 
60
60
  const [openFilter, setOpenFilter] = React.useState(false);
61
+ const [hidden, setHidden] = React.useState(false);
61
62
 
62
63
  const handleSettingsClick = useCallback((event: any) => {
63
64
  setOpenFilter(true);
@@ -145,6 +146,8 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
145
146
  <Popover
146
147
  open={openFilter}
147
148
  onOpenChange={setOpenFilter}
149
+ className={hidden ? "hidden" : undefined}
150
+ modal={true}
148
151
  trigger={
149
152
  <IconButton
150
153
  className={onHover || openFilter ? "bg-white dark:bg-gray-950" : undefined}
@@ -153,17 +156,18 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
153
156
  <FilterListIcon size={"small"}/>
154
157
  </IconButton>}
155
158
  >
156
- {column.filter &&
157
- <FilterForm column={column}
158
- filter={filter}
159
- onHover={onHover}
160
- onFilterUpdate={update}
161
- createFilterField={createFilterField}
162
- popupOpen={openFilter}
163
- setPopupOpen={setOpenFilter}/>}
159
+ <FilterForm column={column}
160
+ filter={filter}
161
+ onHover={onHover}
162
+ onFilterUpdate={update}
163
+ createFilterField={createFilterField}
164
+ hidden={hidden}
165
+ setHidden={setHidden}/>
166
+
164
167
  </Popover>
165
168
 
166
169
  </Badge>
170
+
167
171
  </div>}
168
172
 
169
173
  {column.resizable && <div
@@ -176,7 +180,6 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
176
180
  />}
177
181
  </div>
178
182
 
179
-
180
183
  </ErrorBoundary>
181
184
  );
182
185
  }, equal) as React.FunctionComponent<VirtualTableHeaderProps<any>>;
@@ -187,8 +190,8 @@ function FilterForm<M>({
187
190
  filter,
188
191
  onHover,
189
192
  createFilterField,
190
- popupOpen,
191
- setPopupOpen,
193
+ hidden,
194
+ setHidden,
192
195
  }: FilterFormProps<M>) {
193
196
 
194
197
  const id = column.key;
@@ -216,8 +219,8 @@ function FilterForm<M>({
216
219
  filterValue: filterInternal,
217
220
  setFilterValue: setFilterInternal,
218
221
  column,
219
- popupOpen,
220
- setPopupOpen
222
+ hidden,
223
+ setHidden
221
224
  });
222
225
 
223
226
  if (!filterField) return null;
@@ -102,7 +102,7 @@ export interface VirtualTableProps<T extends Record<string, any>> {
102
102
  /**
103
103
  * Message displayed when there is no data
104
104
  */
105
- emptyMessage?: string;
105
+ emptyComponent?: React.ReactNode;
106
106
 
107
107
  /**
108
108
  * Is the table in a loading state
@@ -0,0 +1,50 @@
1
+ import React, { PropsWithChildren, useCallback, useRef, useState } from "react";
2
+ import { DialogControllerEntryProps, DialogsController } from "../../types/dialogs_controller";
3
+
4
+ export const DialogsControllerContext = React.createContext<DialogsController>({} as DialogsController);
5
+
6
+ export const DialogsProvider: React.FC<PropsWithChildren<{}>> = ({ children }) => {
7
+
8
+ const [dialogEntries, setDialogEntries] = useState<DialogControllerEntryProps[]>([]);
9
+ const dialogEntriesRef = useRef<DialogControllerEntryProps[]>(dialogEntries);
10
+
11
+ const updateDialogEntries = (newPanels: DialogControllerEntryProps[]) => {
12
+ dialogEntriesRef.current = newPanels;
13
+ setDialogEntries(newPanels);
14
+ };
15
+
16
+ const close = useCallback(() => {
17
+
18
+ if (dialogEntries.length === 0)
19
+ return;
20
+
21
+ const updatedPanels = [...dialogEntries.slice(0, -1)];
22
+ updateDialogEntries(updatedPanels);
23
+
24
+ }, [dialogEntries]);
25
+
26
+ const open = useCallback((dialogEntry: DialogControllerEntryProps) => {
27
+
28
+ const updatedPanels = [...dialogEntries, dialogEntry];
29
+ updateDialogEntries(updatedPanels);
30
+
31
+ return {
32
+ closeDialog: () => {
33
+ const updatedPanels = dialogEntriesRef.current.filter(e => e.key !== dialogEntry.key);
34
+ updateDialogEntries(updatedPanels);
35
+ }
36
+ }
37
+ }, [dialogEntries]);
38
+
39
+ return (
40
+ <DialogsControllerContext.Provider value={{
41
+ open,
42
+ close
43
+ }}>
44
+ {children}
45
+ {dialogEntries.map((entry, i) => <entry.Component
46
+ key={`dialog_${i}`}
47
+ open={true}/>)}
48
+ </DialogsControllerContext.Provider>
49
+ );
50
+ };
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
 
3
- import { ArrayProperty, FieldConfig, FieldProps, Property, ResolvedProperty } from "../types";
3
+ import { ArrayProperty, PropertyConfig, FieldProps, Property, ResolvedProperty } from "../types";
4
4
  import {
5
5
  ArrayCustomShapedFieldBinding,
6
6
  ArrayOfReferencesFieldBinding,
@@ -44,7 +44,7 @@ export function isDefaultFieldConfigId(id: string) {
44
44
  return Object.keys(DEFAULT_FIELD_CONFIGS).includes(id);
45
45
  }
46
46
 
47
- export const DEFAULT_FIELD_CONFIGS: Record<string, FieldConfig<any>> = {
47
+ export const DEFAULT_FIELD_CONFIGS: Record<string, PropertyConfig<any>> = {
48
48
  text_field: {
49
49
  key: "text_field",
50
50
  name: "Text field",
@@ -313,7 +313,7 @@ export const DEFAULT_FIELD_CONFIGS: Record<string, FieldConfig<any>> = {
313
313
  }
314
314
  };
315
315
 
316
- export function getFieldConfig(property: Property | ResolvedProperty, customFields: Record<string, FieldConfig<any>>): FieldConfig | undefined {
316
+ export function getFieldConfig(property: Property | ResolvedProperty, customFields: Record<string, PropertyConfig<any>>): PropertyConfig | undefined {
317
317
  const fieldId = getFieldId(property);
318
318
  const defaultFieldId = getDefaultFieldId(property);
319
319
  // console.log("fieldId", { fieldId });
@@ -386,7 +386,7 @@ export function getDefaultFieldId(property: Property | ResolvedProperty) {
386
386
  }
387
387
 
388
388
  export function getFieldId(property: Property | ResolvedProperty): string | undefined {
389
- if (property.fieldConfig)
390
- return property.fieldConfig;
389
+ if (property.propertyConfig)
390
+ return property.propertyConfig;
391
391
  return getDefaultFieldId(property);
392
392
  }