@firecms/core 3.0.0-canary.168 → 3.0.0-canary.169

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 (96) hide show
  1. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
  2. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  3. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +15 -1
  4. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  5. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
  6. package/dist/components/SelectableTable/SelectableTable.d.ts +12 -2
  7. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  8. package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -0
  9. package/dist/components/common/index.d.ts +1 -1
  10. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +10 -2
  11. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  12. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  13. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  14. package/dist/core/EntityEditView.d.ts +25 -7
  15. package/dist/core/FireCMSRouter.d.ts +4 -0
  16. package/dist/core/NavigationRoutes.d.ts +0 -1
  17. package/dist/core/index.d.ts +2 -1
  18. package/dist/hooks/data/useEntityFetch.d.ts +2 -1
  19. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  20. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  21. package/dist/index.es.js +2344 -1305
  22. package/dist/index.es.js.map +1 -1
  23. package/dist/index.umd.js +2344 -1306
  24. package/dist/index.umd.js.map +1 -1
  25. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  26. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  27. package/dist/routes/FireCMSRoute.d.ts +1 -0
  28. package/dist/routes/HomePageRoute.d.ts +3 -0
  29. package/dist/types/collections.d.ts +18 -6
  30. package/dist/types/datasource.d.ts +2 -2
  31. package/dist/types/dialogs_controller.d.ts +3 -7
  32. package/dist/types/entities.d.ts +1 -1
  33. package/dist/types/entity_actions.d.ts +1 -6
  34. package/dist/types/navigation.d.ts +28 -12
  35. package/dist/types/plugins.d.ts +1 -0
  36. package/dist/types/side_entity_controller.d.ts +1 -1
  37. package/dist/util/entity_cache.d.ts +23 -0
  38. package/dist/util/navigation_from_path.d.ts +5 -1
  39. package/dist/util/navigation_utils.d.ts +12 -1
  40. package/package.json +5 -5
  41. package/src/components/CircularProgressCenter.tsx +1 -1
  42. package/src/components/DeleteEntityDialog.tsx +1 -1
  43. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +40 -19
  44. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -4
  45. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +18 -1
  46. package/src/components/EntityCollectionTable/index.tsx +1 -1
  47. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
  48. package/src/components/EntityCollectionView/EntityCollectionView.tsx +79 -38
  49. package/src/components/NotFoundPage.tsx +2 -2
  50. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -5
  51. package/src/components/SelectableTable/SelectableTable.tsx +18 -3
  52. package/src/components/UnsavedChangesDialog.tsx +42 -0
  53. package/src/components/VirtualTable/VirtualTable.tsx +19 -2
  54. package/src/components/VirtualTable/VirtualTableProps.tsx +15 -0
  55. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +1 -1
  56. package/src/components/common/default_entity_actions.tsx +24 -10
  57. package/src/components/common/index.ts +1 -1
  58. package/src/components/common/{useDataSourceEntityCollectionTableController.tsx → useDataSourceTableController.tsx} +122 -13
  59. package/src/components/common/useDebouncedCallback.tsx +20 -0
  60. package/src/components/common/useScrollRestoration.tsx +68 -0
  61. package/src/contexts/BreacrumbsContext.tsx +38 -0
  62. package/src/contexts/DialogsProvider.tsx +2 -3
  63. package/src/core/DefaultAppBar.tsx +28 -4
  64. package/src/core/EntityEditView.tsx +457 -296
  65. package/src/core/EntitySidePanel.tsx +72 -6
  66. package/src/core/FireCMS.tsx +7 -4
  67. package/src/core/FireCMSRouter.tsx +17 -0
  68. package/src/core/NavigationRoutes.tsx +15 -28
  69. package/src/core/SideDialogs.tsx +10 -5
  70. package/src/core/index.tsx +4 -2
  71. package/src/hooks/data/save.ts +1 -1
  72. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  73. package/src/hooks/data/useEntityFetch.tsx +5 -1
  74. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  75. package/src/hooks/useBuildNavigationController.tsx +59 -21
  76. package/src/hooks/useLargeLayout.tsx +0 -35
  77. package/src/hooks/useResolvedNavigationFrom.tsx +4 -6
  78. package/src/internal/useBuildSideDialogsController.tsx +4 -2
  79. package/src/internal/useBuildSideEntityController.tsx +157 -77
  80. package/src/internal/useUnsavedChangesDialog.tsx +126 -92
  81. package/src/routes/CustomCMSRoute.tsx +21 -0
  82. package/src/routes/FireCMSRoute.tsx +230 -0
  83. package/src/routes/HomePageRoute.tsx +17 -0
  84. package/src/types/collections.ts +19 -6
  85. package/src/types/datasource.ts +2 -1
  86. package/src/types/dialogs_controller.tsx +3 -7
  87. package/src/types/entities.ts +1 -1
  88. package/src/types/entity_actions.tsx +1 -7
  89. package/src/types/navigation.ts +35 -16
  90. package/src/types/plugins.tsx +1 -0
  91. package/src/types/side_entity_controller.tsx +1 -1
  92. package/src/util/entity_cache.ts +196 -0
  93. package/src/util/navigation_from_path.ts +10 -2
  94. package/src/util/navigation_utils.ts +53 -1
  95. package/src/util/parent_references_from_path.ts +3 -3
  96. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -1,14 +1,12 @@
