@firecms/core 3.0.0-canary.5 → 3.0.0-canary.51

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 (218) hide show
  1. package/README.md +2 -2
  2. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  3. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
  4. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  5. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
  6. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +3 -2
  7. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  8. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  9. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  10. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -3
  11. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  12. package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
  13. package/dist/components/EntityPreview.d.ts +25 -7
  14. package/dist/components/EntityView.d.ts +11 -0
  15. package/dist/components/FieldCaption.d.ts +5 -0
  16. package/dist/components/FireCMSAppBar.d.ts +3 -2
  17. package/dist/components/HomePage/NavigationCard.d.ts +8 -0
  18. package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
  19. package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
  20. package/dist/components/HomePage/index.d.ts +3 -1
  21. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  22. package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
  23. package/dist/components/common/types.d.ts +4 -6
  24. package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
  25. package/dist/components/index.d.ts +4 -2
  26. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  27. package/dist/core/Drawer.d.ts +5 -12
  28. package/dist/core/DrawerNavigationItem.d.ts +9 -0
  29. package/dist/core/{EntityView.d.ts → EntityEditView.d.ts} +2 -2
  30. package/dist/core/NavigationRoutes.d.ts +1 -1
  31. package/dist/core/Scaffold.d.ts +7 -10
  32. package/dist/core/index.d.ts +3 -4
  33. package/dist/form/EntityForm.d.ts +1 -1
  34. package/dist/form/components/ErrorFocus.d.ts +1 -1
  35. package/dist/form/components/StorageItemPreview.d.ts +3 -2
  36. package/dist/form/components/StorageUploadProgress.d.ts +1 -1
  37. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  38. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  39. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -3
  40. package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
  41. package/dist/form/validation.d.ts +1 -1
  42. package/dist/hooks/data/delete.d.ts +2 -2
  43. package/dist/hooks/data/save.d.ts +2 -3
  44. package/dist/hooks/data/useDataSource.d.ts +2 -2
  45. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  46. package/dist/hooks/index.d.ts +2 -0
  47. package/dist/hooks/useBuildNavigationController.d.ts +6 -4
  48. package/dist/hooks/useProjectLog.d.ts +6 -2
  49. package/dist/hooks/useStorageSource.d.ts +2 -2
  50. package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
  51. package/dist/index.es.js +9644 -9122
  52. package/dist/index.es.js.map +1 -1
  53. package/dist/index.umd.js +5 -5
  54. package/dist/index.umd.js.map +1 -1
  55. package/dist/internal/useBuildDataSource.d.ts +1 -12
  56. package/dist/preview/PropertyPreview.d.ts +1 -1
  57. package/dist/preview/PropertyPreviewProps.d.ts +1 -4
  58. package/dist/preview/components/BooleanPreview.d.ts +5 -1
  59. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  60. package/dist/preview/components/ReferencePreview.d.ts +1 -7
  61. package/dist/types/analytics.d.ts +1 -1
  62. package/dist/types/auth.d.ts +37 -1
  63. package/dist/types/collections.d.ts +29 -5
  64. package/dist/types/datasource.d.ts +3 -6
  65. package/dist/types/entities.d.ts +5 -1
  66. package/dist/types/entity_actions.d.ts +14 -0
  67. package/dist/types/entity_callbacks.d.ts +2 -2
  68. package/dist/types/entity_overrides.d.ts +6 -0
  69. package/dist/types/index.d.ts +2 -1
  70. package/dist/types/navigation.d.ts +14 -13
  71. package/dist/types/permissions.d.ts +5 -1
  72. package/dist/types/plugins.d.ts +20 -20
  73. package/dist/types/properties.d.ts +4 -4
  74. package/dist/types/property_config.d.ts +2 -2
  75. package/dist/types/roles.d.ts +31 -0
  76. package/dist/types/storage.d.ts +11 -3
  77. package/dist/types/user.d.ts +5 -0
  78. package/dist/util/collections.d.ts +9 -1
  79. package/dist/util/entities.d.ts +1 -1
  80. package/dist/util/icon_synonyms.d.ts +2 -4
  81. package/dist/util/icons.d.ts +8 -2
  82. package/dist/util/navigation_utils.d.ts +2 -2
  83. package/dist/util/permissions.d.ts +4 -4
  84. package/dist/util/references.d.ts +4 -2
  85. package/dist/util/resolutions.d.ts +9 -13
  86. package/dist/util/useTraceUpdate.d.ts +1 -0
  87. package/package.json +139 -119
  88. package/src/components/ClearFilterSortButton.tsx +41 -0
  89. package/src/components/DeleteEntityDialog.tsx +4 -4
  90. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
  91. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +275 -278
  92. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +9 -5
  93. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +44 -44
  94. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  95. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -16
  96. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +3 -3
  97. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
  98. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +11 -6
  99. package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +9 -5
  100. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +2 -4
  101. package/src/components/EntityCollectionView/EntityCollectionView.tsx +560 -554
  102. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
  103. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  104. package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
  105. package/src/components/EntityPreview.tsx +207 -70
  106. package/src/components/EntityView.tsx +84 -0
  107. package/src/components/FieldCaption.tsx +14 -0
  108. package/src/components/FireCMSAppBar.tsx +33 -11
  109. package/src/components/HomePage/DefaultHomePage.tsx +15 -11
  110. package/src/components/HomePage/NavigationCard.tsx +69 -0
  111. package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
  112. package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
  113. package/src/components/HomePage/index.tsx +3 -1
  114. package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
  115. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +4 -4
  116. package/src/components/ReferenceWidget.tsx +5 -5
  117. package/src/components/SearchIconsView.tsx +4 -4
  118. package/src/components/SelectableTable/SelectableTable.tsx +1 -1
  119. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  120. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
  121. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +35 -24
  122. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  123. package/src/components/VirtualTable/VirtualTable.tsx +28 -20
  124. package/src/components/VirtualTable/VirtualTableProps.tsx +1 -1
  125. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  126. package/src/components/common/types.tsx +4 -6
  127. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +12 -1
  128. package/src/components/index.tsx +4 -2
  129. package/src/contexts/AuthControllerContext.tsx +1 -1
  130. package/src/core/Drawer.tsx +78 -103
  131. package/src/core/DrawerNavigationItem.tsx +62 -0
  132. package/src/core/{EntityView.tsx → EntityEditView.tsx} +21 -40
  133. package/src/core/EntitySidePanel.tsx +2 -2
  134. package/src/core/FireCMS.tsx +22 -7
  135. package/src/core/NavigationRoutes.tsx +11 -4
  136. package/src/core/Scaffold.tsx +76 -61
  137. package/src/core/field_configs.tsx +1 -2
  138. package/src/core/index.tsx +3 -4
  139. package/src/form/EntityForm.tsx +42 -22
  140. package/src/form/PropertyFieldBinding.tsx +0 -2
  141. package/src/form/components/StorageItemPreview.tsx +5 -3
  142. package/src/form/components/StorageUploadProgress.tsx +7 -6
  143. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +8 -12
  144. package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
  145. package/src/form/field_bindings/KeyValueFieldBinding.tsx +15 -15
  146. package/src/form/field_bindings/MapFieldBinding.tsx +15 -15
  147. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +1 -1
  148. package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -0
  149. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +14 -5
  150. package/src/form/field_bindings/TextFieldBinding.tsx +7 -5
  151. package/src/form/validation.ts +3 -4
  152. package/src/hooks/data/delete.ts +3 -3
  153. package/src/hooks/data/save.ts +2 -2
  154. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  155. package/src/hooks/data/useDataSource.tsx +8 -3
  156. package/src/hooks/data/useEntityFetch.tsx +4 -4
  157. package/src/hooks/index.tsx +3 -0
  158. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +9 -10
  159. package/src/hooks/useBuildModeController.tsx +11 -5
  160. package/src/hooks/useBuildNavigationController.tsx +199 -81
  161. package/src/hooks/useProjectLog.tsx +17 -7
  162. package/src/hooks/useReferenceDialog.tsx +2 -2
  163. package/src/hooks/useStorageSource.tsx +7 -2
  164. package/src/hooks/useValidateAuthenticator.tsx +115 -0
  165. package/src/internal/useBuildDataSource.ts +42 -44
  166. package/src/internal/useBuildSideEntityController.tsx +86 -20
  167. package/src/preview/PropertyPreview.tsx +3 -14
  168. package/src/preview/PropertyPreviewProps.tsx +1 -11
  169. package/src/preview/components/BooleanPreview.tsx +19 -4
  170. package/src/preview/components/EnumValuesChip.tsx +1 -1
  171. package/src/preview/components/ReferencePreview.tsx +55 -147
  172. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
  173. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +0 -1
  174. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
  175. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
  176. package/src/preview/property_previews/ArrayOneOfPreview.tsx +0 -1
  177. package/src/preview/property_previews/ArrayPropertyPreview.tsx +0 -1
  178. package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
  179. package/src/types/analytics.ts +1 -0
  180. package/src/types/auth.tsx +50 -1
  181. package/src/types/collections.ts +33 -5
  182. package/src/types/datasource.ts +8 -5
  183. package/src/types/entities.ts +9 -1
  184. package/src/types/entity_actions.tsx +17 -0
  185. package/src/types/entity_callbacks.ts +2 -2
  186. package/src/types/entity_overrides.tsx +7 -0
  187. package/src/types/firecms.tsx +0 -1
  188. package/src/types/index.ts +2 -1
  189. package/src/types/navigation.ts +17 -16
  190. package/src/types/permissions.ts +6 -1
  191. package/src/types/plugins.tsx +26 -28
  192. package/src/types/properties.ts +8 -6
  193. package/src/types/property_config.tsx +2 -2
  194. package/src/types/roles.ts +41 -0
  195. package/src/types/side_entity_controller.tsx +1 -0
  196. package/src/types/storage.ts +12 -3
  197. package/src/types/user.ts +7 -0
  198. package/src/util/collections.ts +22 -0
  199. package/src/util/entities.ts +1 -1
  200. package/src/util/icon_list.ts +2 -2
  201. package/src/util/icon_synonyms.ts +4 -6
  202. package/src/util/icons.tsx +11 -3
  203. package/src/util/navigation_utils.ts +6 -6
  204. package/src/util/objects.ts +0 -14
  205. package/src/util/permissions.ts +11 -8
  206. package/src/util/references.ts +36 -5
  207. package/src/util/resolutions.ts +6 -24
  208. package/src/util/strings.ts +2 -2
  209. package/src/util/useTraceUpdate.tsx +2 -1
  210. package/dist/core/SideEntityView.d.ts +0 -7
  211. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  212. package/dist/internal/useLocaleConfig.d.ts +0 -1
  213. package/dist/types/appcheck.d.ts +0 -26
  214. package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
  215. package/src/core/SideEntityView.tsx +0 -38
  216. package/src/internal/useBuildCustomizationController.tsx +0 -5
  217. package/src/internal/useLocaleConfig.tsx +0 -18
  218. package/src/types/appcheck.ts +0 -29
