@firecms/core 3.0.0-rc.1 → 3.0.0-rc.3
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/dist/components/HomePage/HomePageDnD.d.ts +2 -1
- package/dist/components/PropertyCollectionView.d.ts +23 -0
- package/dist/components/UserDisplay.d.ts +7 -0
- package/dist/components/VirtualTable/fields/VirtualTableUserSelect.d.ts +12 -0
- package/dist/contexts/InternalUserManagementContext.d.ts +3 -0
- package/dist/core/EntityEditView.d.ts +10 -4
- package/dist/core/FireCMS.d.ts +0 -1
- package/dist/core/field_configs.d.ts +1 -1
- package/dist/form/EntityForm.d.ts +5 -2
- package/dist/form/components/LocalChangesMenu.d.ts +11 -0
- package/dist/form/field_bindings/UserSelectFieldBinding.d.ts +12 -0
- package/dist/form/index.d.ts +2 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useCollapsedGroups.d.ts +9 -0
- package/dist/hooks/useInternalUserManagementController.d.ts +12 -0
- package/dist/index.es.js +1983 -650
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1981 -648
- package/dist/index.umd.js.map +1 -1
- package/dist/preview/components/UserPreview.d.ts +8 -0
- package/dist/preview/index.d.ts +1 -0
- package/dist/types/collections.d.ts +13 -0
- package/dist/types/entities.d.ts +5 -1
- package/dist/types/firecms.d.ts +15 -0
- package/dist/types/firecms_context.d.ts +16 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/internal_user_management.d.ts +20 -0
- package/dist/types/plugins.d.ts +2 -0
- package/dist/types/properties.d.ts +41 -6
- package/dist/types/property_config.d.ts +1 -1
- package/dist/types/user.d.ts +1 -1
- package/dist/util/collections.d.ts +1 -0
- package/dist/util/entity_cache.d.ts +6 -1
- package/dist/util/make_properties_editable.d.ts +1 -2
- package/dist/util/objects.d.ts +1 -0
- package/dist/util/useStorageUploadController.d.ts +1 -0
- package/package.json +6 -6
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +47 -47
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +12 -0
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +6 -1
- package/src/components/EntityView.tsx +29 -40
- package/src/components/ErrorView.tsx +1 -1
- package/src/components/HomePage/DefaultHomePage.tsx +21 -34
- package/src/components/HomePage/HomePageDnD.tsx +143 -83
- package/src/components/HomePage/RenameGroupDialog.tsx +9 -3
- package/src/components/PropertyCollectionView.tsx +329 -0
- package/src/components/PropertyConfigBadge.tsx +2 -2
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +2 -1
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +1 -2
- package/src/components/UserDisplay.tsx +55 -0
- package/src/components/VirtualTable/fields/VirtualTableUserSelect.tsx +99 -0
- package/src/components/common/useColumnsIds.tsx +1 -8
- package/src/contexts/InternalUserManagementContext.tsx +4 -0
- package/src/core/EntityEditView.tsx +27 -14
- package/src/core/EntityEditViewFormActions.tsx +33 -18
- package/src/core/EntitySidePanel.tsx +9 -3
- package/src/core/FireCMS.tsx +22 -13
- package/src/core/field_configs.tsx +15 -1
- package/src/form/EntityForm.tsx +173 -42
- package/src/form/EntityFormActions.tsx +30 -15
- package/src/form/PropertyFieldBinding.tsx +4 -0
- package/src/form/components/ErrorFocus.tsx +22 -29
- package/src/form/components/LocalChangesMenu.tsx +144 -0
- package/src/form/field_bindings/UserSelectFieldBinding.tsx +94 -0
- package/src/form/index.tsx +5 -1
- package/src/hooks/index.tsx +3 -0
- package/src/hooks/useBrowserTitleAndIcon.tsx +1 -1
- package/src/hooks/useBuildNavigationController.tsx +104 -31
- package/src/hooks/useCollapsedGroups.ts +64 -0
- package/src/hooks/useFireCMSContext.tsx +6 -2
- package/src/hooks/useInternalUserManagementController.tsx +16 -0
- package/src/preview/PropertyPreview.tsx +8 -0
- package/src/preview/components/ReferencePreview.tsx +4 -2
- package/src/preview/components/UserPreview.tsx +27 -0
- package/src/preview/index.ts +1 -0
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +1 -1
- package/src/preview/property_previews/MapPropertyPreview.tsx +2 -2
- package/src/preview/property_previews/NumberPropertyPreview.tsx +2 -2
- package/src/types/collections.ts +14 -0
- package/src/types/entities.ts +7 -1
- package/src/types/firecms.tsx +16 -0
- package/src/types/firecms_context.tsx +17 -0
- package/src/types/index.ts +1 -0
- package/src/types/internal_user_management.ts +24 -0
- package/src/types/plugins.tsx +3 -0
- package/src/types/properties.ts +45 -6
- package/src/types/property_config.tsx +1 -0
- package/src/types/user.ts +1 -1
- package/src/util/collections.ts +8 -0
- package/src/util/createFormexStub.tsx +4 -0
- package/src/util/entities.ts +1 -1
- package/src/util/entity_cache.ts +72 -53
- package/src/util/join_collections.ts +3 -3
- package/src/util/make_properties_editable.ts +0 -22
- package/src/util/objects.ts +40 -2
- package/src/util/useStorageUploadController.tsx +71 -34
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PropertyPreviewProps } from "../PropertyPreviewProps";
|
|
2
|
+
/**
|
|
3
|
+
* Preview component for displaying user information.
|
|
4
|
+
* This is a simple wrapper around UserDisplay.
|
|
5
|
+
*
|
|
6
|
+
* @group Preview components
|
|
7
|
+
*/
|
|
8
|
+
export declare function UserPreview({ value }: PropertyPreviewProps<string>): import("react/jsx-runtime").JSX.Element;
|
package/dist/preview/index.d.ts
CHANGED
|
@@ -60,6 +60,8 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
60
60
|
* https://fonts.google.com/icons
|
|
61
61
|
* e.g. 'account_tree' or 'person'.
|
|
62
62
|
* Find all the icons in https://firecms.co/docs/icons
|
|
63
|
+
* You can also pass a React node if you want to render a custom icon.
|
|
64
|
+
* If not specified, a default icon will be used.
|
|
63
65
|
*/
|
|
64
66
|
icon?: string | React.ReactNode;
|
|
65
67
|
/**
|
|
@@ -305,6 +307,17 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
305
307
|
* This prop has no effect if the history plugin is not enabled
|
|
306
308
|
*/
|
|
307
309
|
history?: boolean;
|
|
310
|
+
/**
|
|
311
|
+
* Should local changes be backed up in local storage, to prevent data loss on
|
|
312
|
+
* accidental navigations.
|
|
313
|
+
* - `manual_apply`: When the user navigates back to an entity with local changes,
|
|
314
|
+
* they will be prompted to restore the changes.
|
|
315
|
+
* - `auto_apply`: When the user navigates back to an entity with local changes,
|
|
316
|
+
* the changes will be automatically applied.
|
|
317
|
+
* - `false`: Local changes will not be backed up.
|
|
318
|
+
* Defaults to `manual_apply`.
|
|
319
|
+
*/
|
|
320
|
+
localChangesBackup?: "manual_apply" | "auto_apply" | false;
|
|
308
321
|
}
|
|
309
322
|
/**
|
|
310
323
|
* Parameter passed to the `Actions` prop in the collection configuration.
|
package/dist/types/entities.d.ts
CHANGED
|
@@ -42,7 +42,11 @@ export declare class EntityReference {
|
|
|
42
42
|
* to the root of the database).
|
|
43
43
|
*/
|
|
44
44
|
readonly path: string;
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Optional database ID where the entity is stored (if multiple databases are used)
|
|
47
|
+
*/
|
|
48
|
+
readonly databaseId?: string;
|
|
49
|
+
constructor(id: string, path: string, databaseId?: string);
|
|
46
50
|
get pathWithId(): string;
|
|
47
51
|
isEntityReference(): boolean;
|
|
48
52
|
}
|
package/dist/types/firecms.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { UserConfigurationPersistence } from "./local_config_persistence";
|
|
|
13
13
|
import { FireCMSPlugin } from "./plugins";
|
|
14
14
|
import { CMSAnalyticsEvent } from "./analytics";
|
|
15
15
|
import { EntityAction } from "./entity_actions";
|
|
16
|
+
import { InternalUserManagement } from "./internal_user_management";
|
|
16
17
|
/**
|
|
17
18
|
* Use this callback to build entity collections dynamically.
|
|
18
19
|
* You can use the user to decide which collections to show.
|
|
@@ -123,6 +124,7 @@ export type FireCMSProps<USER extends User> = {
|
|
|
123
124
|
* Use plugins to modify the behaviour of the CMS.
|
|
124
125
|
* DEPRECATED: use the `plugins` prop in the `useBuildNavigationController` instead.
|
|
125
126
|
* This prop will work as a fallback for the `plugins` prop in the `useBuildNavigationController`.
|
|
127
|
+
* @deprecated
|
|
126
128
|
*/
|
|
127
129
|
plugins?: FireCMSPlugin<any, any, any>[];
|
|
128
130
|
/**
|
|
@@ -134,6 +136,19 @@ export type FireCMSProps<USER extends User> = {
|
|
|
134
136
|
* The function must return a URL that gets opened when the button is clicked
|
|
135
137
|
*/
|
|
136
138
|
entityLinkBuilder?: EntityLinkBuilder;
|
|
139
|
+
/**
|
|
140
|
+
* You can use this props to provide your own user management implementation.
|
|
141
|
+
* Note that this will not affect the UI, but it will be used to show user information
|
|
142
|
+
* in various places of the CMS, for example, to show who created or modified an entity,
|
|
143
|
+
* or to assign ownership of an entity.
|
|
144
|
+
*
|
|
145
|
+
* You can also use this data to be retrieved in your custom properties,
|
|
146
|
+
* for example, to show a list of users in a dropdown.
|
|
147
|
+
*
|
|
148
|
+
* If you are using the FireCMS user management plugin, this
|
|
149
|
+
* prop will be implemented automatically.
|
|
150
|
+
*/
|
|
151
|
+
userManagement?: InternalUserManagement;
|
|
137
152
|
components?: {
|
|
138
153
|
/**
|
|
139
154
|
* Component to render when a reference is missing
|
|
@@ -10,6 +10,7 @@ import { SideDialogsController } from "./side_dialogs_controller";
|
|
|
10
10
|
import { DialogsController } from "./dialogs_controller";
|
|
11
11
|
import { CustomizationController } from "./customization_controller";
|
|
12
12
|
import { AnalyticsController } from "./analytics_controller";
|
|
13
|
+
import { InternalUserManagement } from "./internal_user_management";
|
|
13
14
|
/**
|
|
14
15
|
* Context that includes the internal controllers and contexts used by the app.
|
|
15
16
|
* Some controllers and context included in this context can be accessed
|
|
@@ -67,4 +68,19 @@ export type FireCMSContext<USER extends User = User, AuthControllerType extends
|
|
|
67
68
|
* Callback to send analytics events
|
|
68
69
|
*/
|
|
69
70
|
analyticsController?: AnalyticsController;
|
|
71
|
+
/**
|
|
72
|
+
* This section is used to manage users in the CMS.
|
|
73
|
+
* It is used to show user information in various places of the CMS,
|
|
74
|
+
* for example, to show who created or modified an entity,
|
|
75
|
+
* or to assign ownership of an entity.
|
|
76
|
+
*
|
|
77
|
+
* In the base CMS, this information is not used for access control.
|
|
78
|
+
* You can pass your own implementation of this section, to populate
|
|
79
|
+
* the dropdown of users when assigning ownership of an entity,
|
|
80
|
+
* or to show more information about the user.
|
|
81
|
+
*
|
|
82
|
+
* If you are using the FireCMS user management plugin, this
|
|
83
|
+
* section will be implemented automatically.
|
|
84
|
+
*/
|
|
85
|
+
userManagement: InternalUserManagement<USER>;
|
|
70
86
|
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from "./fields";
|
|
|
13
13
|
export * from "./property_config";
|
|
14
14
|
export * from "./datasource";
|
|
15
15
|
export * from "./entity_link_builder";
|
|
16
|
+
export * from "./internal_user_management";
|
|
16
17
|
export * from "./side_entity_controller";
|
|
17
18
|
export * from "./side_dialogs_controller";
|
|
18
19
|
export * from "./firecms_context";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { User } from "./user";
|
|
2
|
+
export type InternalUserManagement<USER extends User = User> = {
|
|
3
|
+
/**
|
|
4
|
+
* List of users to be managed by the CMS.
|
|
5
|
+
*/
|
|
6
|
+
users: USER[];
|
|
7
|
+
/**
|
|
8
|
+
* Function to get a user by its uid. This is used to show
|
|
9
|
+
* user information when assigning ownership of an entity.
|
|
10
|
+
*
|
|
11
|
+
* You can pass your own implementation if you want to show
|
|
12
|
+
* more information about the user.
|
|
13
|
+
*
|
|
14
|
+
* If you are using the FireCMS user management plugin, this
|
|
15
|
+
* function will be implemented automatically.
|
|
16
|
+
*
|
|
17
|
+
* @param uid
|
|
18
|
+
*/
|
|
19
|
+
getUser: (uid: string) => USER | null;
|
|
20
|
+
};
|
package/dist/types/plugins.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { CMSType, Property } from "./properties";
|
|
|
7
7
|
import { EntityStatus } from "./entities";
|
|
8
8
|
import { ResolvedProperty } from "./resolved_entities";
|
|
9
9
|
import { NavigationGroupMapping } from "./navigation";
|
|
10
|
+
import { InternalUserManagement } from "./internal_user_management";
|
|
10
11
|
/**
|
|
11
12
|
* Interface used to define plugins for FireCMS.
|
|
12
13
|
* NOTE: This is a work in progress and the API is not stable yet.
|
|
@@ -37,6 +38,7 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
|
|
|
37
38
|
}>>;
|
|
38
39
|
props?: PROPS;
|
|
39
40
|
};
|
|
41
|
+
userManagement?: InternalUserManagement;
|
|
40
42
|
homePage?: {
|
|
41
43
|
/**
|
|
42
44
|
* Additional actions to be rendered in the home page, close to the search bar.
|
|
@@ -102,6 +102,10 @@ export interface BaseProperty<T extends CMSType, CustomProps = any> {
|
|
|
102
102
|
/**
|
|
103
103
|
* Should this property be editable. If set to true, the user will be able to modify the property and
|
|
104
104
|
* save the new config. The saved config will then become the source of truth.
|
|
105
|
+
* Defaults to `true.
|
|
106
|
+
* This props is only useful when you are using the collection editor to modify collection
|
|
107
|
+
* configurations from the CMS itself. You can also use the `editable` prop in the
|
|
108
|
+
* `EntityCollection` interface to disable the edition of all properties in a collection.
|
|
105
109
|
*/
|
|
106
110
|
editable?: boolean;
|
|
107
111
|
/**
|
|
@@ -291,6 +295,15 @@ export interface StringProperty extends BaseProperty<string> {
|
|
|
291
295
|
* indicate that this string refers to a path in your storage provider.
|
|
292
296
|
*/
|
|
293
297
|
storage?: StorageConfig;
|
|
298
|
+
/**
|
|
299
|
+
* This property is used to indicate that the string is a user ID, and
|
|
300
|
+
* it will be rendered as a user picker.
|
|
301
|
+
* Note that the user ID needs to be the one used in your authentication
|
|
302
|
+
* provider, e.g. Firebase Auth.
|
|
303
|
+
* You can also use a property builder to specify the user path dynamically
|
|
304
|
+
* based on other values of the entity.
|
|
305
|
+
*/
|
|
306
|
+
userSelect?: boolean;
|
|
294
307
|
/**
|
|
295
308
|
* If the value of this property is a URL, you can set this flag to true
|
|
296
309
|
* to add a link, or one of the supported media types to render a preview
|
|
@@ -608,8 +621,15 @@ export type StorageConfig = {
|
|
|
608
621
|
/**
|
|
609
622
|
* Use client side image compression and resizing
|
|
610
623
|
* Will only be applied to these MIME types: image/jpeg, image/png and image/webp
|
|
624
|
+
* @deprecated Use `imageResize` instead
|
|
611
625
|
*/
|
|
612
|
-
imageCompression?:
|
|
626
|
+
imageCompression?: ImageResize;
|
|
627
|
+
/**
|
|
628
|
+
* Advanced image resizing and cropping configuration.
|
|
629
|
+
* Applied before upload to optimize storage and bandwidth.
|
|
630
|
+
* Only applies to image MIME types: image/jpeg, image/png, image/webp
|
|
631
|
+
*/
|
|
632
|
+
imageResize?: ImageResize;
|
|
613
633
|
/**
|
|
614
634
|
* Specific metadata set in your uploaded file.
|
|
615
635
|
* For the default Firebase implementation, the values passed here are of type
|
|
@@ -721,17 +741,32 @@ export type PreviewType = "image" | "video" | "audio" | "file";
|
|
|
721
741
|
* @group Entity properties
|
|
722
742
|
*/
|
|
723
743
|
export type FileType = "image/*" | "video/*" | "audio/*" | "application/*" | "text/*" | "font/*" | string;
|
|
724
|
-
export interface
|
|
744
|
+
export interface ImageResize {
|
|
745
|
+
/**
|
|
746
|
+
* Maximum width in pixels. Image will be scaled down proportionally if wider.
|
|
747
|
+
*/
|
|
748
|
+
maxWidth?: number;
|
|
725
749
|
/**
|
|
726
|
-
*
|
|
750
|
+
* Maximum height in pixels. Image will be scaled down proportionally if taller.
|
|
727
751
|
*/
|
|
728
752
|
maxHeight?: number;
|
|
729
753
|
/**
|
|
730
|
-
*
|
|
754
|
+
* Resize mode determines how the image fits within maxWidth/maxHeight bounds.
|
|
755
|
+
* - `contain`: Scale down to fit within bounds, preserving aspect ratio (default)
|
|
756
|
+
* - `cover`: Scale to fill bounds, preserving aspect ratio (may crop)
|
|
731
757
|
*/
|
|
732
|
-
|
|
758
|
+
mode?: 'contain' | 'cover';
|
|
759
|
+
/**
|
|
760
|
+
* Output format for the resized image.
|
|
761
|
+
* - `original`: Keep the original format (default)
|
|
762
|
+
* - `jpeg`: Convert to JPEG
|
|
763
|
+
* - `png`: Convert to PNG
|
|
764
|
+
* - `webp`: Convert to WebP
|
|
765
|
+
*/
|
|
766
|
+
format?: 'original' | 'jpeg' | 'png' | 'webp';
|
|
733
767
|
/**
|
|
734
|
-
*
|
|
768
|
+
* Quality for lossy formats (JPEG, WebP). Number between 0 and 100.
|
|
769
|
+
* Higher is better quality but larger file size. Defaults to 80.
|
|
735
770
|
*/
|
|
736
771
|
quality?: number;
|
|
737
772
|
}
|
|
@@ -37,4 +37,4 @@ export type PropertyConfig<T extends CMSType = any> = {
|
|
|
37
37
|
*/
|
|
38
38
|
description?: string;
|
|
39
39
|
};
|
|
40
|
-
export type PropertyConfigId = "text_field" | "multiline" | "markdown" | "url" | "email" | "select" | "multi_select" | "number_input" | "number_select" | "multi_number_select" | "file_upload" | "multi_file_upload" | "group" | "key_value" | "reference" | "reference_as_string" | "multi_references" | "switch" | "date_time" | "repeat" | "custom_array" | "block";
|
|
40
|
+
export type PropertyConfigId = "text_field" | "multiline" | "markdown" | "url" | "email" | "user_select" | "select" | "multi_select" | "number_input" | "number_select" | "multi_number_select" | "file_upload" | "multi_file_upload" | "group" | "key_value" | "reference" | "reference_as_string" | "multi_references" | "switch" | "date_time" | "repeat" | "custom_array" | "block";
|
package/dist/types/user.d.ts
CHANGED
|
@@ -9,3 +9,4 @@ export declare function resolveDefaultSelectedView(defaultSelectedView: string |
|
|
|
9
9
|
* @param permissionsBuilder
|
|
10
10
|
*/
|
|
11
11
|
export declare const applyPermissionsFunctionIfEmpty: (collections: EntityCollection[], permissionsBuilder?: PermissionsBuilder<any, any>) => EntityCollection[];
|
|
12
|
+
export declare function getLocalChangesBackup(collection: EntityCollection): "manual_apply" | "auto_apply";
|
|
@@ -4,14 +4,18 @@
|
|
|
4
4
|
* @param data - The data to cache and persist.
|
|
5
5
|
*/
|
|
6
6
|
export declare function saveEntityToCache(path: string, data: object): void;
|
|
7
|
+
export declare function removeEntityFromMemoryCache(path: string): void;
|
|
8
|
+
export declare function saveEntityToMemoryCache(path: string, data: object): void;
|
|
9
|
+
export declare function getEntityFromMemoryCache(path: string): object | undefined;
|
|
10
|
+
export declare function hasEntityInCache(path: string): boolean;
|
|
7
11
|
/**
|
|
8
12
|
* Retrieves an entity from the in-memory cache or `localStorage`.
|
|
9
13
|
* If the entity is not in the cache but exists in `localStorage`, it loads it into the cache.
|
|
10
14
|
* @param path - The unique path/key for the entity.
|
|
15
|
+
* @param useLocalStorage
|
|
11
16
|
* @returns The cached entity or `undefined` if not found.
|
|
12
17
|
*/
|
|
13
18
|
export declare function getEntityFromCache(path: string): object | undefined;
|
|
14
|
-
export declare function hasEntityInCache(path: string): boolean;
|
|
15
19
|
/**
|
|
16
20
|
* Removes an entity from both the in-memory cache and `localStorage`.
|
|
17
21
|
* @param path - The unique path/key for the entity to remove.
|
|
@@ -21,3 +25,4 @@ export declare function removeEntityFromCache(path: string): void;
|
|
|
21
25
|
* Clears the entire in-memory cache and removes all related entities from `localStorage`.
|
|
22
26
|
*/
|
|
23
27
|
export declare function clearEntityCache(): void;
|
|
28
|
+
export declare function flattenKeys(obj: any, prefix?: string, result?: string[]): string[];
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import { Properties
|
|
1
|
+
import { Properties } from "../types";
|
|
2
2
|
export declare function makePropertiesEditable(properties: Properties): Properties<any>;
|
|
3
|
-
export declare function makePropertiesNonEditable(properties: PropertiesOrBuilders): PropertiesOrBuilders;
|
package/dist/util/objects.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const pick: <T>(obj: T, ...args: any[]) => T;
|
|
2
2
|
export declare function isObject(item: any): any;
|
|
3
|
+
export declare function isPlainObject(obj: any): boolean;
|
|
3
4
|
export declare function mergeDeep<T extends Record<any, any>, U extends Record<any, any>>(target: T, source: U, ignoreUndefined?: boolean): T & U;
|
|
4
5
|
export declare function getValueInPath(o: object | undefined, path: string): any;
|
|
5
6
|
export declare function removeInPath(o: object, path: string): object | undefined;
|
|
@@ -31,6 +31,7 @@ export declare function useStorageUploadController<M extends object>({ entityId,
|
|
|
31
31
|
fileNameBuilder: (file: File) => Promise<string>;
|
|
32
32
|
storagePathBuilder: (file: File) => string;
|
|
33
33
|
onFileUploadComplete: (uploadedPath: string, entry: StorageFieldItem, metadata?: any) => Promise<void>;
|
|
34
|
+
onFileUploadError: (entry: StorageFieldItem) => void;
|
|
34
35
|
onFilesAdded: (acceptedFiles: File[]) => Promise<void>;
|
|
35
36
|
multipleFilesSupported: boolean;
|
|
36
37
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.0-rc.
|
|
4
|
+
"version": "3.0.0-rc.3",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -53,11 +53,12 @@
|
|
|
53
53
|
"@dnd-kit/core": "^6.3.1",
|
|
54
54
|
"@dnd-kit/modifiers": "^9.0.0",
|
|
55
55
|
"@dnd-kit/sortable": "^10.0.0",
|
|
56
|
-
"@firecms/editor": "^3.0.0-rc.
|
|
57
|
-
"@firecms/formex": "^3.0.0-rc.
|
|
58
|
-
"@firecms/ui": "^3.0.0-rc.
|
|
56
|
+
"@firecms/editor": "^3.0.0-rc.3",
|
|
57
|
+
"@firecms/formex": "^3.0.0-rc.3",
|
|
58
|
+
"@firecms/ui": "^3.0.0-rc.3",
|
|
59
59
|
"@radix-ui/react-portal": "^1.1.9",
|
|
60
60
|
"clsx": "^2.1.1",
|
|
61
|
+
"compressorjs": "^1.2.1",
|
|
61
62
|
"date-fns": "^3.6.0",
|
|
62
63
|
"fuse.js": "^7.1.0",
|
|
63
64
|
"history": "^5.3.0",
|
|
@@ -67,7 +68,6 @@
|
|
|
67
68
|
"prism-react-renderer": "^2.4.1",
|
|
68
69
|
"react-dropzone": "^14.3.8",
|
|
69
70
|
"react-fast-compare": "^3.2.2",
|
|
70
|
-
"react-image-file-resizer": "^0.4.8",
|
|
71
71
|
"react-transition-group": "^4.4.5",
|
|
72
72
|
"react-use-measure": "^2.1.7",
|
|
73
73
|
"react-window": "^1.8.11",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"dist",
|
|
109
109
|
"src"
|
|
110
110
|
],
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "213f4c106eb3e6639eb1ad23c7d94282ef54d444",
|
|
112
112
|
"publishConfig": {
|
|
113
113
|
"access": "public"
|
|
114
114
|
},
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
import React, { MouseEvent, useCallback } from "react";
|
|
2
2
|
|
|
3
3
|
import { CollectionSize, Entity, EntityAction, EntityCollection, SelectionController } from "../../types";
|
|
4
|
-
import {
|
|
5
|
-
Checkbox,
|
|
6
|
-
Chip,
|
|
7
|
-
cls,
|
|
8
|
-
EditIcon,
|
|
9
|
-
IconButton,
|
|
10
|
-
Menu,
|
|
11
|
-
MenuItem,
|
|
12
|
-
MoreVertIcon,
|
|
13
|
-
Skeleton,
|
|
14
|
-
Tooltip
|
|
15
|
-
} from "@firecms/ui";
|
|
4
|
+
import { Badge, Checkbox, cls, IconButton, Menu, MenuItem, MoreVertIcon, Skeleton, Tooltip } from "@firecms/ui";
|
|
16
5
|
import { useFireCMSContext, useLargeLayout } from "../../hooks";
|
|
17
|
-
import {
|
|
6
|
+
import { getEntityFromCache } from "../../util/entity_cache";
|
|
7
|
+
import { getLocalChangesBackup } from "../../util";
|
|
18
8
|
|
|
19
9
|
/**
|
|
20
10
|
*
|
|
@@ -79,7 +69,9 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
|
|
|
79
69
|
|
|
80
70
|
const collapsedActions = actions.filter(a => a.collapsed || a.collapsed === undefined);
|
|
81
71
|
const uncollapsedActions = actions.filter(a => a.collapsed === false);
|
|
82
|
-
const
|
|
72
|
+
const enableLocalChangesBackup = collection ? getLocalChangesBackup(collection) : false;
|
|
73
|
+
const hasDraft = enableLocalChangesBackup ? getEntityFromCache(fullPath + "/" + entity.id) : false;
|
|
74
|
+
const iconSize = largeLayout && (size === "m" || size === "l" || size == "xl") ? "medium" : "small";
|
|
83
75
|
return (
|
|
84
76
|
<div
|
|
85
77
|
className={cls(
|
|
@@ -99,37 +91,50 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
|
|
|
99
91
|
{(hasActions || selectionEnabled) &&
|
|
100
92
|
<div className="w-34 flex justify-center">
|
|
101
93
|
|
|
102
|
-
{uncollapsedActions.map((action, index) =>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
94
|
+
{uncollapsedActions.map((action, index) => {
|
|
95
|
+
const isEditAction = action.key === "edit";
|
|
96
|
+
const tooltip = isEditAction && hasDraft ? "Local unsaved changes" : action.name;
|
|
97
|
+
|
|
98
|
+
let iconButton = <IconButton
|
|
99
|
+
onClick={(event: MouseEvent) => {
|
|
100
|
+
event.stopPropagation();
|
|
101
|
+
action.onClick({
|
|
102
|
+
view: "collection",
|
|
103
|
+
entity,
|
|
104
|
+
fullPath,
|
|
105
|
+
fullIdPath,
|
|
106
|
+
collection,
|
|
107
|
+
context,
|
|
108
|
+
selectionController,
|
|
109
|
+
highlightEntity,
|
|
110
|
+
unhighlightEntity,
|
|
111
|
+
onCollectionChange,
|
|
112
|
+
openEntityMode: openEntityMode ?? collection?.openEntityMode
|
|
113
|
+
});
|
|
114
|
+
}}
|
|
115
|
+
size={iconSize}>
|
|
116
|
+
{action.icon}
|
|
117
|
+
</IconButton>;
|
|
118
|
+
if (isEditAction && hasDraft) {
|
|
119
|
+
iconButton = (
|
|
120
|
+
<Badge color={"warning"}>
|
|
121
|
+
{iconButton}
|
|
122
|
+
</Badge>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return (
|
|
126
|
+
<Tooltip key={index}
|
|
127
|
+
title={tooltip}
|
|
128
|
+
asChild={true}>
|
|
129
|
+
{iconButton}
|
|
130
|
+
</Tooltip>
|
|
131
|
+
);
|
|
132
|
+
})}
|
|
128
133
|
|
|
129
134
|
{hasCollapsedActions &&
|
|
130
135
|
<Menu
|
|
131
136
|
trigger={<IconButton
|
|
132
|
-
size={
|
|
137
|
+
size={iconSize}>
|
|
133
138
|
<MoreVertIcon/>
|
|
134
139
|
</IconButton>}>
|
|
135
140
|
{collapsedActions.map((action, index) => (
|
|
@@ -161,7 +166,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
|
|
|
161
166
|
{selectionEnabled &&
|
|
162
167
|
<Tooltip title={`Select ${entity.id}`}>
|
|
163
168
|
<Checkbox
|
|
164
|
-
size={
|
|
169
|
+
size={iconSize}
|
|
165
170
|
checked={Boolean(isSelected)}
|
|
166
171
|
onCheckedChange={onCheckedChange}
|
|
167
172
|
/>
|
|
@@ -175,11 +180,6 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
|
|
|
175
180
|
onClick={(event) => {
|
|
176
181
|
event.stopPropagation();
|
|
177
182
|
}}>
|
|
178
|
-
{hasDraft && <Tooltip title={"Local unsaved changes"} className={"inline"}>
|
|
179
|
-
<Chip colorScheme={"orangeDarker"} className={"p-0.5"}>
|
|
180
|
-
<EditIcon size={12}/>
|
|
181
|
-
</Chip>
|
|
182
|
-
</Tooltip>}
|
|
183
183
|
<span className="min-w-0 truncate text-center">
|
|
184
184
|
{entity
|
|
185
185
|
? entity.id
|
|
@@ -17,6 +17,7 @@ import { VirtualTableSelect } from "../VirtualTable/fields/VirtualTableSelect";
|
|
|
17
17
|
import { VirtualTableNumberInput } from "../VirtualTable/fields/VirtualTableNumberInput";
|
|
18
18
|
import { VirtualTableSwitch } from "../VirtualTable/fields/VirtualTableSwitch";
|
|
19
19
|
import { VirtualTableDateField } from "../VirtualTable/fields/VirtualTableDateField";
|
|
20
|
+
import { VirtualTableUserSelect } from "../VirtualTable/fields/VirtualTableUserSelect";
|
|
20
21
|
|
|
21
22
|
import { TableStorageUpload } from "./fields/TableStorageUpload";
|
|
22
23
|
import { TableReferenceField } from "./fields/TableReferenceField";
|
|
@@ -332,6 +333,17 @@ export const PropertyTableCell = React.memo<PropertyTableCellProps<any>>(
|
|
|
332
333
|
updateValue={updateValue}
|
|
333
334
|
/>;
|
|
334
335
|
fullHeight = true;
|
|
336
|
+
} else if (stringProperty.userSelect) {
|
|
337
|
+
innerComponent = <VirtualTableUserSelect name={propertyKey as string}
|
|
338
|
+
multiple={false}
|
|
339
|
+
focused={selected}
|
|
340
|
+
disabled={disabled}
|
|
341
|
+
small={getPreviewSizeFrom(size) !== "medium"}
|
|
342
|
+
error={validationError ?? error}
|
|
343
|
+
internalValue={internalValue as string}
|
|
344
|
+
updateValue={updateValue}
|
|
345
|
+
/>;
|
|
346
|
+
fullHeight = true;
|
|
335
347
|
} else if (stringProperty.markdown || !stringProperty.storage || !stringProperty.reference) {
|
|
336
348
|
const multiline = Boolean(stringProperty.multiline) || Boolean(stringProperty.markdown);
|
|
337
349
|
innerComponent = <VirtualTableInput error={validationError ?? error}
|
|
@@ -376,6 +376,11 @@ export const EntityCollectionView = React.memo(
|
|
|
376
376
|
console.error("Save failure");
|
|
377
377
|
console.error(e);
|
|
378
378
|
setError(e);
|
|
379
|
+
},
|
|
380
|
+
onPreSaveHookError: (e: Error) => {
|
|
381
|
+
console.error("Pre-save hook error");
|
|
382
|
+
console.error(e);
|
|
383
|
+
setError(e);
|
|
379
384
|
}
|
|
380
385
|
});
|
|
381
386
|
|
|
@@ -694,7 +699,7 @@ export const EntityCollectionView = React.memo(
|
|
|
694
699
|
className="mt-4"
|
|
695
700
|
>
|
|
696
701
|
<AddIcon/>
|
|
697
|
-
Create your first
|
|
702
|
+
Create your first entry
|
|
698
703
|
</Button>
|
|
699
704
|
</div>
|
|
700
705
|
: <Typography variant={"label"}>No results with the applied filter/sort</Typography>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
|
-
import { PropertyPreview } from "../preview";
|
|
3
2
|
import { Entity, EntityCollection, ResolvedEntityCollection, ResolvedProperties } from "../types";
|
|
4
3
|
import { resolveCollection } from "../util";
|
|
5
|
-
import { cls, defaultBorderMixin, IconButton, OpenInNewIcon } from "@firecms/ui";
|
|
4
|
+
import { cls, defaultBorderMixin, IconButton, OpenInNewIcon, Typography } from "@firecms/ui";
|
|
6
5
|
import { CustomizationController } from "../types/customization_controller";
|
|
7
6
|
import { useCustomizationController } from "../hooks/useCustomizationController";
|
|
8
7
|
import { useAuthController } from "../hooks";
|
|
8
|
+
import { PropertyCollectionView } from "./PropertyCollectionView";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @group Components
|
|
@@ -40,47 +40,36 @@ export function EntityView<M extends Record<string, any>>(
|
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
42
|
<div className={"w-full " + className}>
|
|
43
|
-
<div className={"w-full mb-4"}>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
<div className={"w-full mb-4 p-4"}>
|
|
44
|
+
|
|
45
|
+
<div className={`grid grid-cols-12 gap-x-4 py-4 items-start border-b ${defaultBorderMixin}`}>
|
|
46
|
+
<div className="col-span-4 pr-2">
|
|
47
|
+
<Typography variant="caption"
|
|
48
|
+
color={"secondary"}
|
|
49
|
+
component={"span"}
|
|
50
|
+
className="break-words">
|
|
51
|
+
Id
|
|
52
|
+
</Typography>
|
|
47
53
|
</div>
|
|
48
|
-
<div
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
<div className="col-span-8">
|
|
55
|
+
<div
|
|
56
|
+
className="flex-grow text-surface-900 dark:text-white flex items-center">
|
|
57
|
+
<span className="flex-grow mr-2">{entity.id}</span>
|
|
58
|
+
{customizationController?.entityLinkBuilder &&
|
|
59
|
+
<a href={customizationController.entityLinkBuilder({ entity })}
|
|
60
|
+
rel="noopener noreferrer"
|
|
61
|
+
target="_blank">
|
|
62
|
+
<IconButton>
|
|
63
|
+
<OpenInNewIcon
|
|
64
|
+
size={"small"}/>
|
|
65
|
+
</IconButton>
|
|
66
|
+
</a>}
|
|
67
|
+
</div>
|
|
60
68
|
</div>
|
|
61
69
|
</div>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<div
|
|
67
|
-
key={`reference_previews_${key}`}
|
|
68
|
-
className={cls(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0")}>
|
|
69
|
-
<div className="flex items-center w-1/4">
|
|
70
|
-
<span className="pl-2 text-sm text-surface-600">{property.name}</span>
|
|
71
|
-
</div>
|
|
72
|
-
<div
|
|
73
|
-
className="flex-grow p-2 ml-2 w-3/4 text-surface-900 dark:text-white min-h-[56px] flex items-center">
|
|
74
|
-
<PropertyPreview
|
|
75
|
-
propertyKey={key}
|
|
76
|
-
value={value}
|
|
77
|
-
// entity={entity}
|
|
78
|
-
property={property}
|
|
79
|
-
size={"medium"}/>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
)
|
|
83
|
-
})}
|
|
70
|
+
|
|
71
|
+
<PropertyCollectionView data={entity.values} properties={properties} size={"medium"}/>
|
|
72
|
+
|
|
84
73
|
</div>
|
|
85
74
|
</div>
|
|
86
75
|
);
|