1
- import React from "react";
1
+ /**
2
+ * Custom hook to handle navigation blocking when there are unsaved changes.
3
+ *
4
+ * @param when - Indicates whether to block navigation.
5
+ * @param onSuccess - Callback invoked when navigation is confirmed.
6
+ * @returns An object containing the state of navigation blocking and handlers.
7
+ */
2
8
  export declare function useNavigationUnsavedChangesDialog(when: boolean, onSuccess: () => void): {
3
9
  navigationWasBlocked: boolean;
4
10
  handleCancel: () => void;
5
11
  handleOk: () => void;
6
12
  };
7
- export interface UnsavedChangesDialogProps {
8
- open: boolean;
9
- body?: React.ReactNode;
10
- title?: string;
11
- handleOk: () => void;
12
- handleCancel: () => void;
13
- }
14
- export declare function UnsavedChangesDialog({ open, handleOk, handleCancel, body, title }: UnsavedChangesDialogProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { CMSView } from "../types";
2
+ export declare function CustomCMSRoute({ cmsView }: {
3
+ cmsView: CMSView;
4
+ }): import("react").ReactNode;
@@ -0,0 +1 @@
1
+ export declare function FireCMSRoute(): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,3 @@
1
+ export declare function HomePageRoute({ children }: {
2
+ children: React.ReactNode;
3
+ }): import("react").ReactNode;
@@ -72,6 +72,10 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
72
72
  * Set of properties that compose an entity
73
73
  */
74
74
  properties: PropertiesOrBuilders<M>;
75
+ /**
76
+ * Default preview properties displayed when this collection is referenced to.
77
+ */
78
+ previewProperties?: string[];
75
79
  /**
76
80
  * Title property of the entity. This is the property that will be used
77
81
  * as the title in entity related views and references.
@@ -79,11 +83,10 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
79
83
  */
80
84
  titleProperty?: keyof M;
81
85
  /**
82
- * Can this collection be edited by the end user.
83
- * Defaults to `true`.
84
- * Keep in mind that you can also set this prop to individual properties.
86
+ * When editing an entity, you can choose to open the entity in a side dialog
87
+ * or in a full screen dialog. Defaults to `full_screen`.
85
88
  */
86
- editable?: boolean;
89
+ openEntityMode?: "side_panel" | "full_screen";
87
90
  /**
88
91
  * Order in which the properties are displayed.
89
92
  * If you are specifying your collection as code, the order is the same as the
@@ -272,9 +275,12 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
272
275
  */
273
276
  sideDialogWidth?: number | string;
274
277
  /**
275
- * Default preview properties displayed when this collection is referenced to.
278
+ * Can this collection configuration be edited by the end user.
279
+ * Defaults to `true`.
280
+ * Keep in mind that you can also set this prop to individual properties.
281
+ * This prop has only effect if you are using the collection editor.
276
282
  */
277
- previewProperties?: string[];
283
+ editable?: boolean;
278
284
  }