@@ -44,7 +44,7 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
44
44
 
45
45
  const selectedEntities = selectionController.selectedEntities;
46
46
 
47
- const addButton = canCreateEntity(collection, authController, fullPathToCollectionSegments(path), null) &&
47
+ const addButton = canCreateEntity(collection, authController, path, null) &&
48
48
  onNewClick && (largeLayout
49
49
  ? <Button
50
50
  id={`add_entity_${path}`}
@@ -57,14 +57,13 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
57
57
  : <Button
58
58
  id={`add_entity_${path}`}
59
59
  onClick={onNewClick}
60
- size="medium"
61
60
  variant="filled"
62
61
  color="primary"
63
62
  >
64
63
  <AddIcon/>
65
64
  </Button>);
66
65
 
67
- const multipleDeleteEnabled = canDeleteEntity(collection, authController, fullPathToCollectionSegments(path), null);
66
+ const multipleDeleteEnabled = canDeleteEntity(collection, authController, path, null);
68
67
 
69
68
  let multipleDeleteButton: React.ReactNode | undefined;
70
69
  if (selectionEnabled) {
@@ -112,11 +111,11 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
112
111
 
113
112
  if (plugins) {
114
113
  plugins.forEach((plugin, i) => {
115
- if (plugin.collections?.CollectionActions) {
116
- actions.push(...toArray(plugin.collections?.CollectionActions)
114
+ if (plugin.collectionView?.CollectionActions) {
115
+ actions.push(...toArray(plugin.collectionView?.CollectionActions)
117
116
  .map((Action, j) => (
118
117
  <ErrorBoundary key={`plugin_actions_${i}_${j}`}>
119
- <Action {...actionProps} {...plugin.collections?.collectionActionsProps}/>
118
+ <Action {...actionProps} {...plugin.collectionView?.collectionActionsProps}/>
120
119
  </ErrorBoundary>
121
120
  )));
122
121
  }
@@ -0,0 +1,68 @@
1
+ import React from "react";
2
+ import { useCustomizationController, useFireCMSContext } from "../../hooks";
3
+ import { CollectionActionsProps, EntityCollection, EntityTableController, SelectionController } from "../../types";
4
+ import { toArray } from "../../util/arrays";
5
+ import { ErrorBoundary } from "../ErrorBoundary";
6
+ import { ClearFilterSortButton } from "../ClearFilterSortButton";
7
+
8
+ export type EntityCollectionViewStartActionsProps<M extends Record<string, any>> = {
9
+ collection: EntityCollection<M>;
10
+ path: string;
11
+ relativePath: string;
12
+ parentCollectionIds: string[];
13
+ selectionController: SelectionController<M>;
14
+ tableController: EntityTableController<M>;
15
+ collectionEntitiesCount: number;
16
+ }
17
+
18
+ export function EntityCollectionViewStartActions<M extends Record<string, any>>({
19
+ collection,
20
+ relativePath,
21
+ parentCollectionIds,
22
+ path,
23
+ selectionController,
24
+ tableController,
25
+ collectionEntitiesCount
26
+ }: EntityCollectionViewStartActionsProps<M>) {
27
+
28
+ const context = useFireCMSContext();
29
+
30
+ const customizationController = useCustomizationController();
31
+ const plugins = customizationController.plugins ?? [];
32
+
33
+ const actionProps: CollectionActionsProps = {
34
+ path,
35
+ relativePath,
36
+ parentCollectionIds,
37
+ collection,
38
+ selectionController,
39
+ context,
40
+ tableController,
41
+ collectionEntitiesCount
42
+ };
43
+ const actions: React.ReactNode[] = [
44
+ <ClearFilterSortButton
45
+ key={"clear_filter"}
46
+ tableController={tableController}
47
+ enabled={!collection.forceFilter}/>
48
+ ];
49
+
50
+ if (plugins) {
51
+ plugins.forEach((plugin, i) => {
52
+ if (plugin.collectionView?.CollectionActionsStart) {
53
+ actions.push(...toArray(plugin.collectionView?.CollectionActionsStart)
54
+ .map((Action, j) => (
55
+ <ErrorBoundary key={`plugin_actions_${i}_${j}`}>
56
+ <Action {...actionProps} {...plugin.collectionView?.collectionActionsStartProps}/>
57
+ </ErrorBoundary>
58
+ )));
59
+ }
60
+ });
61
+ }
62
+
63
+ return (
64
+ <>
65
+ {actions}
66
+ </>
67
+ );
68
+ }
@@ -0,0 +1,30 @@
1
+ import { useCallback, useState } from "react";
2
+ import { Entity, SelectionController } from "../../types";
3
+
4
+ export function useSelectionController<M extends Record<string, any> = any>(
5
+ onSelectionChange?: (entity: Entity<M>, selected: boolean) => void
6
+ ): SelectionController<M> {
7
+
8
+ const [selectedEntities, setSelectedEntities] = useState<Entity<M>[]>([]);
9
+
10
+ const toggleEntitySelection = useCallback((entity: Entity<M>) => {
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);
15
+ } else {
16
+ onSelectionChange?.(entity, true);
17
+ newValue = [...selectedEntities, entity];
18
+ }
19
+ setSelectedEntities(newValue);
20
+ }, [selectedEntities]);
21
+
22
+ const isEntitySelected = useCallback((entity: Entity<M>) => selectedEntities.map(e => e.id).includes(entity.id), [selectedEntities]);
23
+
24
+ return {
25
+ selectedEntities,
26
+ setSelectedEntities,
27
+ isEntitySelected,
28
+ toggleEntitySelection
29
+ };
30
+ }
@@ -1,84 +1,221 @@
1
- import React, { useMemo } from "react";
2
- import { PropertyPreview } from "../preview";
3
- import { Entity, EntityCollection, ResolvedEntityCollection, ResolvedProperties } from "../types";
4
- import { resolveCollection } from "../util";
5
- import { cn, defaultBorderMixin, IconButton, OpenInNewIcon } from "@firecms/ui";
6
- import { CustomizationController } from "../types/customization_controller";
7
- import { useCustomizationController } from "../hooks/useCustomizationController";
1
+ import * as React from "react";
2
+ import { useMemo } from "react";
3
+
4
+ import { Entity, EntityCollection, ResolvedProperty } from "../types";
5
+
6
+ import {
7
+ getEntityImagePreviewPropertyKey,
8
+ getEntityPreviewKeys,
9
+ getEntityTitlePropertyKey,
10
+ getValueInPath,
11
+ resolveCollection
12
+ } from "../util";
13
+ import { cn, defaultBorderMixin, IconButton, KeyboardTabIcon, Skeleton, Tooltip, Typography } from "@firecms/ui";
14
+ import { PreviewSize, PropertyPreview, SkeletonPropertyComponent } from "../preview";
15
+ import { useCustomizationController, useNavigationController, useSideEntityController } from "../hooks";
16
+ import { useAnalyticsController } from "../hooks/useAnalyticsController";
17
+
18
+ export type EntityPreviewProps = {
19
+ size: PreviewSize,
20
+ actions?: React.ReactNode,
21
+ collection?: EntityCollection,
22
+ hover?: boolean;
23
+ previewProperties?: string[],
24
+ disabled: undefined | boolean,
25
+ entity: Entity<any>,
26
+ includeEntityNavigation?: boolean,
27
+ onClick?: (e: React.SyntheticEvent) => void;
28
+ };
8
29
 
9
30
  /**
10
- * @group Components
31
+ * This view is used to display a preview of an entity.
32
+ * It is used by default in reference fields and whenever a reference is displayed.
11
33
  */
12
- export interface EntityPreviewProps<M extends Record<string, any>> {
13
- entity: Entity<M>;
14
- collection: EntityCollection<M>;
15
- path: string;
16
- className?: string;
17
- }
34
+ export function EntityPreview({
35
+ actions,
36
+ disabled,
37
+ hover,
38
+ collection: collectionProp,
39
+ previewProperties,
40
+ onClick,
41
+ size,
42
+ includeEntityNavigation,
43
+ entity
44
+ }: EntityPreviewProps) {
18
45
 
19
- export function EntityPreview<M extends Record<string, any>>(
20
- {
21
- entity,
22
- collection,
23
- path,
24
- className
25
- }: EntityPreviewProps<M>) {
46
+ const analyticsController = useAnalyticsController();
47
+ const sideEntityController = useSideEntityController();
48
+ const customizationController = useCustomizationController();
49
+
50
+ const navigationController = useNavigationController();
51
+
52
+ const collection = collectionProp ?? navigationController.getCollection(entity.path);
26
53
 
27
- const customizationController: CustomizationController = useCustomizationController();
28
- const resolvedCollection: ResolvedEntityCollection<M> = useMemo(() => resolveCollection<M>({
54
+ if (!collection) {
55
+ throw Error(`Couldn't find the corresponding collection view for the path: ${entity.path}`);
56
+ }
57
+
58
+ const resolvedCollection = React.useMemo(() => resolveCollection({
29
59
  collection,
30
- path,
31
- entityId: entity.id,
60
+ path: entity.path,
32
61
  values: entity.values,
33
62
  fields: customizationController.propertyConfigs
34
- }), [collection, path, entity]);
63
+ }), [collection]);
35
64
 
36
- const properties: ResolvedProperties = resolvedCollection.properties;
65
+ const listProperties = useMemo(() => getEntityPreviewKeys(resolvedCollection, customizationController.propertyConfigs, previewProperties, size === "small" || size === "medium" ? 3 : 1),
66
+ [previewProperties, resolvedCollection, size]);
37
67
 
38
- return (
39
- <div className={"w-full " + className}>
40
- <div className={"w-full mb-4"}>
41
- <div className={cn(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0")}>
42
- <div className="flex items-center w-1/4">
43
- <span className="pl-2 text-sm text-gray-600">Id</span>
44
- </div>
45
- <div
46
- className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
47
- <span className="flex-grow mr-2">{entity.id}</span>
48
- {customizationController?.entityLinkBuilder &&
49
- <a href={customizationController.entityLinkBuilder({ entity })}
50
- rel="noopener noreferrer"
51
- target="_blank">
52
- <IconButton>
53
- <OpenInNewIcon
54
- size={"small"}/>
55
- </IconButton>
56
- </a>}
68
+ const titleProperty = getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs);
69
+ const imagePropertyKey = getEntityImagePreviewPropertyKey(resolvedCollection);
70
+ const imageProperty = imagePropertyKey ? resolvedCollection.properties[imagePropertyKey] : undefined;
71
+
72
+ const restProperties = listProperties.filter(p => p !== titleProperty && p !== imagePropertyKey);
73
+
74
+ return <EntityPreviewContainer onClick={disabled ? undefined : onClick}
75
+ hover={disabled ? undefined : hover}
76
+ size={size}>
77
+ {imageProperty && (
78
+ <div className={cn("w-10 h-10 mr-2 shrink-0 grow-0", size === "tiny" ? "my-0.5" : "m-2 self-start")}>
79
+ <PropertyPreview property={imageProperty}
80
+ propertyKey={imagePropertyKey as string}
81
+ size={"tiny"}
82
+ value={getValueInPath(entity.values, imagePropertyKey as string)}/>
83
+ </div>
84
+ )}
85
+
86
+ <div className={"flex flex-col flex-grow w-full m-1"}>
87
+
88
+ {size !== "tiny" && (
89
+ entity
90
+ ? <div className={`${
91
+ size !== "medium"
92
+ ? "block whitespace-nowrap overflow-hidden truncate"
93
+ : ""
94
+ }`}>
95
+ <Typography variant={"caption"}
96
+ color={"disabled"}
97
+ className={"font-mono"}>
98
+ {entity.id}
99
+ </Typography>
57
100
  </div>
101
+ : <Skeleton/>)}
102
+
103
+ {titleProperty && (
104
+ <div className={"my-0.5 text-sm font-medium"}>
105
+ {
106
+ entity
107
+ ? <PropertyPreview
108
+ propertyKey={titleProperty as string}
109
+ value={getValueInPath(entity.values, titleProperty)}
110
+ property={resolvedCollection.properties[titleProperty as string] as ResolvedProperty}
111
+ size={"medium"}/>
112
+ : <SkeletonPropertyComponent
113
+ property={resolvedCollection.properties[titleProperty as string] as ResolvedProperty}
114
+ size={"medium"}/>
115
+ }
58
116
  </div>
59
- {Object.entries(properties)
60
- .map(([key, property]) => {
61
- const value = (entity.values)[key];
62
- return (
63
- <div
64
- key={`reference_previews_${key}`}
65
- className={cn(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0")}>
66
- <div className="flex items-center w-1/4">
67
- <span className="pl-2 text-sm text-gray-600">{property.name}</span>
68
- </div>
69
- <div
70
- className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
71
- <PropertyPreview
72
- propertyKey={key}
73
- value={value}
74
- // entity={entity}
75
- property={property}
76
- size={"medium"}/>
77
- </div>
78
- </div>
79
- )
80
- })}
81
- </div>
117
+ )}
118
+
119
+ {restProperties && restProperties.map((key) => {
120
+ const childProperty = resolvedCollection.properties[key as string];
121
+ if (!childProperty) return null;
122
+
123
+ return (
124
+ <div key={"ref_prev_" + key}
125
+ className={restProperties.length > 1 ? "my-0.5" : "my-0"}>
126
+ {
127
+ entity
128
+ ? <PropertyPreview
129
+ propertyKey={key as string}
130
+ value={getValueInPath(entity.values, key)}
131
+ property={childProperty as ResolvedProperty}
132
+ size={"tiny"}/>
133
+ : <SkeletonPropertyComponent
134
+ property={childProperty as ResolvedProperty}
135
+ size={"tiny"}/>
136
+ }
137
+ </div>
138
+ );
139
+ })}
140
+
82
141
  </div>
83
- );
142
+
143
+ {entity && includeEntityNavigation &&
144
+ <Tooltip title={`See details for ${entity.id}`}
145
+ className={size !== "tiny" ? "self-start" : ""}>
146
+ <IconButton
147
+ color={"inherit"}
148
+ size={"small"}
149
+ onClick={(e) => {
150
+ e.stopPropagation();
151
+ analyticsController.onAnalyticsEvent?.("entity_click_from_reference", {
152
+ path: entity.path,
153
+ entityId: entity.id
154
+ });
155
+ sideEntityController.open({
156
+ entityId: entity.id,
157
+ path: entity.path,
158
+ collection,
159
+ updateUrl: true
160
+ });
161
+ }}>
162
+ <KeyboardTabIcon size={"small"}/>
163
+ </IconButton>
164
+ </Tooltip>}
165
+
166
+ {actions}
167
+
168
+ </EntityPreviewContainer>;
84
169
  }
170
+
171
+ export type EntityPreviewContainerProps = {
172
+ children: React.ReactNode;
173
+ hover?: boolean;
174
+ fullwidth?: boolean;
175
+ size: PreviewSize;
176
+ className?: string;
177
+ style?: React.CSSProperties;
178
+ onClick?: (e: React.SyntheticEvent) => void;
179
+ };
180
+
181
+ const EntityPreviewContainerInner = React.forwardRef<HTMLDivElement, EntityPreviewContainerProps>(({
182
+ children,
183
+ hover,
184
+ onClick,
185
+ size,
186
+ style,
187
+ className,
188
+ fullwidth = true,
189
+ ...props
190
+ }, ref) => {
191
+ return <div
192
+ ref={ref}
193
+ style={{
194
+ ...style,
195
+ // @ts-ignore
196
+ tabindex: 0
197
+ }}
198
+ className={cn(
199
+ "bg-white dark:bg-gray-900",
200
+ fullwidth ? "w-full" : "",
201
+ "items-center",
202
+ hover ? "hover:bg-slate-50 dark:hover:bg-gray-800 group-hover:bg-slate-50 dark:group-hover:bg-gray-800" : "",
203
+ size === "tiny" ? "p-1" : "p-2",
204
+ "flex border rounded-lg",
205
+ onClick ? "cursor-pointer" : "",
206
+ defaultBorderMixin,
207
+ className)}
208
+ onClick={(event) => {
209
+ if (onClick) {
210
+ event.preventDefault();
211
+ onClick(event);
212
+ }
213
+ }}
214
+ {...props}>
215
+ {children}
216
+ </div>;
217
+ });
218
+
219
+ EntityPreviewContainerInner.displayName = "EntityPreviewContainer";
220
+
221
+ export const EntityPreviewContainer = React.memo(EntityPreviewContainerInner) as React.FC<EntityPreviewContainerProps>;
@@ -0,0 +1,84 @@
1
+ import React, { useMemo } from "react";
2
+ import { PropertyPreview } from "../preview";
3
+ import { Entity, EntityCollection, ResolvedEntityCollection, ResolvedProperties } from "../types";
4
+ import { resolveCollection } from "../util";
5
+ import { cn, defaultBorderMixin, IconButton, OpenInNewIcon } from "@firecms/ui";
6
+ import { CustomizationController } from "../types/customization_controller";
7
+ import { useCustomizationController } from "../hooks/useCustomizationController";
8
+
9
+ /**
10
+ * @group Components
11
+ */
12
+ export interface EntityViewProps<M extends Record<string, any>> {
13
+ entity: Entity<M>;
14
+ collection: EntityCollection<M>;
15
+ path: string;
16
+ className?: string;
17
+ }
18
+
19
+ export function EntityView<M extends Record<string, any>>(
20
+ {
21
+ entity,
22
+ collection,
23
+ path,
24
+ className
25
+ }: EntityViewProps<M>) {
26
+
27
+ const customizationController: CustomizationController = useCustomizationController();
28
+ const resolvedCollection: ResolvedEntityCollection<M> = useMemo(() => resolveCollection<M>({
29
+ collection,
30
+ path,
31
+ entityId: entity.id,
32
+ values: entity.values,
33
+ fields: customizationController.propertyConfigs
34
+ }), [collection, path, entity, customizationController.propertyConfigs]);
35
+
36
+ const properties: ResolvedProperties = resolvedCollection.properties;
37
+
38
+ return (
39
+ <div className={"w-full " + className}>
40
+ <div className={"w-full mb-4"}>
41
+ <div className={cn(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0")}>
42
+ <div className="flex items-center w-1/4">
43
+ <span className="pl-2 text-sm text-gray-600">Id</span>
44
+ </div>
45
+ <div
46
+ className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
47
+ <span className="flex-grow mr-2">{entity.id}</span>
48
+ {customizationController?.entityLinkBuilder &&
49
+ <a href={customizationController.entityLinkBuilder({ entity })}
50
+ rel="noopener noreferrer"
51
+ target="_blank">
52
+ <IconButton>
53
+ <OpenInNewIcon
54
+ size={"small"}/>
55
+ </IconButton>
56
+ </a>}
57
+ </div>
58
+ </div>
59
+ {Object.entries(properties)
60
+ .map(([key, property]) => {
61
+ const value = entity.values?.[key];
62
+ return (
63
+ <div
64
+ key={`reference_previews_${key}`}
65
+ className={cn(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0")}>
66
+ <div className="flex items-center w-1/4">
67
+ <span className="pl-2 text-sm text-gray-600">{property.name}</span>
68
+ </div>
69
+ <div
70
+ className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
71
+ <PropertyPreview
72
+ propertyKey={key}
73
+ value={value}
74
+ // entity={entity}
75
+ property={property}
76
+ size={"medium"}/>
77
+ </div>
78
+ </div>
79
+ )
80
+ })}
81
+ </div>
82
+ </div>
83
+ );
84
+ }
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ import { Typography } from "@firecms/ui";
3
+
4
+ export function FieldCaption({
5
+ error,
6
+ children
7
+ }: { error?: boolean, children?: React.ReactNode }) {
8
+ if (!children) return null;
9
+ return (
10
+ <Typography variant={"caption"} color={error ? "error" : "secondary"} className={"ml-3.5 mt-0.5"}>
11
+ {children}
12
+ </Typography>
13
+ );
14
+ }
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
 
3
3
  import { Link as ReactLink } from "react-router-dom";
4
- import { ErrorBoundary } from "../components";
4
+ import { ErrorBoundary, FireCMSLogo } from "../components";
5
5
  import {
6
6
  Avatar,
7
7
  cn,
@@ -18,7 +18,8 @@ import { useAuthController, useLargeLayout, useModeController, useNavigationCont
18
18
  import { User } from "../types";
19
19
 
20
20
  export type FireCMSAppBarProps<ADDITIONAL_PROPS = object> = {
21
- title: string;
21
+
22
+ title: React.ReactNode;
22
23
  /**
23
24
  * A component that gets rendered on the upper side of the main toolbar
24
25
  */
@@ -36,6 +37,8 @@ export type FireCMSAppBarProps<ADDITIONAL_PROPS = object> = {
36
37
 
37
38
  style?: React.CSSProperties;
38
39
 
40
+ logo?: string;
41
+
39
42
  user?: User;
40
43
  } & ADDITIONAL_PROPS;
41
44
 
@@ -57,6 +60,7 @@ export const FireCMSAppBar = function FireCMSAppBar({
57
60
  includeDrawer,
58
61
  className,
59
62
  style,
63
+ logo,
60
64
  user: userProp
61
65
  }: FireCMSAppBarProps) {
62
66
  const navigation = useNavigationController();
@@ -92,7 +96,7 @@ export const FireCMSAppBar = function FireCMSAppBar({
92
96
  className={cn("pr-2",
93
97
  {
94
98
  "ml-[17rem]": drawerOpen && largeLayout,
95
- "ml-16": includeDrawer && !(drawerOpen && largeLayout) && !startAdornment,
99
+ "ml-16": includeDrawer && !(drawerOpen && largeLayout),
96
100
  "h-16": true,
97
101
  "z-10": largeLayout,
98
102
  "transition-all": true,
@@ -102,27 +106,37 @@ export const FireCMSAppBar = function FireCMSAppBar({
102
106
  "w-[calc(100%-64px)]": includeDrawer && !(drawerOpen && largeLayout),
103
107
  "w-[calc(100%-17rem)]": includeDrawer && (drawerOpen && largeLayout),
104
108
  "duration-150": drawerOpen && largeLayout,
105
- fixed: true,
109
+ fixed: true
106
110
  },
107
111
  className)}>
108
112
 
109
113
  <div className="flex flex-row gap-2 px-4 h-full items-center">
110
114
 
111
- {startAdornment}
112
-
113
115
  {navigation && <div className="mr-8 hidden lg:block">
114
116
  <ReactLink
115
117
  className="visited:text-inherit visited:dark:text-inherit"
116
118
  to={navigation?.basePath ?? "/"}
117
119
  >
118
- <Typography variant="subtitle1"
119
- noWrap
120
- className={"ml-2 !font-medium"}>
121
- {title}
122
- </Typography>
120
+ <div className={"flex flex-row gap-4"}>
121
+ {!includeDrawer && (logo
122
+ ? <img src={logo}
123
+ alt="Logo"
124
+ className={cn("w-[32px] h-[32px]")}/>
125
+ : <FireCMSLogo width={"32px"} height={"32px"}/>)}
126
+
127
+ {typeof title === "string"
128
+ ? <Typography variant="subtitle1"
129
+ noWrap
130
+ className={"ml-2 !font-medium"}>
131
+ {title}
132
+ </Typography>
133
+ : title}
134
+ </div>
123
135
  </ReactLink>
124
136
  </div>}
125
137
 
138
+ {startAdornment}
139
+
126
140
  <div className={"flex-grow"}/>
127
141
 
128
142
  {endAdornment &&
@@ -141,6 +155,14 @@ export const FireCMSAppBar = function FireCMSAppBar({
141
155
  </IconButton>
142
156
 
143
157
  <Menu trigger={avatarComponent}>
158
+ {user && <div className={"px-4 py-2 mb-2"}>
159
+ {user.displayName && <Typography variant={"body1"} color={"secondary"}>
160
+ {user.displayName}
161
+ </Typography>}
162
+ {user.email && <Typography variant={"body2"} color={"secondary"}>
163
+ {user.email}
164
+ </Typography>}
165
+ </div>}
144
166
 
145
167
  {dropDownActions}
146
168