@firecms/core 3.0.0-beta.8 → 3.0.0-beta.9

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 (190) hide show
  1. package/dist/app/Drawer.d.ts +1 -1
  2. package/dist/app/Scaffold.d.ts +1 -1
  3. package/dist/components/ArrayContainer.d.ts +2 -1
  4. package/dist/components/CircularProgressCenter.d.ts +1 -1
  5. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +0 -1
  6. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +1 -1
  7. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  8. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +1 -1
  9. package/dist/components/EntityPreview.d.ts +1 -1
  10. package/dist/components/ErrorView.d.ts +1 -1
  11. package/dist/components/HomePage/DefaultHomePage.d.ts +1 -1
  12. package/dist/components/HomePage/NavigationCardBinding.d.ts +1 -1
  13. package/dist/components/PropertyIdCopyTooltip.d.ts +8 -0
  14. package/dist/components/VirtualTable/VirtualTableProps.d.ts +10 -6
  15. package/dist/components/common/useColumnsIds.d.ts +2 -1
  16. package/dist/components/index.d.ts +1 -0
  17. package/dist/core/DefaultAppBar.d.ts +1 -1
  18. package/dist/core/DefaultDrawer.d.ts +1 -1
  19. package/dist/core/DrawerNavigationItem.d.ts +2 -1
  20. package/dist/core/EntityEditView.d.ts +3 -3
  21. package/dist/core/FireCMS.d.ts +1 -1
  22. package/dist/core/NavigationRoutes.d.ts +1 -1
  23. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  24. package/dist/form/components/FormikArrayContainer.d.ts +2 -1
  25. package/dist/form/components/LabelWithIcon.d.ts +1 -1
  26. package/dist/form/components/LabelWithIconAndTooltip.d.ts +15 -0
  27. package/dist/form/components/index.d.ts +1 -0
  28. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  29. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  30. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  31. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  32. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  33. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +11 -0
  34. package/dist/form/field_bindings/{MultiSelectBinding.d.ts → MultiSelectFieldBinding.d.ts} +1 -1
  35. package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
  36. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  37. package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
  38. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +1 -1
  39. package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
  40. package/dist/form/index.d.ts +3 -3
  41. package/dist/form/useClearRestoreValue.d.ts +2 -2
  42. package/dist/hooks/useProjectLog.d.ts +2 -2
  43. package/dist/index.es.js +14127 -11014
  44. package/dist/index.es.js.map +1 -1
  45. package/dist/index.umd.js +19987 -587
  46. package/dist/index.umd.js.map +1 -1
  47. package/dist/preview/PropertyPreviewProps.d.ts +1 -1
  48. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  49. package/dist/preview/components/ReferencePreview.d.ts +1 -1
  50. package/dist/types/auth.d.ts +1 -1
  51. package/dist/types/collections.d.ts +12 -3
  52. package/dist/types/datasource.d.ts +35 -22
  53. package/dist/types/entities.d.ts +1 -0
  54. package/dist/types/entity_overrides.d.ts +2 -2
  55. package/dist/types/fields.d.ts +20 -15
  56. package/dist/types/navigation.d.ts +1 -0
  57. package/dist/types/properties.d.ts +18 -18
  58. package/dist/types/side_dialogs_controller.d.ts +10 -0
  59. package/dist/types/storage.d.ts +75 -0
  60. package/dist/util/entities.d.ts +1 -1
  61. package/dist/util/icon_list.d.ts +5 -1
  62. package/dist/util/icons.d.ts +1 -0
  63. package/dist/util/index.d.ts +1 -0
  64. package/dist/util/plurals.d.ts +0 -2
  65. package/dist/util/property_utils.d.ts +1 -1
  66. package/dist/util/resolutions.d.ts +15 -1
  67. package/dist/util/storage.d.ts +1 -1
  68. package/dist/util/useStorageUploadController.d.ts +2 -2
  69. package/package.json +15 -17
  70. package/src/app/Drawer.tsx +1 -1
  71. package/src/app/Scaffold.tsx +22 -27
  72. package/src/components/ArrayContainer.tsx +15 -10
  73. package/src/components/CircularProgressCenter.tsx +1 -1
  74. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +4 -9
  75. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +2 -2
  76. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +4 -2
  77. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +21 -14
  78. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +12 -4
  79. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +17 -19
  80. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +19 -7
  81. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -5
  82. package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -25
  83. package/src/components/EntityCollectionView/useSelectionController.tsx +19 -7
  84. package/src/components/EntityPreview.tsx +30 -31
  85. package/src/components/ErrorView.tsx +1 -1
  86. package/src/components/HomePage/DefaultHomePage.tsx +24 -18
  87. package/src/components/HomePage/NavigationCardBinding.tsx +1 -1
  88. package/src/components/HomePage/SmallNavigationCard.tsx +1 -2
  89. package/src/components/PropertyIdCopyTooltip.tsx +48 -0
  90. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +7 -6
  91. package/src/components/SearchIconsView.tsx +5 -2
  92. package/src/components/SelectableTable/SelectableTable.tsx +2 -4
  93. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +1 -1
  94. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +54 -7
  95. package/src/components/VirtualTable/VirtualTable.tsx +33 -9
  96. package/src/components/VirtualTable/VirtualTableCell.tsx +1 -9
  97. package/src/components/VirtualTable/VirtualTableProps.tsx +12 -8
  98. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +3 -3
  99. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +2 -1
  100. package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +2 -1
  101. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +14 -28
  102. package/src/components/common/useColumnsIds.tsx +13 -0
  103. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +12 -12
  104. package/src/components/common/useTableSearchHelper.ts +1 -0
  105. package/src/components/index.tsx +1 -0
  106. package/src/core/DefaultAppBar.tsx +3 -3
  107. package/src/core/DefaultDrawer.tsx +18 -12
  108. package/src/core/DrawerNavigationItem.tsx +31 -27
  109. package/src/core/EntityEditView.tsx +136 -109
  110. package/src/core/FireCMS.tsx +2 -2
  111. package/src/core/NavigationRoutes.tsx +1 -1
  112. package/src/core/SideDialogs.tsx +1 -0
  113. package/src/core/field_configs.tsx +5 -5
  114. package/src/form/PropertyFieldBinding.tsx +24 -14
  115. package/src/form/components/CustomIdField.tsx +4 -2
  116. package/src/form/components/FieldHelperText.tsx +1 -1
  117. package/src/form/components/FormikArrayContainer.tsx +4 -1
  118. package/src/form/components/LabelWithIcon.tsx +27 -19
  119. package/src/form/components/LabelWithIconAndTooltip.tsx +28 -0
  120. package/src/form/components/StorageItemPreview.tsx +1 -0
  121. package/src/form/components/StorageUploadProgress.tsx +0 -1
  122. package/src/form/components/index.tsx +1 -0
  123. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +22 -14
  124. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +24 -22
  125. package/src/form/field_bindings/BlockFieldBinding.tsx +13 -11
  126. package/src/form/field_bindings/DateTimeFieldBinding.tsx +17 -14
  127. package/src/form/field_bindings/KeyValueFieldBinding.tsx +8 -7
  128. package/src/form/field_bindings/MapFieldBinding.tsx +16 -24
  129. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +150 -0
  130. package/src/form/field_bindings/{MultiSelectBinding.tsx → MultiSelectFieldBinding.tsx} +25 -20
  131. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +9 -7
  132. package/src/form/field_bindings/ReferenceFieldBinding.tsx +27 -18
  133. package/src/form/field_bindings/RepeatFieldBinding.tsx +27 -17
  134. package/src/form/field_bindings/SelectFieldBinding.tsx +17 -10
  135. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +36 -17
  136. package/src/form/field_bindings/SwitchFieldBinding.tsx +9 -5
  137. package/src/form/field_bindings/TextFieldBinding.tsx +26 -23
  138. package/src/form/index.tsx +4 -9
  139. package/src/form/useClearRestoreValue.tsx +2 -2
  140. package/src/hooks/data/delete.ts +2 -1
  141. package/src/hooks/data/save.ts +5 -2
  142. package/src/hooks/data/useDataSource.tsx +11 -3
  143. package/src/hooks/useBuildNavigationController.tsx +10 -3
  144. package/src/hooks/useProjectLog.tsx +18 -7
  145. package/src/internal/useBuildDataSource.ts +59 -76
  146. package/src/internal/useBuildSideDialogsController.tsx +1 -0
  147. package/src/internal/useBuildSideEntityController.tsx +17 -4
  148. package/src/internal/useUnsavedChangesDialog.tsx +3 -1
  149. package/src/preview/PropertyPreview.tsx +2 -2
  150. package/src/preview/PropertyPreviewProps.tsx +1 -1
  151. package/src/preview/components/BooleanPreview.tsx +1 -1
  152. package/src/preview/components/EnumValuesChip.tsx +1 -1
  153. package/src/preview/components/ImagePreview.tsx +4 -2
  154. package/src/preview/components/ReferencePreview.tsx +2 -13
  155. package/src/preview/components/UrlComponentPreview.tsx +1 -1
  156. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
  157. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +1 -1
  158. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +1 -1
  159. package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -2
  160. package/src/preview/property_previews/ArrayPropertyPreview.tsx +1 -1
  161. package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
  162. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +2 -2
  163. package/src/preview/property_previews/StringPropertyPreview.tsx +2 -2
  164. package/src/preview/util.ts +2 -2
  165. package/src/types/auth.tsx +1 -1
  166. package/src/types/collections.ts +15 -4
  167. package/src/types/datasource.ts +42 -27
  168. package/src/types/entities.ts +2 -0
  169. package/src/types/entity_overrides.tsx +2 -2
  170. package/src/types/fields.tsx +23 -17
  171. package/src/types/navigation.ts +1 -0
  172. package/src/types/properties.ts +18 -17
  173. package/src/types/side_dialogs_controller.tsx +13 -0
  174. package/src/types/storage.ts +82 -0
  175. package/src/util/entities.ts +1 -1
  176. package/src/util/icon_list.ts +14 -9
  177. package/src/util/icons.tsx +8 -2
  178. package/src/util/index.ts +1 -0
  179. package/src/util/plurals.ts +0 -2
  180. package/src/util/property_utils.tsx +1 -1
  181. package/src/util/references.ts +3 -0
  182. package/src/util/resolutions.ts +43 -11
  183. package/src/util/storage.ts +6 -2
  184. package/src/util/useStorageUploadController.tsx +15 -7
  185. package/dist/components/PropertyIdCopyTooltipContent.d.ts +0 -3
  186. package/dist/form/PropertiesForm.d.ts +0 -8
  187. package/dist/form/field_bindings/MarkdownFieldBinding.d.ts +0 -9
  188. package/src/components/PropertyIdCopyTooltipContent.tsx +0 -27
  189. package/src/form/PropertiesForm.tsx +0 -81
  190. package/src/form/field_bindings/MarkdownFieldBinding.tsx +0 -695
