@firecms/core 3.0.0-alpha.28 → 3.0.0-alpha.30
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/core/FireCMS.d.ts +2 -2
- package/dist/core/builders.d.ts +2 -2
- package/dist/core/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +4 -2
- package/dist/core/components/EntityCollectionTable/internal/EntityCollectionRowActions.d.ts +15 -11
- package/dist/core/components/EntityCollectionTable/internal/default_entity_actions.d.ts +5 -0
- package/dist/core/components/EntityCollectionTable/types.d.ts +1 -37
- package/dist/core/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/core/contexts/DialogsProvider.d.ts +4 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/internal/useBuildNavigationContext.d.ts +3 -3
- package/dist/core/util/resolutions.d.ts +1 -1
- package/dist/hooks/useDialogsController.d.ts +11 -0
- package/dist/index.es.js +8400 -8230
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +67 -67
- package/dist/index.umd.js.map +1 -1
- package/dist/styles.d.ts +1 -1
- package/dist/types/collections.d.ts +36 -13
- package/dist/types/dialogs_controller.d.ts +30 -0
- package/dist/types/entity_actions.d.ts +33 -0
- package/dist/types/firecms.d.ts +5 -5
- package/dist/types/firecms_context.d.ts +7 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/navigation.d.ts +3 -3
- package/dist/types/plugins.d.ts +11 -10
- package/package.json +3 -3
- package/src/components/Sheet.tsx +1 -0
- package/src/core/FireCMS.tsx +9 -6
- package/src/core/builders.ts +6 -6
- package/src/core/components/ArrayContainer.tsx +1 -1
- package/src/core/components/EntityCollectionTable/EntityCollectionTable.tsx +16 -9
- package/src/core/components/EntityCollectionTable/EntityCollectionTableProps.tsx +5 -2
- package/src/core/components/EntityCollectionTable/internal/EntityCollectionRowActions.tsx +96 -79
- package/src/core/components/EntityCollectionTable/internal/default_entity_actions.tsx +107 -0
- package/src/core/components/EntityCollectionTable/types.tsx +1 -56
- package/src/core/components/EntityCollectionView/EntityCollectionView.tsx +66 -42
- package/src/core/components/ReferenceSelectionInner.tsx +2 -2
- package/src/core/components/VirtualTable/VirtualTable.tsx +4 -6
- package/src/core/components/VirtualTable/VirtualTableProps.tsx +1 -1
- package/src/core/contexts/DialogsProvider.tsx +50 -0
- package/src/core/index.tsx +1 -1
- package/src/core/internal/useBuildNavigationContext.tsx +13 -13
- package/src/core/internal/useBuildSideEntityController.tsx +9 -1
- package/src/form/EntityForm.tsx +63 -6
- package/src/hooks/useDialogsController.tsx +14 -0
- package/src/hooks/useFireCMSContext.tsx +5 -11
- package/src/styles.ts +1 -1
- package/src/types/collections.ts +36 -17
- package/src/types/dialogs_controller.tsx +31 -0
- package/src/types/entity_actions.tsx +36 -0
- package/src/types/firecms.tsx +5 -5
- package/src/types/firecms_context.tsx +8 -0
- package/src/types/index.ts +1 -0
- package/src/types/navigation.ts +3 -3
- package/src/types/plugins.tsx +11 -10
- /package/dist/core/contexts/{SnackbarContext.d.ts → SnackbarProvider.d.ts} +0 -0
- /package/src/core/contexts/{SnackbarContext.tsx → SnackbarProvider.tsx} +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, { PropsWithChildren, useCallback, useRef, useState } from "react";
|
|
2
|
+
import { DialogControllerEntryProps, DialogsController } from "../../types/dialogs_controller";
|
|
3
|
+
|
|
4
|
+
export const DialogsControllerContext = React.createContext<DialogsController>({} as DialogsController);
|
|
5
|
+
|
|
6
|
+
export const DialogsProvider: React.FC<PropsWithChildren<{}>> = ({ children }) => {
|
|
7
|
+
|
|
8
|
+
const [dialogEntries, setDialogEntries] = useState<DialogControllerEntryProps[]>([]);
|
|
9
|
+
const dialogEntriesRef = useRef<DialogControllerEntryProps[]>(dialogEntries);
|
|
10
|
+
|
|
11
|
+
const updateDialogEntries = (newPanels: DialogControllerEntryProps[]) => {
|
|
12
|
+
dialogEntriesRef.current = newPanels;
|
|
13
|
+
setDialogEntries(newPanels);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const close = useCallback(() => {
|
|
17
|
+
|
|
18
|
+
if (dialogEntries.length === 0)
|
|
19
|
+
return;
|
|
20
|
+
|
|
21
|
+
const updatedPanels = [...dialogEntries.slice(0, -1)];
|
|
22
|
+
updateDialogEntries(updatedPanels);
|
|
23
|
+
|
|
24
|
+
}, [dialogEntries]);
|
|
25
|
+
|
|
26
|
+
const open = useCallback((dialogEntry: DialogControllerEntryProps) => {
|
|
27
|
+
|
|
28
|
+
const updatedPanels = [...dialogEntries, dialogEntry];
|
|
29
|
+
updateDialogEntries(updatedPanels);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
closeDialog: () => {
|
|
33
|
+
const updatedPanels = dialogEntriesRef.current.filter(e => e.key !== dialogEntry.key);
|
|
34
|
+
updateDialogEntries(updatedPanels);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}, [dialogEntries]);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<DialogsControllerContext.Provider value={{
|
|
41
|
+
open,
|
|
42
|
+
close
|
|
43
|
+
}}>
|
|
44
|
+
{children}
|
|
45
|
+
{dialogEntries.map((entry, i) => <entry.Component
|
|
46
|
+
key={`dialog_${i}`}
|
|
47
|
+
open={true}/>)}
|
|
48
|
+
</DialogsControllerContext.Provider>
|
|
49
|
+
);
|
|
50
|
+
};
|
package/src/core/index.tsx
CHANGED
|
@@ -20,7 +20,7 @@ export * from "./useBuildModeController";
|
|
|
20
20
|
export * from "../components/util/useOutsideAlerter";
|
|
21
21
|
export * from "./useBuildLocalConfigurationPersistence";
|
|
22
22
|
|
|
23
|
-
export { SnackbarProvider } from "./contexts/
|
|
23
|
+
export { SnackbarProvider } from "./contexts/SnackbarProvider";
|
|
24
24
|
export { ModeControllerProvider } from "./contexts/ModeController";
|
|
25
25
|
|
|
26
26
|
export * from "./contexts/AuthControllerContext";
|
|
@@ -25,27 +25,27 @@ import {
|
|
|
25
25
|
} from "../util";
|
|
26
26
|
import { getParentReferencesFromPath } from "../util/parent_references_from_path";
|
|
27
27
|
|
|
28
|
-
type BuildNavigationContextProps<UserType extends User> = {
|
|
28
|
+
type BuildNavigationContextProps<EC extends EntityCollection, UserType extends User> = {
|
|
29
29
|
basePath: string,
|
|
30
30
|
baseCollectionPath: string,
|
|
31
31
|
authController: AuthController<UserType>;
|
|
32
|
-
collections?:
|
|
32
|
+
collections?: EC[] | EntityCollectionsBuilder<EC>;
|
|
33
33
|
views?: CMSView[] | CMSViewsBuilder;
|
|
34
34
|
userConfigPersistence?: UserConfigurationPersistence;
|
|
35
35
|
plugins?: FireCMSPlugin[];
|
|
36
36
|
dataSource: DataSource;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
export function useBuildNavigationContext<UserType extends User>({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
export function useBuildNavigationContext<EC extends EntityCollection, UserType extends User>({
|
|
40
|
+
basePath,
|
|
41
|
+
baseCollectionPath,
|
|
42
|
+
authController,
|
|
43
|
+
collections: baseCollections,
|
|
44
|
+
views: baseViews,
|
|
45
|
+
userConfigPersistence,
|
|
46
|
+
plugins,
|
|
47
|
+
dataSource
|
|
48
|
+
}: BuildNavigationContextProps<EC, UserType>): NavigationContext {
|
|
49
49
|
|
|
50
50
|
const location = useLocation();
|
|
51
51
|
|
|
@@ -288,7 +288,7 @@ function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[],
|
|
|
288
288
|
});
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
async function resolveCollections(collections: undefined | EntityCollection[] | EntityCollectionsBuilder
|
|
291
|
+
async function resolveCollections(collections: undefined | EntityCollection[] | EntityCollectionsBuilder<any>, authController: AuthController, dataSource: DataSource, plugins?: FireCMSPlugin[]) {
|
|
292
292
|
let resolvedCollections: EntityCollection[] = [];
|
|
293
293
|
if (typeof collections === "function") {
|
|
294
294
|
resolvedCollections = await collections({
|
|
@@ -38,7 +38,15 @@ export const useBuildSideEntityController = (navigation: NavigationContext,
|
|
|
38
38
|
const newFlag = location.hash === `#${NEW_URL_HASH}`;
|
|
39
39
|
const entityOrCollectionPath = navigation.urlPathToDataPath(location.pathname);
|
|
40
40
|
const panelsFromUrl = buildSidePanelsFromUrl(entityOrCollectionPath, navigation.collections, newFlag);
|
|
41
|
-
|
|
41
|
+
for (let i = 0; i < panelsFromUrl.length; i++) {
|
|
42
|
+
const panel = panelsFromUrl[i];
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
if (i === 0)
|
|
45
|
+
sideDialogsController.replace(propsToSidePanel(panel, navigation, smallLayout));
|
|
46
|
+
else
|
|
47
|
+
sideDialogsController.open(propsToSidePanel(panel, navigation, smallLayout))
|
|
48
|
+
}, 1);
|
|
49
|
+
}
|
|
42
50
|
}
|
|
43
51
|
initialised.current = true;
|
|
44
52
|
}
|
package/src/form/EntityForm.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import React, { MutableRefObject, useCallback, useEffect, useMemo, useRef, useSt
|
|
|
3
3
|
import {
|
|
4
4
|
CMSAnalyticsEvent,
|
|
5
5
|
Entity,
|
|
6
|
+
EntityAction,
|
|
6
7
|
EntityCollection,
|
|
7
8
|
EntityStatus,
|
|
8
9
|
EntityValues,
|
|
@@ -15,13 +16,26 @@ import { Form, Formik, FormikHelpers, FormikProps } from "formik";
|
|
|
15
16
|
import { PropertyFieldBinding } from "./PropertyFieldBinding";
|
|
16
17
|
import { CustomFieldValidator, getYupEntitySchema } from "./validation";
|
|
17
18
|
import equal from "react-fast-compare"
|
|
18
|
-
import {
|
|
19
|
-
|
|
19
|
+
import {
|
|
20
|
+
canCreateEntity,
|
|
21
|
+
canDeleteEntity,
|
|
22
|
+
ErrorBoundary,
|
|
23
|
+
fullPathToCollectionSegments,
|
|
24
|
+
getDefaultValuesFor,
|
|
25
|
+
isHidden,
|
|
26
|
+
isReadOnly,
|
|
27
|
+
resolveCollection
|
|
28
|
+
} from "../core";
|
|
29
|
+
import { useAuthController, useDataSource, useFireCMSContext, useSideEntityController } from "../hooks";
|
|
20
30
|
import { ErrorFocus } from "./components/ErrorFocus";
|
|
21
31
|
import { CustomIdField } from "./components/CustomIdField";
|
|
22
|
-
import { DialogActions, Typography } from "../components";
|
|
32
|
+
import { DialogActions, IconButton, Typography } from "../components";
|
|
23
33
|
import { Button } from "../components/Button";
|
|
24
34
|
import { cn } from "../components/util/cn";
|
|
35
|
+
import {
|
|
36
|
+
copyEntityAction,
|
|
37
|
+
deleteEntityAction
|
|
38
|
+
} from "../core/components/EntityCollectionTable/internal/default_entity_actions";
|
|
25
39
|
|
|
26
40
|
/**
|
|
27
41
|
* @category Components
|
|
@@ -326,6 +340,21 @@ function EntityFormInternal<M extends Record<string, any>>({
|
|
|
326
340
|
: undefined,
|
|
327
341
|
[entityId, collection.properties, uniqueFieldValidator]);
|
|
328
342
|
|
|
343
|
+
const authController = useAuthController();
|
|
344
|
+
|
|
345
|
+
const getActionsForEntity = useCallback(({ entity, customEntityActions }: { entity?: Entity<M>, customEntityActions?: EntityAction[] }): EntityAction[] => {
|
|
346
|
+
const createEnabled = canCreateEntity(collection, authController, fullPathToCollectionSegments(path), null);
|
|
347
|
+
const deleteEnabled = entity ? canDeleteEntity(collection, authController, fullPathToCollectionSegments(path), entity) : true;
|
|
348
|
+
const actions: EntityAction[] = [];
|
|
349
|
+
if (createEnabled)
|
|
350
|
+
actions.push(copyEntityAction);
|
|
351
|
+
if (deleteEnabled)
|
|
352
|
+
actions.push(deleteEntityAction);
|
|
353
|
+
if (customEntityActions)
|
|
354
|
+
actions.push(...customEntityActions);
|
|
355
|
+
return actions;
|
|
356
|
+
}, [authController, collection, path]);
|
|
357
|
+
|
|
329
358
|
return (
|
|
330
359
|
<Formik
|
|
331
360
|
initialValues={baseDataSourceValues as M}
|
|
@@ -417,7 +446,8 @@ function EntityFormInternal<M extends Record<string, any>>({
|
|
|
417
446
|
status={status}
|
|
418
447
|
savingError={savingError}
|
|
419
448
|
closeAfterSaveRef={closeAfterSaveRef}
|
|
420
|
-
autoSave={autoSave}
|
|
449
|
+
autoSave={autoSave}
|
|
450
|
+
entityActions={getActionsForEntity({ entity, customEntityActions: collection.entityActions })}/>}
|
|
421
451
|
|
|
422
452
|
</div>
|
|
423
453
|
</div>
|
|
@@ -436,7 +466,8 @@ function InnerForm<M extends Record<string, any>>(props: FormikProps<M> & {
|
|
|
436
466
|
status: "new" | "existing" | "copy",
|
|
437
467
|
savingError?: Error,
|
|
438
468
|
closeAfterSaveRef: MutableRefObject<boolean>,
|
|
439
|
-
autoSave?: boolean
|
|
469
|
+
autoSave?: boolean,
|
|
470
|
+
entityActions: EntityAction[]
|
|
440
471
|
}) {
|
|
441
472
|
|
|
442
473
|
const {
|
|
@@ -455,9 +486,14 @@ function InnerForm<M extends Record<string, any>>(props: FormikProps<M> & {
|
|
|
455
486
|
savingError,
|
|
456
487
|
dirty,
|
|
457
488
|
closeAfterSaveRef,
|
|
458
|
-
autoSave
|
|
489
|
+
autoSave,
|
|
490
|
+
entityActions
|
|
459
491
|
} = props;
|
|
460
492
|
|
|
493
|
+
const context = useFireCMSContext();
|
|
494
|
+
const formActions = entityActions.filter(a => a.includeInForm === undefined || a.includeInForm);
|
|
495
|
+
const sideEntityController = useSideEntityController();
|
|
496
|
+
|
|
461
497
|
const modified = dirty;
|
|
462
498
|
useEffect(() => {
|
|
463
499
|
if (onModified)
|
|
@@ -553,6 +589,27 @@ function InnerForm<M extends Record<string, any>>(props: FormikProps<M> & {
|
|
|
553
589
|
</Typography>
|
|
554
590
|
</div>}
|
|
555
591
|
|
|
592
|
+
{entity && formActions.length > 0 && <div className="flex-grow flex overflow-auto no-scrollbar">
|
|
593
|
+
{formActions.map(action => (
|
|
594
|
+
<IconButton
|
|
595
|
+
key={action.name}
|
|
596
|
+
color="primary"
|
|
597
|
+
onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
|
598
|
+
event.stopPropagation();
|
|
599
|
+
if (entity)
|
|
600
|
+
action.onClick({
|
|
601
|
+
entity,
|
|
602
|
+
fullPath: resolvedCollection.path,
|
|
603
|
+
collection: resolvedCollection,
|
|
604
|
+
context,
|
|
605
|
+
sideEntityController
|
|
606
|
+
});
|
|
607
|
+
}}>
|
|
608
|
+
{action.icon}
|
|
609
|
+
</IconButton>
|
|
610
|
+
))}
|
|
611
|
+
</div>}
|
|
612
|
+
|
|
556
613
|
<Button
|
|
557
614
|
variant="text"
|
|
558
615
|
disabled={disabled}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { DialogsController } from "../types/dialogs_controller";
|
|
3
|
+
import { DialogsControllerContext } from "../core/contexts/DialogsProvider";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Use this hook to open a dialog imperatively.
|
|
7
|
+
* Alternatively, you can use dialogs declaratively using the `Dialog` component.
|
|
8
|
+
*
|
|
9
|
+
* Consider that in order to use this hook you need to have a parent
|
|
10
|
+
* `FireCMS`
|
|
11
|
+
*
|
|
12
|
+
* @category Hooks and utilities
|
|
13
|
+
*/
|
|
14
|
+
export const useDialogsController = (): DialogsController => useContext(DialogsControllerContext);
|
|
@@ -8,17 +8,9 @@ import { useDataSource } from "./data/useDataSource";
|
|
|
8
8
|
import { useStorageSource } from "./useStorageSource";
|
|
9
9
|
import { useSnackbarController } from "./useSnackbarController";
|
|
10
10
|
import { useUserConfigurationPersistence } from "./useUserConfigurationPersistence";
|
|
11
|
+
import { useDialogsController } from "./useDialogsController";
|
|
11
12
|
|
|
12
|
-
export const FireCMSContextInstance = createContext<Partial<FireCMSContext>>({
|
|
13
|
-
sideDialogsController: {} as any,
|
|
14
|
-
sideEntityController: {} as any,
|
|
15
|
-
navigation: {} as any,
|
|
16
|
-
dataSource: {} as any,
|
|
17
|
-
storageSource: {} as any,
|
|
18
|
-
authController: {} as any,
|
|
19
|
-
snackbarController: {} as any,
|
|
20
|
-
fields: {}
|
|
21
|
-
});
|
|
13
|
+
export const FireCMSContextInstance = createContext<Partial<FireCMSContext>>({});
|
|
22
14
|
|
|
23
15
|
/**
|
|
24
16
|
* Hook to retrieve the {@link FireCMSContext}.
|
|
@@ -40,6 +32,7 @@ export const useFireCMSContext = <UserType extends User = User, AuthControllerTy
|
|
|
40
32
|
const storageSource = useStorageSource();
|
|
41
33
|
const snackbarController = useSnackbarController();
|
|
42
34
|
const userConfigPersistence = useUserConfigurationPersistence();
|
|
35
|
+
const dialogsController = useDialogsController();
|
|
43
36
|
|
|
44
37
|
return {
|
|
45
38
|
...partialContext,
|
|
@@ -50,7 +43,8 @@ export const useFireCMSContext = <UserType extends User = User, AuthControllerTy
|
|
|
50
43
|
dataSource,
|
|
51
44
|
storageSource,
|
|
52
45
|
snackbarController,
|
|
53
|
-
userConfigPersistence
|
|
46
|
+
userConfigPersistence,
|
|
47
|
+
dialogsController
|
|
54
48
|
}
|
|
55
49
|
|
|
56
50
|
};
|
package/src/styles.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const focusedMixin = "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent";
|
|
1
|
+
export const focusedMixin = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent";
|
|
2
2
|
export const focusedInvisibleMixin = "focus:bg-opacity-70 focus:bg-gray-100 focus:dark:bg-gray-800 focus:dark:bg-opacity-60";
|
|
3
3
|
export const focusedClasses = "z-30 outline-none ring-2 ring-primary ring-opacity-75 ring-offset-2 ring-offset-transparent ";
|
|
4
4
|
export const fieldBackgroundMixin = "bg-opacity-70 bg-gray-100 dark:bg-gray-800 dark:bg-opacity-60 transition duration-150 ease-in-out";
|
package/src/types/collections.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { EntityCallbacks } from "./entity_callbacks";
|
|
|
6
6
|
import { Permissions, PermissionsBuilder } from "./permissions";
|
|
7
7
|
import { EnumValues, PropertiesOrBuilders } from "./properties";
|
|
8
8
|
import { FormContext } from "./fields";
|
|
9
|
+
import { EntityAction } from "./entity_actions";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* This interface represents a view that includes a collection of entities.
|
|
@@ -14,9 +15,7 @@ import { FormContext } from "./fields";
|
|
|
14
15
|
*
|
|
15
16
|
* @category Models
|
|
16
17
|
*/
|
|
17
|
-
export interface EntityCollection<M extends Record<string, any> = any,
|
|
18
|
-
AdditionalKey extends string = string,
|
|
19
|
-
UserType extends User = User> {
|
|
18
|
+
export interface EntityCollection<M extends Record<string, any> = any, UserType extends User = User> {
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* Name of the collection, typically plural.
|
|
@@ -58,9 +57,9 @@ export interface EntityCollection<M extends Record<string, any> = any,
|
|
|
58
57
|
|
|
59
58
|
/**
|
|
60
59
|
* Icon key to use in this collection.
|
|
61
|
-
* You can use any of the icons in the
|
|
62
|
-
* https://
|
|
63
|
-
* e.g. '
|
|
60
|
+
* You can use any of the icons in the Material specs:
|
|
61
|
+
* https://fonts.google.com/icons
|
|
62
|
+
* e.g. 'account_tree' or 'person'
|
|
64
63
|
*/
|
|
65
64
|
icon?: string;
|
|
66
65
|
|
|
@@ -93,7 +92,7 @@ export interface EntityCollection<M extends Record<string, any> = any,
|
|
|
93
92
|
* - If you are using a collection group, you will also have an
|
|
94
93
|
* additional `collectionGroupParent` column.
|
|
95
94
|
*/
|
|
96
|
-
propertiesOrder?: Extract<keyof M
|
|
95
|
+
propertiesOrder?: Extract<keyof M, string>[];
|
|
97
96
|
|
|
98
97
|
/**
|
|
99
98
|
* If enabled, content is loaded in batches. If `false` all entities in the
|
|
@@ -113,7 +112,7 @@ export interface EntityCollection<M extends Record<string, any> = any,
|
|
|
113
112
|
* Permissions the logged-in user can perform on this collection.
|
|
114
113
|
* If not specified everything defaults to `true`.
|
|
115
114
|
*/
|
|
116
|
-
permissions?: Permissions | PermissionsBuilder<
|
|
115
|
+
permissions?: Permissions | PermissionsBuilder<any, UserType, M>;
|
|
117
116
|
|
|
118
117
|
/**
|
|
119
118
|
* Are the entities in this collection selectable. Defaults to `true`
|
|
@@ -140,6 +139,31 @@ export interface EntityCollection<M extends Record<string, any> = any,
|
|
|
140
139
|
*/
|
|
141
140
|
Actions?: React.ComponentType<CollectionActionsProps> | React.ComponentType<CollectionActionsProps>[];
|
|
142
141
|
|
|
142
|
+
/**
|
|
143
|
+
* You can define additional actions that can be performed on the entities
|
|
144
|
+
* in this collection. These actions can be displayed in the collection
|
|
145
|
+
* view or in the entity view.
|
|
146
|
+
*
|
|
147
|
+
* You can use the `onClick` method to implement your own logic.
|
|
148
|
+
* In the `context` prop you can access all the controllers of FireCMS.
|
|
149
|
+
*
|
|
150
|
+
* ```
|
|
151
|
+
* const archiveEntityAction: EntityAction = {
|
|
152
|
+
* icon: <ArchiveIcon/>,
|
|
153
|
+
* name: "Archive",
|
|
154
|
+
* onClick({
|
|
155
|
+
* entity,
|
|
156
|
+
* collection,
|
|
157
|
+
* context,
|
|
158
|
+
* }): Promise<void> {
|
|
159
|
+
* // Add your code here
|
|
160
|
+
* return Promise.resolve(undefined);
|
|
161
|
+
* }
|
|
162
|
+
* }
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
entityActions?: EntityAction<M, UserType>[];
|
|
166
|
+
|
|
143
167
|
/**
|
|
144
168
|
* Pass your own selection controller if you want to control selected
|
|
145
169
|
* entities externally.
|
|
@@ -190,8 +214,7 @@ export interface EntityCollection<M extends Record<string, any> = any,
|
|
|
190
214
|
* You can add additional fields to the collection view by implementing
|
|
191
215
|
* an additional field delegate.
|
|
192
216
|
*/
|
|
193
|
-
additionalFields?: AdditionalFieldDelegate<M,
|
|
194
|
-
|
|
217
|
+
additionalFields?: AdditionalFieldDelegate<M, UserType>[];
|
|
195
218
|
|
|
196
219
|
/**
|
|
197
220
|
* Default size of the rendered collection
|
|
@@ -245,10 +268,8 @@ export interface EntityCollection<M extends Record<string, any> = any,
|
|
|
245
268
|
|
|
246
269
|
/**
|
|
247
270
|
* Parameter passed to the `Actions` prop in the collection configuration.
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* If you don't want to render the actions in the home page card, you can
|
|
251
|
-
* return `null` if mode is `home`.
|
|
271
|
+
* The component will receive this prop when it is rendered in the collection
|
|
272
|
+
* toolbar.
|
|
252
273
|
*
|
|
253
274
|
* @category Models
|
|
254
275
|
*/
|
|
@@ -349,7 +370,6 @@ export type FilterCombination<Key extends string> = Partial<Record<Key, "asc" |
|
|
|
349
370
|
*/
|
|
350
371
|
export type CollectionSize = "xs" | "s" | "m" | "l" | "xl";
|
|
351
372
|
|
|
352
|
-
|
|
353
373
|
export type AdditionalFieldDelegateProps<M extends Record<string, any> = any, UserType extends User = User> = {
|
|
354
374
|
entity: Entity<M>,
|
|
355
375
|
context: FireCMSContext<UserType>
|
|
@@ -361,14 +381,13 @@ export type AdditionalFieldDelegateProps<M extends Record<string, any> = any, Us
|
|
|
361
381
|
* @category Models
|
|
362
382
|
*/
|
|
363
383
|
export interface AdditionalFieldDelegate<M extends Record<string, any> = any,
|
|
364
|
-
AdditionalKey extends string = string,
|
|
365
384
|
UserType extends User = User> {
|
|
366
385
|
|
|
367
386
|
/**
|
|
368
387
|
* ID of this column. You can use this id in the `properties` field of the
|
|
369
388
|
* collection in any order you want
|
|
370
389
|
*/
|
|
371
|
-
id:
|
|
390
|
+
id: string;
|
|
372
391
|
|
|
373
392
|
/**
|
|
374
393
|
* Header of this column
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controller to open the side dialog
|
|
3
|
+
* @category Hooks and utilities
|
|
4
|
+
*/
|
|
5
|
+
export interface DialogsController {
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Close the last dialog
|
|
9
|
+
*/
|
|
10
|
+
close: () => void;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Open a dialog
|
|
14
|
+
* @param props
|
|
15
|
+
*/
|
|
16
|
+
open: (props: DialogControllerEntryProps) => { closeDialog: () => void };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Props used to open a side dialog
|
|
21
|
+
* @category Hooks and utilities
|
|
22
|
+
*/
|
|
23
|
+
export interface DialogControllerEntryProps {
|
|
24
|
+
|
|
25
|
+
key: string;
|
|
26
|
+
/**
|
|
27
|
+
* The component type that will be rendered
|
|
28
|
+
*/
|
|
29
|
+
Component: React.ComponentType<{ open: boolean }>;
|
|
30
|
+
|
|
31
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { FireCMSContext } from "./firecms_context";
|
|
3
|
+
import { Entity } from "./entities";
|
|
4
|
+
import { EntityCollection, SelectionController } from "./collections";
|
|
5
|
+
import { User } from "./user";
|
|
6
|
+
import { SideEntityController } from "./side_entity_controller";
|
|
7
|
+
|
|
8
|
+
export type EntityAction<M extends object = any, UserType extends User = User> = {
|
|
9
|
+
name: string;
|
|
10
|
+
icon?: React.ReactElement;
|
|
11
|
+
onClick: (props: EntityActionClickProps<M, UserType>) => Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Show this action in the menu, defaults to true
|
|
14
|
+
*/
|
|
15
|
+
collapsed?: boolean;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Show this action in the form, defaults to true
|
|
19
|
+
*/
|
|
20
|
+
includeInForm?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type EntityActionClickProps<M extends object, UserType extends User = User> = {
|
|
24
|
+
entity: Entity<M>;
|
|
25
|
+
context: FireCMSContext<UserType>;
|
|
26
|
+
fullPath?: string;
|
|
27
|
+
collection?: EntityCollection<M>;
|
|
28
|
+
selectionController?: SelectionController;
|
|
29
|
+
highlightEntity?: (entity: Entity<any>) => void;
|
|
30
|
+
unhighlightEntity?: (entity: Entity<any>) => void;
|
|
31
|
+
onCollectionChange?: () => void;
|
|
32
|
+
/**
|
|
33
|
+
* If this actions is being called from a side dialog
|
|
34
|
+
*/
|
|
35
|
+
sideEntityController?: SideEntityController;
|
|
36
|
+
};
|
package/src/types/firecms.tsx
CHANGED
|
@@ -23,11 +23,11 @@ import { CMSAnalyticsEvent } from "./analytics";
|
|
|
23
23
|
* APIs directly, or a REST API.
|
|
24
24
|
* @category Models
|
|
25
25
|
*/
|
|
26
|
-
export type EntityCollectionsBuilder = (params: {
|
|
26
|
+
export type EntityCollectionsBuilder<EC extends EntityCollection> = (params: {
|
|
27
27
|
user: User | null,
|
|
28
28
|
authController: AuthController,
|
|
29
29
|
dataSource: DataSource
|
|
30
|
-
}) =>
|
|
30
|
+
}) => EC[] | Promise<EC[]>;
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Use this callback to build custom views dynamically.
|
|
@@ -47,7 +47,7 @@ export type CMSViewsBuilder = (params: {
|
|
|
47
47
|
/**
|
|
48
48
|
* @category Models
|
|
49
49
|
*/
|
|
50
|
-
export type FireCMSProps<UserType extends User> = {
|
|
50
|
+
export type FireCMSProps<UserType extends User, EC extends EntityCollection> = {
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Use this function to return the components you want to render under
|
|
@@ -73,7 +73,7 @@ export type FireCMSProps<UserType extends User> = {
|
|
|
73
73
|
* Each of the navigation entries in this field
|
|
74
74
|
* generates an entry in the main menu.
|
|
75
75
|
*/
|
|
76
|
-
collections?:
|
|
76
|
+
collections?: EC[] | EntityCollectionsBuilder<EC>;
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* Custom additional views created by the developer, added to the main
|
|
@@ -148,7 +148,7 @@ export type FireCMSProps<UserType extends User> = {
|
|
|
148
148
|
* Use plugins to modify the behaviour of the CMS.
|
|
149
149
|
* Currently, in ALPHA, and likely subject to change.
|
|
150
150
|
*/
|
|
151
|
-
plugins?: FireCMSPlugin[];
|
|
151
|
+
plugins?: FireCMSPlugin<any, any, any>[];
|
|
152
152
|
|
|
153
153
|
/**
|
|
154
154
|
* Callback used to get analytics events from the CMS
|
|
@@ -13,6 +13,7 @@ import { FireCMSPlugin } from "./plugins";
|
|
|
13
13
|
import { CMSAnalyticsEvent } from "./analytics";
|
|
14
14
|
import { PropertyConfig } from "./property_config";
|
|
15
15
|
import { EntityCustomView } from "./collections";
|
|
16
|
+
import { DialogsController } from "./dialogs_controller";
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Context that includes the internal controllers and contexts used by the app.
|
|
@@ -59,9 +60,16 @@ export type FireCMSContext<UserType extends User = User, AuthControllerType exte
|
|
|
59
60
|
|
|
60
61
|
/**
|
|
61
62
|
* Controller used to open side dialogs
|
|
63
|
+
* This is the controller used internally by side entity dialogs
|
|
64
|
+
* or reference dialogs.
|
|
62
65
|
*/
|
|
63
66
|
sideDialogsController: SideDialogsController;
|
|
64
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Controller used to open regular dialogs
|
|
70
|
+
*/
|
|
71
|
+
dialogsController: DialogsController;
|
|
72
|
+
|
|
65
73
|
/**
|
|
66
74
|
* Used auth controller
|
|
67
75
|
*/
|
package/src/types/index.ts
CHANGED
package/src/types/navigation.ts
CHANGED
|
@@ -154,9 +154,9 @@ export interface CMSView {
|
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
156
|
* Icon key to use in this view.
|
|
157
|
-
* You can use any of the icons in the
|
|
158
|
-
* https://
|
|
159
|
-
* e.g. '
|
|
157
|
+
* You can use any of the icons in the Material specs:
|
|
158
|
+
* https://fonts.google.com/icons
|
|
159
|
+
* e.g. 'account_tree' or 'person'
|
|
160
160
|
*/
|
|
161
161
|
icon?: string;
|
|
162
162
|
|