279
285
  /**
280
286
  * Parameter passed to the `Actions` prop in the collection configuration.
@@ -485,6 +491,12 @@ export type EntityTableController<M extends Record<string, any> = any> = {
485
491
  clearFilter?: () => void;
486
492
  itemCount?: number;
487
493
  setItemCount?: (itemCount: number) => void;
494
+ initialScroll?: number;
495
+ onScroll?: (props: {
496
+ scrollDirection: "forward" | "backward";
497
+ scrollOffset: number;
498
+ scrollUpdateWasRequested: boolean;
499
+ }) => void;
488
500
  paginationEnabled?: boolean;
489
501
  pageSize?: number;
490
502
  checkFilterCombination?: (filterValues: FilterValues<any>, sortBy?: [string, "asc" | "desc"]) => boolean;
@@ -8,13 +8,13 @@ import { FireCMSContext } from "./firecms_context";
8
8
  export interface FetchEntityProps<M extends Record<string, any> = any> {
9
9
  path: string;
10
10
  entityId: string;
11
+ databaseId?: string;
11
12
  collection?: EntityCollection<M, any>;
12
13
  }
13
14
  /**
14
15
  * @group Datasource
15
16
  */
16
17
  export type ListenEntityProps<M extends Record<string, any> = any> = FetchEntityProps<M> & {
17
- databaseId?: string;
18
18
  onUpdate: (entity: Entity<M>) => void;
19
19
  onError?: (error: Error) => void;
20
20
  };
