@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.
- package/README.md +2 -2
- package/dist/components/ClearFilterSortButton.d.ts +5 -0
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
- package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +3 -2
- package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
- package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -3
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
- package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
- package/dist/components/EntityPreview.d.ts +25 -7
- package/dist/components/EntityView.d.ts +11 -0
- package/dist/components/FieldCaption.d.ts +5 -0
- package/dist/components/FireCMSAppBar.d.ts +3 -2
- package/dist/components/HomePage/NavigationCard.d.ts +8 -0
- package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
- package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
- package/dist/components/HomePage/index.d.ts +3 -1
- package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/components/common/types.d.ts +4 -6
- package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
- package/dist/components/index.d.ts +4 -2
- package/dist/contexts/AuthControllerContext.d.ts +1 -1
- package/dist/core/Drawer.d.ts +5 -12
- package/dist/core/DrawerNavigationItem.d.ts +9 -0
- package/dist/core/{EntityView.d.ts → EntityEditView.d.ts} +2 -2
- package/dist/core/NavigationRoutes.d.ts +1 -1
- package/dist/core/Scaffold.d.ts +7 -10
- package/dist/core/index.d.ts +3 -4
- package/dist/form/EntityForm.d.ts +1 -1
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/form/components/StorageItemPreview.d.ts +3 -2
- package/dist/form/components/StorageUploadProgress.d.ts +1 -1
- package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -3
- package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
- package/dist/form/validation.d.ts +1 -1
- package/dist/hooks/data/delete.d.ts +2 -2
- package/dist/hooks/data/save.d.ts +2 -3
- package/dist/hooks/data/useDataSource.d.ts +2 -2
- package/dist/hooks/data/useEntityFetch.d.ts +3 -3
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useBuildNavigationController.d.ts +6 -4
- package/dist/hooks/useProjectLog.d.ts +6 -2
- package/dist/hooks/useStorageSource.d.ts +2 -2
- package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
- package/dist/index.es.js +9644 -9122
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +1 -12
- package/dist/preview/PropertyPreview.d.ts +1 -1
- package/dist/preview/PropertyPreviewProps.d.ts +1 -4
- package/dist/preview/components/BooleanPreview.d.ts +5 -1
- package/dist/preview/components/EnumValuesChip.d.ts +1 -1
- package/dist/preview/components/ReferencePreview.d.ts +1 -7
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/auth.d.ts +37 -1
- package/dist/types/collections.d.ts +29 -5
- package/dist/types/datasource.d.ts +3 -6
- package/dist/types/entities.d.ts +5 -1
- package/dist/types/entity_actions.d.ts +14 -0
- package/dist/types/entity_callbacks.d.ts +2 -2
- package/dist/types/entity_overrides.d.ts +6 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/navigation.d.ts +14 -13
- package/dist/types/permissions.d.ts +5 -1
- package/dist/types/plugins.d.ts +20 -20
- package/dist/types/properties.d.ts +4 -4
- package/dist/types/property_config.d.ts +2 -2
- package/dist/types/roles.d.ts +31 -0
- package/dist/types/storage.d.ts +11 -3
- package/dist/types/user.d.ts +5 -0
- package/dist/util/collections.d.ts +9 -1
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/icon_synonyms.d.ts +2 -4
- package/dist/util/icons.d.ts +8 -2
- package/dist/util/navigation_utils.d.ts +2 -2
- package/dist/util/permissions.d.ts +4 -4
- package/dist/util/references.d.ts +4 -2
- package/dist/util/resolutions.d.ts +9 -13
- package/dist/util/useTraceUpdate.d.ts +1 -0
- package/package.json +139 -119
- package/src/components/ClearFilterSortButton.tsx +41 -0
- package/src/components/DeleteEntityDialog.tsx +4 -4
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +275 -278
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +9 -5
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +44 -44
- package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -16
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +11 -6
- package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +9 -5
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +2 -4
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +560 -554
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
- package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
- package/src/components/EntityPreview.tsx +207 -70
- package/src/components/EntityView.tsx +84 -0
- package/src/components/FieldCaption.tsx +14 -0
- package/src/components/FireCMSAppBar.tsx +33 -11
- package/src/components/HomePage/DefaultHomePage.tsx +15 -11
- package/src/components/HomePage/NavigationCard.tsx +69 -0
- package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
- package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
- package/src/components/HomePage/index.tsx +3 -1
- package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +4 -4
- package/src/components/ReferenceWidget.tsx +5 -5
- package/src/components/SearchIconsView.tsx +4 -4
- package/src/components/SelectableTable/SelectableTable.tsx +1 -1
- package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +35 -24
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
- package/src/components/VirtualTable/VirtualTable.tsx +28 -20
- package/src/components/VirtualTable/VirtualTableProps.tsx +1 -1
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
- package/src/components/common/types.tsx +4 -6
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +12 -1
- package/src/components/index.tsx +4 -2
- package/src/contexts/AuthControllerContext.tsx +1 -1
- package/src/core/Drawer.tsx +78 -103
- package/src/core/DrawerNavigationItem.tsx +62 -0
- package/src/core/{EntityView.tsx → EntityEditView.tsx} +21 -40
- package/src/core/EntitySidePanel.tsx +2 -2
- package/src/core/FireCMS.tsx +22 -7
- package/src/core/NavigationRoutes.tsx +11 -4
- package/src/core/Scaffold.tsx +76 -61
- package/src/core/field_configs.tsx +1 -2
- package/src/core/index.tsx +3 -4
- package/src/form/EntityForm.tsx +42 -22
- package/src/form/PropertyFieldBinding.tsx +0 -2
- package/src/form/components/StorageItemPreview.tsx +5 -3
- package/src/form/components/StorageUploadProgress.tsx +7 -6
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +8 -12
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +15 -15
- package/src/form/field_bindings/MapFieldBinding.tsx +15 -15
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +1 -1
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -0
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +14 -5
- package/src/form/field_bindings/TextFieldBinding.tsx +7 -5
- package/src/form/validation.ts +3 -4
- package/src/hooks/data/delete.ts +3 -3
- package/src/hooks/data/save.ts +2 -2
- package/src/hooks/data/useCollectionFetch.tsx +1 -1
- package/src/hooks/data/useDataSource.tsx +8 -3
- package/src/hooks/data/useEntityFetch.tsx +4 -4
- package/src/hooks/index.tsx +3 -0
- package/src/hooks/useBuildLocalConfigurationPersistence.tsx +9 -10
- package/src/hooks/useBuildModeController.tsx +11 -5
- package/src/hooks/useBuildNavigationController.tsx +199 -81
- package/src/hooks/useProjectLog.tsx +17 -7
- package/src/hooks/useReferenceDialog.tsx +2 -2
- package/src/hooks/useStorageSource.tsx +7 -2
- package/src/hooks/useValidateAuthenticator.tsx +115 -0
- package/src/internal/useBuildDataSource.ts +42 -44
- package/src/internal/useBuildSideEntityController.tsx +86 -20
- package/src/preview/PropertyPreview.tsx +3 -14
- package/src/preview/PropertyPreviewProps.tsx +1 -11
- package/src/preview/components/BooleanPreview.tsx +19 -4
- package/src/preview/components/EnumValuesChip.tsx +1 -1
- package/src/preview/components/ReferencePreview.tsx +55 -147
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +0 -1
- package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
- package/src/types/analytics.ts +1 -0
- package/src/types/auth.tsx +50 -1
- package/src/types/collections.ts +33 -5
- package/src/types/datasource.ts +8 -5
- package/src/types/entities.ts +9 -1
- package/src/types/entity_actions.tsx +17 -0
- package/src/types/entity_callbacks.ts +2 -2
- package/src/types/entity_overrides.tsx +7 -0
- package/src/types/firecms.tsx +0 -1
- package/src/types/index.ts +2 -1
- package/src/types/navigation.ts +17 -16
- package/src/types/permissions.ts +6 -1
- package/src/types/plugins.tsx +26 -28
- package/src/types/properties.ts +8 -6
- package/src/types/property_config.tsx +2 -2
- package/src/types/roles.ts +41 -0
- package/src/types/side_entity_controller.tsx +1 -0
- package/src/types/storage.ts +12 -3
- package/src/types/user.ts +7 -0
- package/src/util/collections.ts +22 -0
- package/src/util/entities.ts +1 -1
- package/src/util/icon_list.ts +2 -2
- package/src/util/icon_synonyms.ts +4 -6
- package/src/util/icons.tsx +11 -3
- package/src/util/navigation_utils.ts +6 -6
- package/src/util/objects.ts +0 -14
- package/src/util/permissions.ts +11 -8
- package/src/util/references.ts +36 -5
- package/src/util/resolutions.ts +6 -24
- package/src/util/strings.ts +2 -2
- package/src/util/useTraceUpdate.tsx +2 -1
- package/dist/core/SideEntityView.d.ts +0 -7
- package/dist/internal/useBuildCustomizationController.d.ts +0 -2
- package/dist/internal/useLocaleConfig.d.ts +0 -1
- package/dist/types/appcheck.d.ts +0 -26
- package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
- package/src/core/SideEntityView.tsx +0 -38
- package/src/internal/useBuildCustomizationController.tsx +0 -5
- package/src/internal/useLocaleConfig.tsx +0 -18
- 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,
|
|
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,
|
|
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.
|
|
116
|
-
actions.push(...toArray(plugin.
|
|
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.
|
|
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
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
|
|
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
|
-
*
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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
|
|
63
|
+
}), [collection]);
|
|
35
64
|
|
|
36
|
-
const
|
|
65
|
+
const listProperties = useMemo(() => getEntityPreviewKeys(resolvedCollection, customizationController.propertyConfigs, previewProperties, size === "small" || size === "medium" ? 3 : 1),
|
|
66
|
+
[previewProperties, resolvedCollection, size]);
|
|
37
67
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
|