@@ -45,7 +45,7 @@ import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
45
45
  import {
46
46
  AddIcon,
47
47
  Button,
48
- cls,
48
+ cls, focusedDisabled,
49
49
  IconButton,
50
50
  KeyboardTabIcon,
51
51
  Markdown,
@@ -117,7 +117,7 @@ export type EntityCollectionViewProps<M extends Record<string, any>> = {
117
117
  *
118
118
  * @param fullPath
119
119
  * @param collection
120
- * @constructor
120
+
121
121
  * @group Components
122
122
  */
123
123
  export const EntityCollectionView = React.memo(
@@ -153,7 +153,7 @@ export const EntityCollectionView = React.memo(
153
153
  }, [collection]);
154
154
 
155
155
  const canCreateEntities = canCreateEntity(collection, authController, fullPath, null);
156
- const [selectedNavigationEntity, setSelectedNavigationEntity] = useState<Entity<M> | undefined>(undefined);
156
+ const [highlightedEntity, setHighlightedEntity] = useState<Entity<M> | undefined>(undefined);
157
157
  const [deleteEntityClicked, setDeleteEntityClicked] = React.useState<Entity<M> | Entity<M>[] | undefined>(undefined);
158
158
 
159
159
  const [lastDeleteTimestamp, setLastDeleteTimestamp] = React.useState<number>(0);
@@ -162,12 +162,12 @@ export const EntityCollectionView = React.memo(
162
162
  const [docsCount, setDocsCount] = useState<number>(0);
163
163
 
164
164
  const unselectNavigatedEntity = useCallback(() => {
165
- const currentSelection = selectedNavigationEntity;
165
+ const currentSelection = highlightedEntity;
166
166
  setTimeout(() => {
167
- if (currentSelection === selectedNavigationEntity)
168
- setSelectedNavigationEntity(undefined);
167
+ if (currentSelection === highlightedEntity)
168
+ setHighlightedEntity(undefined);
169
169
  }, 2400);
170
- }, [selectedNavigationEntity]);
170
+ }, [highlightedEntity]);
171
171
 
172
172
  const checkInlineEditing = useCallback((entity?: Entity<any>): boolean => {
173
173
  const collection = collectionRef.current;
@@ -186,13 +186,12 @@ export const EntityCollectionView = React.memo(
186
186
  const usedSelectionController = collection.selectionController ?? selectionController;
187
187
  const {
188
188
  selectedEntities,
189
- isEntitySelected,
190
189
  setSelectedEntities
191
190
  } = usedSelectionController;
192
191
 
193
- useEffect(() => {
194
- setDeleteEntityClicked(undefined);
195
- }, [selectedEntities]);
192
+ // useEffect(() => {
193
+ // setDeleteEntityClicked(undefined);
194
+ // }, [selectedEntities]);
196
195
 
197
196
  const tableController = useDataSourceEntityCollectionTableController<M>({
198
197
  fullPath,
@@ -208,9 +207,8 @@ export const EntityCollectionView = React.memo(
208
207
  }, [tableController.setPopupCell]);
209
208
 
210
209
  const onEntityClick = useCallback((clickedEntity: Entity<M>) => {
211
- console.log("Entity clicked", clickedEntity)
212
210
  const collection = collectionRef.current;
213
- setSelectedNavigationEntity(clickedEntity);
211
+ setHighlightedEntity(clickedEntity);
214
212
  analyticsController.onAnalyticsEvent?.("edit_entity_clicked", {
215
213
  path: clickedEntity.path,
216
214
  entityId: clickedEntity.id
@@ -307,7 +305,7 @@ export const EntityCollectionView = React.memo(
307
305
  value,
308
306
  property,
309
307
  entityId
310
- }) => dataSource.checkUniqueField(fullPath, name, value, entityId),
308
+ }) => dataSource.checkUniqueField(fullPath, name, value, entityId, collection),
311
309
  [fullPath]);
312
310
 
313
311
  const onValueChange: OnCellValueChange<any, any> = ({
@@ -420,7 +418,7 @@ export const EntityCollectionView = React.memo(
420
418
  <ReferencePreview
421
419
  key={reference.path + "/" + reference.id}
422
420
  reference={reference}
423
- size={"tiny"}/>
421
+ size={"smallest"}/>
424
422
  );
425
423
  })}
426
424
  </div>
@@ -468,7 +466,7 @@ export const EntityCollectionView = React.memo(
468
466
  return (largeLayout ? (80 + actionsWidth) : (70 + actionsWidth)) + (collapsedActions.length > 0 ? (largeLayout ? 40 : 30) : 0);
469
467
  };
470
468
 
471
- const tableRowActionsBuilder = ({
469
+ const tableRowActionsBuilder = useCallback(({
472
470
  entity,
473
471
  size,
474
472
  width,
@@ -480,7 +478,7 @@ export const EntityCollectionView = React.memo(
480
478
  frozen?: boolean
481
479
  }) => {
482
480
 
483
- const isSelected = isEntitySelected(entity);
481
+ const isSelected = usedSelectionController.selectedEntities.map(e => e.id).includes(entity.id);
484
482
 
485
483
  const actions = getActionsForEntity({
486
484
  entity,
@@ -495,7 +493,7 @@ export const EntityCollectionView = React.memo(
495
493
  isSelected={isSelected}
496
494
  selectionEnabled={selectionEnabled}
497
495
  size={size}
498
- highlightEntity={setSelectedNavigationEntity}
496
+ highlightEntity={setHighlightedEntity}
499
497
  unhighlightEntity={unselectNavigatedEntity}
500
498
  collection={collection}
501
499
  fullPath={fullPath}
@@ -506,7 +504,7 @@ export const EntityCollectionView = React.memo(
506
504
  />
507
505
  );
508
506
 
509
- };
507
+ }, [updateLastDeleteTimestamp, usedSelectionController]);
510
508
 
511
509
  const title = <Popover
512
510
  open={popOverOpen}
@@ -610,7 +608,7 @@ export const EntityCollectionView = React.memo(
610
608
  uniqueFieldValidator={uniqueFieldValidator}
611
609
  title={title}
612
610
  selectionController={usedSelectionController}
613
- highlightedEntities={selectedNavigationEntity ? [selectedNavigationEntity] : []}
611
+ highlightedEntities={highlightedEntity ? [highlightedEntity] : []}
614
612
  defaultSize={collection.defaultSize}
615
613
  properties={resolvedCollection.properties}
616
614
  getPropertyFor={getPropertyFor}
@@ -662,19 +660,19 @@ export const EntityCollectionView = React.memo(
662
660
  collection={collection}/>}
663
661
  />
664
662
 
665
- <PopupFormField
663
+ {popupCell && <PopupFormField
666
664
  key={`popup_form_${popupCell?.propertyKey}_${popupCell?.entity?.id}`}
667
665
  open={Boolean(popupCell)}
668
666
  onClose={onPopupClose}
669
667
  cellRect={popupCell?.cellRect}
670
668
  propertyKey={popupCell?.propertyKey}
671
669
  collection={collection}
672
- entity={popupCell?.entity}
670
+ entity={popupCell.entity}
673
671
  tableKey={tableKey.current}
674
672
  customFieldValidator={uniqueFieldValidator}
675
673
  path={resolvedFullPath}
676
674
  onCellValueChange={onValueChange}
677
- container={containerRef.current}/>
675
+ container={containerRef.current}/>}
678
676
 
679
677
  {deleteEntityClicked &&
680
678
  <DeleteEntityDialog
@@ -782,10 +780,13 @@ function EntityIdHeaderWidget({
782
780
  const [searchString, setSearchString] = React.useState("");
783
781
  const sideEntityController = useSideEntityController();
784
782
  return (
785
- <Tooltip title={!openPopup ? "Find by ID" : undefined}>
783
+ <Tooltip title={!openPopup ? "Find by ID" : undefined} asChild={false}>
786
784
  <Popover
787
785
  open={openPopup}
788
786
  onOpenChange={setOpenPopup}
787
+ sideOffset={0}
788
+ align={"start"}
789
+ alignOffset={-117}
789
790
  trigger={
790
791
  <IconButton size={"small"}>
791
792
  <SearchIcon size={"small"}/>
@@ -815,7 +816,7 @@ function EntityIdHeaderWidget({
815
816
  setSearchString(e.target.value);
816
817
  }}
817
818
  value={searchString}
818
- className={"flex-grow bg-transparent outline-none p-1"}/>
819
+ className={"flex-grow bg-transparent outline-none p-1 " + focusedDisabled}/>
819
820
  <Button variant={"outlined"}
820
821
  disabled={!(searchString.trim())}
821
822
  type={"submit"}
@@ -7,19 +7,31 @@ export function useSelectionController<M extends Record<string, any> = any>(
7
7
 
8
8
  const [selectedEntities, setSelectedEntities] = useState<Entity<M>[]>([]);
9
9
 
10
- const toggleEntitySelection = useCallback((entity: Entity<M>) => {
10
+ const toggleEntitySelection = useCallback((entity: Entity<M>, newSelectedState?: boolean) => {
11
11
  let newValue;
12
- if (selectedEntities.map(e => e.id).includes(entity.id)) {
13
- onSelectionChange?.(entity, false);
14
- newValue = selectedEntities.filter((item: Entity<M>) => item.id !== entity.id);
12
+ if (newSelectedState === undefined) {
13
+ if (selectedEntities.map(e => e.id).includes(entity.id)) {
14
+ onSelectionChange?.(entity, false);
15
+ newValue = selectedEntities.filter((item: Entity<M>) => item.id !== entity.id);
16
+ } else {
17
+ onSelectionChange?.(entity, true);
18
+ newValue = [...selectedEntities, entity];
19
+ }
15
20
  } else {
16
- onSelectionChange?.(entity, true);
17
- newValue = [...selectedEntities, entity];
21
+ if (newSelectedState) {
22
+ onSelectionChange?.(entity, true);
23
+ newValue = [...selectedEntities, entity];
24
+ } else {
25
+ onSelectionChange?.(entity, false);
26
+ newValue = selectedEntities.filter((item: Entity<M>) => item.id !== entity.id);
27
+ }
18
28
  }
19
29
  setSelectedEntities(newValue);
20
30
  }, [selectedEntities]);
21
31
 
22
- const isEntitySelected = useCallback((entity: Entity<M>) => selectedEntities.map(e => e.id).includes(entity.id), [selectedEntities]);
32
+ const isEntitySelected = useCallback((entity: Entity<M>) => {
33
+ return selectedEntities.map(e => e.id).includes(entity.id);
34
+ }, [selectedEntities]);
23
35
 
24
36
  return {
25
37
  selectedEntities,
@@ -8,6 +8,7 @@ import {
8
8
  getEntityPreviewKeys,
9
9
  getEntityTitlePropertyKey,
10
10
  getValueInPath,
11
+ IconForView,
11
12
  resolveCollection
12
13
  } from "../util";
13
14
  import { cls, defaultBorderMixin, IconButton, KeyboardTabIcon, Skeleton, Tooltip, Typography } from "@firecms/ui";
@@ -76,18 +77,18 @@ export function EntityPreview({
76
77
  return <EntityPreviewContainer onClick={disabled ? undefined : onClick}
77
78
  hover={disabled ? undefined : hover}
78
79
  size={size}>
79
- {imageProperty && (
80
- <div className={cls("w-10 h-10 mr-2 shrink-0 grow-0", size === "tiny" ? "my-0.5" : "m-2 self-start")}>
81
- <PropertyPreview property={imageProperty}
82
- propertyKey={imagePropertyKey as string}
83
- size={"tiny"}
84
- value={getValueInPath(entity.values, imagePropertyKey as string)}/>
85
- </div>
86
- )}
87
-
88
- <div className={"flex flex-col flex-grow w-full m-1"}>
89
-
90
- {size !== "tiny" && includeId && (
80
+ <div className={cls("flex w-10 h-10 mr-2 shrink-0", size === "smallest" ? "my-0.5" : "m-2 self-start")}>
81
+ {imageProperty && <PropertyPreview property={imageProperty}
82
+ propertyKey={imagePropertyKey as string}
83
+ size={"smallest"}
84
+ value={getValueInPath(entity.values, imagePropertyKey as string)}/>}
85
+ {!imageProperty && <IconForView collectionOrView={collection} size={size} className={"m-auto"}/>}
86
+ </div>
87
+
88
+
89
+ <div className={"flex flex-col grow-1 w-full max-w-full m-1"}>
90
+
91
+ {size !== "smallest" && includeId && (
91
92
  entity
92
93
  ? <div className={`${
93
94
  size !== "medium"
@@ -131,10 +132,10 @@ export function EntityPreview({
131
132
  propertyKey={key as string}
132
133
  value={getValueInPath(entity.values, key)}
133
134
  property={childProperty as ResolvedProperty}
134
- size={"tiny"}/>
135
+ size={"smallest"}/>
135
136
  : <SkeletonPropertyComponent
136
137
  property={childProperty as ResolvedProperty}
137
- size={"tiny"}/>
138
+ size={"smallest"}/>
138
139
  }
139
140
  </div>
140
141
  );
@@ -143,11 +144,11 @@ export function EntityPreview({
143
144
  </div>
144
145
 
145
146
  {entity && includeEntityLink &&
146
- <Tooltip title={`See details for ${entity.id}`}
147
- className={size !== "tiny" ? "self-start" : ""}>
147
+ <Tooltip title={`See details for ${entity.id}`}>
148
148
  <IconButton
149
149
  color={"inherit"}
150
150
  size={"small"}
151
+ className={size !== "smallest" ? "self-start" : ""}
151
152
  onClick={(e) => {
152
153
  e.stopPropagation();
153
154
  analyticsController.onAnalyticsEvent?.("entity_click_from_reference", {
@@ -180,16 +181,17 @@ export type EntityPreviewContainerProps = {
180
181
  onClick?: (e: React.SyntheticEvent) => void;
181
182
  };
182
183
 
183
- const EntityPreviewContainerInner = React.forwardRef<HTMLDivElement, EntityPreviewContainerProps>(({
184
- children,
185
- hover,
186
- onClick,
187
- size,
188
- style,
189
- className,
190
- fullwidth = true,
191
- ...props
192
- }, ref) => {
184
+
185
+ export const EntityPreviewContainer = React.forwardRef<HTMLDivElement, EntityPreviewContainerProps>(({
186
+ children,
187
+ hover,
188
+ onClick,
189
+ size,
190
+ style,
191
+ className,
192
+ fullwidth = true,
193
+ ...props
194
+ }, ref) => {
193
195
  return <div
194
196
  ref={ref}
195
197
  style={{
@@ -199,10 +201,11 @@ const EntityPreviewContainerInner = React.forwardRef<HTMLDivElement, EntityPrevi
199
201
  }}
200
202
  className={cls(
201
203
  "bg-white dark:bg-gray-900",
204
+ "min-h-[42px]",
202
205
  fullwidth ? "w-full" : "",
203
206
  "items-center",
204
207
  hover ? "hover:bg-slate-50 dark:hover:bg-gray-800 group-hover:bg-slate-50 dark:group-hover:bg-gray-800" : "",
205
- size === "tiny" ? "p-1" : "p-2",
208
+ size === "smallest" ? "p-1" : "px-2 py-1",
206
209
  "flex border rounded-lg",
207
210
  onClick ? "cursor-pointer" : "",
208
211
  defaultBorderMixin,
@@ -217,7 +220,3 @@ const EntityPreviewContainerInner = React.forwardRef<HTMLDivElement, EntityPrevi
217
220
  {children}
218
221
  </div>;
219
222
  });
220
-
221
- EntityPreviewContainerInner.displayName = "EntityPreviewContainer";
222
-
223
- export const EntityPreviewContainer = React.memo(EntityPreviewContainerInner) as React.FC<EntityPreviewContainerProps>;
@@ -17,7 +17,7 @@ export interface ErrorViewProps {
17
17
  * @param title
18
18
  * @param error
19
19
  * @param tooltip
20
- * @constructor
20
+
21
21
  * @group Components
22
22
  */
23
23
  export function ErrorView({
@@ -1,29 +1,27 @@
1
- import React, { useCallback, useEffect, useState } from "react";
1
+ import React, { useCallback, useEffect, useRef, useState } from "react";
2
2
 
3
3
  import { useCustomizationController, useFireCMSContext, useNavigationController } from "../../hooks";
4
- import { CMSAnalyticsEvent, PluginGenericProps, PluginHomePageAdditionalCardsProps } from "../../types";
4
+ import {
5
+ CMSAnalyticsEvent,
6
+ PluginGenericProps,
7
+ PluginHomePageAdditionalCardsProps,
8
+ TopNavigationEntry
9
+ } from "../../types";
5
10
 
6
11
  import { toArray } from "../../util/arrays";
7
12
  import { NavigationGroup } from "./NavigationGroup";
8
13
  import { NavigationCardBinding } from "./NavigationCardBinding";
9
14
 
10
- // @ts-ignore
11
- import * as JsSearch from "js-search";
15
+ import Fuse from "fuse.js"
12
16
 
13
17
  import { Container, SearchBar } from "@firecms/ui";
14
18
  import { FavouritesView } from "./FavouritesView";
15
19
  import { useRestoreScroll } from "../../internal/useRestoreScroll";
16
20
 
17
- const search = new JsSearch.Search("url");
18
- search.addIndex("name");
19
- search.addIndex("description");
20
- search.addIndex("group");
21
- search.addIndex("path");
22
-
23
21
  /**
24
22
  * Default entry view for the CMS. This component renders navigation cards
25
23
  * for each collection defined in the navigation.
26
- * @constructor
24
+
27
25
  * @group Components
28
26
  */
29
27
  export function DefaultHomePage({
@@ -49,6 +47,8 @@ export function DefaultHomePage({
49
47
  const customizationController = useCustomizationController();
50
48
  const navigationController = useNavigationController();
51
49
 
50
+ const fuse = useRef<Fuse<TopNavigationEntry> | null>(null);
51
+
52
52
  if (!navigationController.topLevelNavigation)
53
53
  throw Error("Navigation not ready in FireCMSHomePage");
54
54
 
@@ -64,13 +64,16 @@ export function DefaultHomePage({
64
64
  } = navigationController.topLevelNavigation;
65
65
 
66
66
  const [filteredUrls, setFilteredUrls] = useState<string[] | null>(null);
67
+ const performingSearch = Boolean(filteredUrls);
67
68
 
68
69
  const filteredNavigationEntries = filteredUrls
69
- ? navigationEntries.filter((entry) => filteredUrls.includes(entry.url))
70
+ ? filteredUrls.map(url => navigationEntries.find(e => e.url === url)).filter(Boolean) as TopNavigationEntry[]
70
71
  : navigationEntries;
71
72
 
72
73
  useEffect(() => {
73
- search.addDocuments(navigationEntries);
74
+ fuse.current = new Fuse(navigationEntries, {
75
+ keys: ["name", "description", "group", "path"]
76
+ });
74
77
  }, [navigationEntries]);
75
78
 
76
79
  const updateSearchResults = useCallback(
@@ -78,12 +81,15 @@ export function DefaultHomePage({
78
81
  if (!value || value === "") {
79
82
  setFilteredUrls(null);
80
83
  } else {
81
- const searchResult = search.search(value);
82
- setFilteredUrls(searchResult.map((e: any) => e.url));
84
+ const searchResult = fuse.current?.search(value);
85
+ if (searchResult) {
86
+ setFilteredUrls(searchResult.map((e) => e.item.url));
87
+ }
83
88
  }
84
89
  }, []);
85
90
 
86
- const allGroups: Array<string | undefined> = [...groups];
91
+ const filteredGroups = filteredUrls ? filteredNavigationEntries.map(entry => entry.group) : [];
92
+ const allGroups: Array<string | undefined> = filteredUrls ? filteredGroups.filter((group, index) => filteredGroups.indexOf(group) === index) : [...groups];
87
93
  if (filteredNavigationEntries.filter(e => !e.group).length > 0 || filteredNavigationEntries.length === 0) {
88
94
  allGroups.push(undefined);
89
95
  }
@@ -139,7 +145,7 @@ export function DefaultHomePage({
139
145
  {additionalActions}
140
146
  </div>
141
147
 
142
- <FavouritesView hidden={Boolean(filteredUrls)}/>
148
+ <FavouritesView hidden={performingSearch}/>
143
149
 
144
150
  {additionalChildrenStart}
145
151
 
@@ -163,7 +169,7 @@ export function DefaultHomePage({
163
169
 
164
170
  const thisGroupCollections = filteredNavigationEntries
165
171
  .filter((entry) => entry.group === group || (!entry.group && group === undefined));
166
- if (thisGroupCollections.length === 0 && AdditionalCards.length === 0)
172
+ if (thisGroupCollections.length === 0 && (AdditionalCards.length === 0 || performingSearch))
167
173
  return null;
168
174
  return (
169
175
  <NavigationGroup
@@ -19,7 +19,7 @@ import { SmallNavigationCard } from "./SmallNavigationCard";
19
19
  * @param name
20
20
  * @param description
21
21
  * @param onClick
22
- * @constructor
22
+
23
23
  */
24
24
  export function NavigationCardBinding({
25
25
  path,
@@ -1,4 +1,4 @@
1
- import { ArrowForwardIcon, cardClickableMixin, cardMixin, cls, focusedMixin, Typography, } from "@firecms/ui";
1
+ import { ArrowForwardIcon, cardClickableMixin, cardMixin, cls, Typography, } from "@firecms/ui";
2
2
 
3
3
  import { Link } from "react-router-dom";
4
4
 
@@ -21,7 +21,6 @@ export function SmallNavigationCard({
21
21
  tabIndex={0}
22
22
  className={cls(cardMixin,
23
23
  cardClickableMixin,
24
- focusedMixin,
25
24
  "cursor-pointer flex flex-row items-center px-4 py-2 text-inherit dark:text-inherit visited:text-inherit visited:dark:text-inherit hover:text-inherit hover:dark:text-inherit ")}
26
25
  to={url}
27
26
  >
@@ -0,0 +1,48 @@
1
+ import { ContentCopyIcon, IconButton, Tooltip, Typography } from "@firecms/ui";
2
+ import { useCallback, useState } from "react";
3
+
4
+ export function PropertyIdCopyTooltip({
5
+ propertyKey,
6
+ className,
7
+ children
8
+ }: {
9
+ propertyKey: string,
10
+ className?: string,
11
+ children: React.ReactNode
12
+ }) {
13
+ return <Tooltip title={<PropertyIdCopyTooltipContent propertyKey={propertyKey}/>}
14
+ delayDuration={800}
15
+ side={"top"}
16
+ asChild={false}
17
+ align={"start"}
18
+ sideOffset={8}
19
+ className={className}>
20
+ {children}
21
+ </Tooltip>
22
+
23
+ }
24
+
25
+ export function PropertyIdCopyTooltipContent({ propertyKey }: { propertyKey: string }) {
26
+
27
+ const [copied, setCopied] = useState(false);
28
+
29
+ return (
30
+ <div className={"flex flex-row gap-2 items-center justify-center text-white"}>
31
+ <div>
32
+ <Typography variant={"caption"} className={"min-w-20 text-slate-400"}
33
+ color={"disabled"}>{copied ? "Copied" : "Property ID"}</Typography>
34
+ <Typography variant={"caption"} className={"text-white"}><code>{propertyKey}</code></Typography>
35
+ </div>
36
+ <IconButton size={"small"}>
37
+ <ContentCopyIcon size={"smallest"}
38
+ className={"text-white"}
39
+ onClick={useCallback(() => {
40
+ navigator.clipboard.writeText(propertyKey);
41
+ setCopied(true);
42
+ setTimeout(() => setCopied(false), 2000);
43
+ }, [propertyKey])}
44
+ />
45
+ </IconButton>
46
+ </div>
47
+ );
48
+ }
@@ -16,7 +16,7 @@ import {
16
16
  } from "../../hooks";
17
17
  import { ErrorView } from "../ErrorView";
18
18
  import { AddIcon, Button, DialogActions, Typography } from "@firecms/ui";
19
- import { canCreateEntity, fullPathToCollectionSegments, resolveCollection } from "../../util";
19
+ import { canCreateEntity, IconForView, resolveCollection } from "../../util";
20
20
  import { useSelectionController } from "../EntityCollectionView/useSelectionController";
21
21
  import { useColumnIds, useTableSearchHelper } from "../common";
22
22
  import { useSideDialogContext } from "../../core";
@@ -109,6 +109,7 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
109
109
 
110
110
  const fullPath = navigation.resolveAliasesFrom(pathInput);
111
111
 
112
+ console.trace("Opening reference dialog for path " + fullPath);
112
113
  const dataSource = useDataSource(collection);
113
114
 
114
115
  const [entitiesDisplayedFirst, setEntitiesDisplayedFirst] = useState<Entity<any>[]>([]);
@@ -247,7 +248,6 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
247
248
  error={"Could not find collection with id " + collection}/>
248
249
  }
249
250
 
250
- // eslint-disable-next-line react-hooks/rules-of-hooks
251
251
  const resolvedCollection = useMemo(() => resolveCollection({
252
252
  collection: collection,
253
253
  path: fullPath,
@@ -255,10 +255,8 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
255
255
  fields: customizationController.propertyConfigs
256
256
  }), [collection, customizationController.propertyConfigs, fullPath]);
257
257
 
258
- // eslint-disable-next-line react-hooks/rules-of-hooks
259
258
  const displayedColumnIds = useColumnIds(resolvedCollection, false);
260
259
 
261
- // eslint-disable-next-line react-hooks/rules-of-hooks
262
260
  const tableController = useDataSourceEntityCollectionTableController<M>({
263
261
  fullPath,
264
262
  collection,
@@ -272,7 +270,6 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
272
270
  onTextSearchClick,
273
271
  textSearchEnabled
274
272
  } =
275
- // eslint-disable-next-line react-hooks/rules-of-hooks
276
273
  useTableSearchHelper({
277
274
  collection,
278
275
  fullPath,
@@ -293,7 +290,11 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
293
290
  tableController={tableController}
294
291
  enablePopupIcon={false}
295
292
  tableRowActionsBuilder={tableRowActionsBuilder}
296
- title={<Typography variant={"subtitle2"}>
293
+ title={<Typography variant={"subtitle2"} className={"flex flex-row gap-2"}>
294
+ <IconForView
295
+ size={"small"}
296
+ collectionOrView={collection}
297
+ className={"text-gray-300 dark:text-gray-600"}/>
297
298
  {collection.singularName ? `Select ${collection.singularName}` : `Select from ${collection.name}`}
298
299
  </Typography>}
299
300
  defaultSize={collection.defaultSize}
@@ -31,7 +31,9 @@ export function SearchIconsView({
31
31
  setKeys(null);
32
32
  } else {
33
33
  const searchResult = iconsSearch.search(value);
34
- setKeys(searchResult.map((e: any) => e.key));
34
+ const limit = 50;
35
+ const limited = searchResult.slice(0, limit);
36
+ setKeys(limited.map((e) => e.item.key));
35
37
  }
36
38
  }, UPDATE_SEARCH_INDEX_WAIT_MS), []
37
39
  );
@@ -57,7 +59,8 @@ export function SearchIconsView({
57
59
  <div className={"flex max-w-full flex-wrap mt-4"}>
58
60
  {icons.map((icon: string) => {
59
61
  return (
60
- <Tooltip title={icon} key={icon}>
62
+ <Tooltip title={icon} key={icon}
63
+ asChild={true}>
61
64
  <IconButton
62
65
  shape={"square"}
63
66
  toggled={selectedIcon === icon}
@@ -128,8 +128,6 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
128
128
  setFilterValues,
129
129
  sortBy,
130
130
  setSortBy,
131
- setSearchString,
132
- clearFilter,
133
131
  itemCount,
134
132
  setItemCount,
135
133
  pageSize = 50,
@@ -223,7 +221,6 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
223
221
  onEndReached={loadNextPage}
224
222
  onResetPagination={resetPagination}
225
223
  error={dataLoadingError}
226
- paginationEnabled={paginationEnabled}
227
224
  onColumnResize={onColumnResize}
228
225
  rowHeight={getRowHeight(size)}
229
226
  loading={dataLoading}
@@ -248,7 +245,8 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
248
245
  );
249
246
 
250
247
  },
251
- equal
248
+ () => false,
249
+ // equal
252
250
  );
253
251
 
254
252
  function createFilterField({
@@ -95,7 +95,7 @@ export function DateTimeFilterField({
95
95
  size={"medium"}
96
96
  locale={locale}
97
97
  value={internalValue ?? undefined}
98
- onChange={(dateValue: Date | undefined) => {
98
+ onChange={(dateValue: Date | null) => {
99
99
  updateFilter(operation, dateValue === null ? undefined : dateValue);
100
100
  }}
101
101
  clearable={true}