@@ -99,7 +99,7 @@ export interface DataSource {
99
99
  * @param entityId
100
100
  * @param collection
101
101
  */
102
- fetchEntity<M extends Record<string, any> = any>({ path, entityId, collection }: FetchEntityProps<M>): Promise<Entity<M> | undefined>;
102
+ fetchEntity<M extends Record<string, any> = any>({ path, entityId, databaseId, collection }: FetchEntityProps<M>): Promise<Entity<M> | undefined>;
103
103
  /**
104
104
  * Get realtime updates on one entity.
105
105
  * @param path
@@ -12,7 +12,7 @@ export interface DialogsController {
12
12
  * Open a dialog
13
13
  * @param props
14
14
  */
15
- open: <T extends object = object>(props: DialogControllerEntryProps<T>) => {
15
+ open: (props: DialogControllerEntryProps) => {
16
16
  closeDialog: () => void;
17
17
  };
18
18
  }
@@ -20,7 +20,7 @@ export interface DialogsController {
20
20
  * Props used to open a side dialog
21
21
  * @group Hooks and utilities
22
22
  */
23
- export interface DialogControllerEntryProps<T extends object = object> {
23
+ export interface DialogControllerEntryProps {
24
24
  key: string;
25
25
  /**
26
26
  * The component type that will be rendered
@@ -28,9 +28,5 @@ export interface DialogControllerEntryProps<T extends object = object> {
28
28
  Component: React.ComponentType<{
29
29
  open: boolean;
30
30
  closeDialog: () => void;
31
- } & T>;
32
- /**
33
- * Props to pass to the dialog component
34
- */
35
- props?: T;
31
+ }>;
36
32
  }
@@ -7,7 +7,7 @@ export type EntityStatus = "new" | "existing" | "copy";
7
7
  * Representation of an entity fetched from the datasource
8
8
  * @group Models
9
9
  */
10
- export interface Entity<M extends object> {
10
+ export interface Entity<M extends object = any> {
11
11
  /**
12
12
  * ID of the entity
13
13
  */
@@ -4,7 +4,6 @@ import { Entity } from "./entities";
4
4
  import { EntityCollection, SelectionController } from "./collections";
5
5
  import { User } from "./user";
6
6
  import { SideEntityController } from "./side_entity_controller";
7
- import { FormContext } from "./fields";
8
7
  /**
9
8
  * An entity action is a custom action that can be performed on an entity.
10
9
  * They are displayed in the entity view and in the collection view.
@@ -56,9 +55,5 @@ export type EntityActionClickProps<M extends object, USER extends User = User> =
56
55
  * If this actions is being called from a side dialog
57
56
  */
58
57
  sideEntityController?: SideEntityController;
59
- /**
60
- * You can use the form context to manage the state of the form.
61
- * This is only available if this action in being triggered from a form context.
62
- */
63
- formContext?: FormContext<M>;
58
+ openEntityMode: "side_panel" | "full_screen";
64
59
  };
@@ -57,11 +57,13 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
57
57
  */
58
58
  getCollectionFromPaths: (pathSegments: string[]) => EC | undefined;
59
59
  /**
60
- * Default path under the navigation routes of the CMS will be created
60
+ * Default path under the navigation routes of the CMS will be created.
61
+ * Defaults to '/'. You may want to change this `basepath` to 'admin' for example.
61
62
  */
62
63
  basePath: string;
63
64
  /**
64
- * Default path under the collection routes of the CMS will be created
65
+ * Default path under the collection routes of the CMS will be created.
66
+ * It defaults to '/c'
65
67
  */
66
68
  baseCollectionPath: string;
67
69
  /**
@@ -72,14 +74,6 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
72
74
  * @param cmsPath
73
75
  */
74
76
  urlPathToDataPath: (cmsPath: string) => string;
75
- /**
76
- * Convert a collection or entity path to a URL path
77
- * @param path
78
- */
79
- buildCMSUrlPath: (path: string) => string;
80
- buildUrlEditCollectionPath: (props: {
81
- path: string;
82
- }) => string;
83
77
  /**
84
78
  * Base url path for the home screen
85
79
  */
@@ -97,10 +91,11 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
97
91
  */
98
92
  buildUrlCollectionPath: (path: string) => string;
99
93
  /**
100
- * Turn a path with aliases into a resolved path
94
+ * Turn a path with collection ids into a resolved path.
95
+ * The ids (typically used in urls) will be replaced with relative paths (typically used in database paths)
101
96
  * @param pathWithAliases
102
97
  */
103
- resolveAliasesFrom: (pathWithAliases: string) => string;
98
+ resolveIdsFrom: (pathWithAliases: string) => string;
104
99
  /**
105
100
  * Call this method to recalculate the navigation
106
101
  */
@@ -120,6 +115,27 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
120
115
  * @param ids
121
116
  */
122
117
  convertIdsToPaths: (ids: string[]) => string[];
118
+ /**
119
+ * A function to navigate to a specified route or URL.
120
+ *
121
+ * @param {string} to - The target route or URL to navigate to.
122
+ * @param {NavigateOptions} [options] - Optional configuration settings for navigation, such as replace behavior or state data.
123
+ */
124
+ navigate: (to: string, options?: NavigateOptions) => void;
125
+ };
126
+ export interface NavigateOptions {
127
+ replace?: boolean;
128
+ state?: any;
129
+ preventScrollReset?: boolean;
130
+ relative?: "route" | "path";
131
+ flushSync?: boolean;
132
+ viewTransition?: boolean;
133
+ }
134
+ export type NavigationBlocker = {
135
+ updateBlockListener: (path: string, block: boolean, basePath?: string) => () => void;
136
+ isBlocked: (path: string) => boolean;
137
+ proceed?: () => void;
138
+ reset?: () => void;
123
139
  };
124
140
  /**
125
141
  * Custom additional views created by the developer, added to the main
@@ -162,6 +162,7 @@ export interface PluginFormActionProps<USER extends User = User, EC extends Enti
162
162
  formContext?: FormContext<any>;
163
163
  context: FireCMSContext<USER>;
164
164
  currentEntityId?: string;
165
+ layout: "side_panel" | "full_screen";
165
166
  }
166
167
  export type PluginFieldBuilderParams<T extends CMSType = CMSType, M extends Record<string, any> = any, EC extends EntityCollection<M> = EntityCollection<M>> = {
167
168
  fieldConfigId: string;
@@ -22,7 +22,7 @@ export interface EntitySidePanelProps<M extends Record<string, any> = any> {
22
22
  * Open the entity with a selected sub-collection view. If the panel for this
23
23
  * entity was already open, it is replaced.
24
24
  */
25
- selectedSubPath?: string;
25
+ selectedTab?: string;
26
26
  /**
27
27
  * Use this prop to override the width of the form view.
28
28
  * e.g. "600px"
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Saves data to the in-memory cache and persists it individually in `localStorage`.
3
+ * @param path - The unique path/key for the data.
4
+ * @param data - The data to cache and persist.
5
+ */
6
+ export declare function saveEntityToCache(path: string, data: object): void;
7
+ /**
8
+ * Retrieves an entity from the in-memory cache or `localStorage`.
9
+ * If the entity is not in the cache but exists in `localStorage`, it loads it into the cache.
10
+ * @param path - The unique path/key for the entity.
11
+ * @returns The cached entity or `undefined` if not found.
12
+ */
13
+ export declare function getEntityFromCache(path: string): object | undefined;
14
+ export declare function hasEntityInCache(path: string): boolean;
15
+ /**
16
+ * Removes an entity from both the in-memory cache and `localStorage`.
17
+ * @param path - The unique path/key for the entity to remove.
18
+ */
19
+ export declare function removeEntityFromCache(path: string): void;
20
+ /**
21
+ * Clears the entire in-memory cache and removes all related entities from `localStorage`.
22
+ */
23
+ export declare function clearEntityCache(): void;
@@ -4,19 +4,23 @@ export interface NavigationViewEntityInternal<M extends Record<string, any>> {
4
4
  type: "entity";
5
5
  entityId: string;
6
6
  path: string;
7
+ fullPath: string;
7
8
  parentCollection: EntityCollection<M>;
8
9
  }
9
10
  export interface NavigationViewCollectionInternal<M extends Record<string, any>> {
10
11
  type: "collection";
12
+ id: string;
11
13
  path: string;
14
+ fullPath: string;
12
15
  collection: EntityCollection<M>;
13
16
  }
14
17
  export interface NavigationViewEntityCustomInternal<M extends Record<string, any>> {
15
18
  type: "custom_view";
16
19
  path: string;
20
+ fullPath: string;
17
21
  view: EntityCustomView<M>;
18
22
  }
19
- export declare function getNavigationEntriesFromPathInternal(props: {
23
+ export declare function getNavigationEntriesFromPath(props: {
20
24
  path: string;
21
25
  collections: EntityCollection[] | undefined;
22
26
  currentFullPath?: string;
@@ -1,4 +1,4 @@
1
- import { EntityCollection } from "../types";
1
+ import { EntityCollection, NavigationController, SideEntityController } from "../types";
2
2
  export declare function removeInitialAndTrailingSlashes(s: string): string;
3
3
  export declare function removeInitialSlash(s: string): string;
4
4
  export declare function removeTrailingSlash(s: string): string;
@@ -18,3 +18,14 @@ export declare function getCollectionByPathOrId(pathOrId: string, collections: E
18
18
  * @param subpaths
19
19
  */
20
20
  export declare function getCollectionPathsCombinations(subpaths: string[]): string[];
21
+ export declare function navigateToEntity({ openEntityMode, collection, entityId, copy, path, selectedTab, sideEntityController, onClose, navigation }: {
22
+ openEntityMode: "side_panel" | "full_screen";
23
+ collection?: EntityCollection;
24
+ entityId?: string;
25
+ selectedTab?: string;
26
+ copy?: boolean;
27
+ path: string;
28
+ sideEntityController: SideEntityController;
29
+ onClose?: () => void;
30
+ navigation: NavigationController;
31
+ }): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firecms/core",
3
3
  "type": "module",
4
- "version": "3.0.0-canary.168",
4
+ "version": "3.0.0-canary.169",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/firecmsco"
@@ -50,9 +50,9 @@
50
50
  "./package.json": "./package.json"
51
51
  },
52
52
  "dependencies": {
53
- "@firecms/editor": "^3.0.0-canary.168",
54
- "@firecms/formex": "^3.0.0-canary.168",
55
- "@firecms/ui": "^3.0.0-canary.168",
53
+ "@firecms/editor": "^3.0.0-canary.169",
54
+ "@firecms/formex": "^3.0.0-canary.169",
55
+ "@firecms/ui": "^3.0.0-canary.169",
56
56
  "@hello-pangea/dnd": "^17.0.0",
57
57
  "@radix-ui/react-portal": "^1.1.2",
58
58
  "clsx": "^2.1.1",
@@ -106,7 +106,7 @@
106
106
  "dist",
107
107
  "src"
108
108
  ],
109
- "gitHead": "81a6eef43477c31b82713fc044e4836838ed74bb",
109
+ "gitHead": "8c086883fc1cb08a74d4bd66ef2f952affa9fdb9",
110
110
  "publishConfig": {
111
111
  "access": "public"
112
112
  },
@@ -13,7 +13,7 @@ export function CircularProgressCenter({ text, ...props }: CircularProgressProps
13
13
  }) {
14
14
  return (
15
15
  <div
16
- className="flex w-full h-screen max-h-full max-w-full bg-surface-50 dark:bg-surface-900 gap-4">
16
+ className="flex w-full h-screen max-h-full max-w-full gap-4">
17
17
  <div className="m-auto flex flex-col gap-2 items-center">
18
18
  <CircularProgress {...props}/>
19
19
  {text && <Typography
@@ -181,7 +181,7 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
181
181
  </DialogContent>
182
182
  <DialogActions>
183
183
 
184
- {loading && <CircularProgress size={"small"}/>}
184
+ {loading && <CircularProgress size={"smallest"}/>}
185
185
 
186
186
  <Button onClick={handleCancel}
187
187
  disabled={loading}
@@ -1,8 +1,20 @@
1
1
  import React, { MouseEvent, useCallback } from "react";
2
2
 
3
3
  import { CollectionSize, Entity, EntityAction, EntityCollection, SelectionController } from "../../types";
4
- import { Checkbox, cls, IconButton, Menu, MenuItem, MoreVertIcon, Skeleton, Tooltip } from "@firecms/ui";
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";
5
16
  import { useFireCMSContext, useLargeLayout } from "../../hooks";
17
+ import { hasEntityInCache } from "../../util/entity_cache";
6
18
 
7
19
  /**
8
20
  *
@@ -32,23 +44,25 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
32
44
  actions = [],
33
45
  hideId,
34
46
  selectionController,
47
+ openEntityMode
35
48
  }:
36
- {
37
- entity: Entity<any>,
38
- collection?: EntityCollection<any>,
39
- fullPath?: string,
40
- width: number,
41
- frozen?: boolean,
42
- size: CollectionSize,
43
- isSelected?: boolean,
44
- selectionEnabled?: boolean,
45
- actions?: EntityAction[],
46
- hideId?: boolean,
47
- onCollectionChange?: () => void,
48
- selectionController?: SelectionController;
49
- highlightEntity?: (entity: Entity<any>) => void;
50
- unhighlightEntity?: (entity: Entity<any>) => void;
51
- }) {
49
+ {
50
+ entity: Entity<any>,
51
+ collection?: EntityCollection<any>,
52
+ fullPath?: string,
53
+ width: number,
54
+ frozen?: boolean,
55
+ size: CollectionSize,
56
+ isSelected?: boolean,
57
+ selectionEnabled?: boolean,
58
+ actions?: EntityAction[],
59
+ hideId?: boolean,
60
+ onCollectionChange?: () => void,
61
+ selectionController?: SelectionController;
62
+ highlightEntity?: (entity: Entity<any>) => void;
63
+ unhighlightEntity?: (entity: Entity<any>) => void;
64
+ openEntityMode: "side_panel" | "full_screen";
65
+ }) {
52
66
 
53
67
  const largeLayout = useLargeLayout();
54
68
 
@@ -63,6 +77,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
63
77
 
64
78
  const collapsedActions = actions.filter(a => a.collapsed || a.collapsed === undefined);
65
79
  const uncollapsedActions = actions.filter(a => a.collapsed === false);
80
+ const hasDraft = hasEntityInCache(fullPath + "/" + entity.id);
66
81
  return (
67
82
  <div
68
83
  className={cls(
@@ -98,6 +113,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
98
113
  highlightEntity,
99
114
  unhighlightEntity,
100
115
  onCollectionChange,
116
+ openEntityMode: openEntityMode ?? collection?.openEntityMode
101
117
  });
102
118
  }}
103
119
  size={largeLayout ? "medium" : "small"}>
@@ -126,6 +142,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
126
142
  highlightEntity,
127
143
  unhighlightEntity,
128
144
  onCollectionChange,
145
+ openEntityMode: openEntityMode ?? collection?.openEntityMode
129
146
  });
130
147
  }}>
131
148
  {action.icon}
@@ -148,11 +165,15 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
148
165
 
149
166
  {!hideId && size !== "xs" && (
150
167
  <div
151
- className="w-[138px] text-center overflow-hidden truncate font-mono text-xs text-text-secondary dark:text-text-secondary-dark max-w-full text-ellipsis px-2"
168
+ className="w-[138px] overflow-hidden truncate font-mono text-xs text-text-secondary dark:text-text-secondary-dark max-w-full text-ellipsis px-2 align-center flex items-center justify-center gap-1"
152
169
  onClick={(event) => {
153
170
  event.stopPropagation();
154
171
  }}>
155
-
172
+ {hasDraft && <Tooltip title={"Unsaved changes"} className={"inline"}>
173
+ <Chip colorScheme={"orangeDarker"} className={"p-0.5"}>
174
+ <EditIcon size={12}/>
175
+ </Chip>
176
+ </Tooltip>}
156
177
  {entity
157
178
  ? entity.id
158
179
  : <Skeleton/>
@@ -34,14 +34,14 @@ import { getRowHeight } from "../common/table_height";
34
34
  *
35
35
  * The data displayed in the table is managed by a {@link EntityTableController}.
36
36
  * You can build the default, bound to a path in the datasource, by using the hook
37
- * {@link useDataSourceEntityCollectionTableController}
37
+ * {@link useDataSourceTableController}
38
38
  *
39
39
  * @see EntityCollectionTableProps
40
40
  * @see EntityCollectionView
41
41
  * @see VirtualTable
42
42
  * @group Components
43
43
  */
44
- export const EntityCollectionTable = function EntityCollectionTable<M extends Record<string, any>, USER extends User>
44
+ export const EntityCollectionTable = function EntityCollectionTable<M extends Record<string, any> = any, USER extends User = any>
45
45
  ({
46
46
  className,
47
47
  style,
@@ -57,6 +57,8 @@ export const EntityCollectionTable = function EntityCollectionTable<M extends Re
57
57
  highlightedEntities,
58
58
  onEntityClick,
59
59
  onColumnResize,
60
+ initialScroll,
61
+ onScroll,
60
62
  onSizeChanged,
61
63
  textSearchEnabled = false,
62
64
  hoverRow = true,
@@ -76,7 +78,8 @@ export const EntityCollectionTable = function EntityCollectionTable<M extends Re
76
78
  getIdColumnWidth,
77
79
  onTextSearchClick,
78
80
  textSearchLoading,
79
- enablePopupIcon
81
+ enablePopupIcon,
82
+ openEntityMode = "side_panel"
80
83
  }: EntityCollectionTableProps<M>) {
81
84
 
82
85
  const ref = useRef<HTMLDivElement>(null);
@@ -265,7 +268,8 @@ export const EntityCollectionTable = function EntityCollectionTable<M extends Re
265
268
  width={column.width}
266
269
  frozen={column.frozen}
267
270
  isSelected={false}
268
- size={size}/>;
271
+ size={size}
272
+ openEntityMode={openEntityMode}/>;
269
273
  } else if (additionalFieldsMap[columnKey]) {
270
274
  return additionalCellRenderer(props);
271
275
  } else if (props.columnIndex < columns.length + 1) {
@@ -313,6 +317,8 @@ export const EntityCollectionTable = function EntityCollectionTable<M extends Re
313
317
  highlightedRow={(entity: Entity<M>) => Boolean(selectedEntities?.find(e => e.id === entity.id && e.path === entity.path))}
314
318
  tableController={tableController}
315
319
  onValueChange={onValueChange}
320
+ initialScroll={initialScroll}
321
+ onScroll={onScroll}
316
322
  onColumnResize={onColumnResize}
317
323
  hoverRow={hoverRow}
318
324
  filterable={filterable}
@@ -72,6 +72,21 @@ export type EntityCollectionTableProps<M extends Record<string, any>,
72
72
  */
73
73
  onColumnResize?(params: OnColumnResizeParams): void;
74
74
 
75
+ /**
76
+ * Initial scroll position
77
+ */
78
+ initialScroll?: number;
79
+
80
+ /**
81
+ * Callback when the table is scrolled
82
+ * @param props
83
+ */
84
+ onScroll?: (props: {
85
+ scrollDirection: "forward" | "backward",
86
+ scrollOffset: number,
87
+ scrollUpdateWasRequested: boolean
88
+ }) => void;
89
+
75
90
  /**
76
91
  * Callback when the selected size of the table is changed
77
92
  */
@@ -90,7 +105,7 @@ export type EntityCollectionTableProps<M extends Record<string, any>,
90
105
 
91
106
  /**
92
107
  * Controller holding the logic for the table
93
- * {@link useDataSourceEntityCollectionTableController}
108
+ * {@link useDataSourceTableController}
94
109
  * {@link EntityTableController}
95
110
  */
96
111
  tableController: EntityTableController<M>;
@@ -136,6 +151,8 @@ export type EntityCollectionTableProps<M extends Record<string, any>,
136
151
  textSearchLoading?: boolean;
137
152
 
138
153
  enablePopupIcon: boolean;
154
+
155
+ openEntityMode?: "side_panel" | "full_screen";
139
156
  };
140
157
 
141
158
  export type GetPropertyForProps<M extends Record<string, any>> = {
@@ -8,5 +8,5 @@ export type {
8
8
 
9
9
  export * from "./PropertyTableCell";
10
10
  export * from "./EntityCollectionRowActions";
11
- export * from "../common/useDataSourceEntityCollectionTableController";
11
+ export * from "../common/useDataSourceTableController";
12
12
  export * from "./column_utils";
@@ -56,7 +56,7 @@ export function CollectionTableToolbar({
56
56
  <Select
57
57
  value={size as string}
58
58
  className="w-16 h-10"
59
- size={"medium"}
59
+ size={"small"}
60
60
  onValueChange={(v) => onSizeChanged(v as CollectionSize)}
61
61
  renderValue={(v) => <div className={"font-medium"}>{v.toUpperCase()}</div>}
62
62
  >
@@ -87,9 +87,9 @@ export function CollectionTableToolbar({
87
87
 
88
88
  <div className="flex items-center gap-2">
89
89
 
90
- {largeLayout && <div className="w-[22px]">
90
+ {largeLayout && <div className="w-[22px] mr-4">
91
91
  {loading &&
92
- <CircularProgress size={"small"}/>}
92
+ <CircularProgress size={"smallest"}/>}
93
93
  </div>}
94
94
 
95
95
  {(onTextSearch || onTextSearchClick) &&