@firecms/collection_editor 3.0.0-canary.26 → 3.0.0-canary.261

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 (88) hide show
  1. package/LICENSE +114 -21
  2. package/README.md +165 -1
  3. package/dist/ConfigControllerProvider.d.ts +1 -2
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.es.js +10742 -4787
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +11412 -3
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/types/collection_editor_controller.d.ts +3 -2
  10. package/dist/types/collection_inference.d.ts +4 -1
  11. package/dist/types/config_controller.d.ts +3 -1
  12. package/dist/types/config_permissions.d.ts +2 -2
  13. package/dist/types/persisted_collection.d.ts +1 -1
  14. package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
  15. package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
  16. package/dist/ui/EditorEntityAction.d.ts +2 -0
  17. package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
  18. package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +3 -1
  19. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +3 -2
  20. package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +2 -2
  21. package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +2 -2
  22. package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +4 -0
  23. package/dist/ui/collection_editor/EntityActionsSelectDialog.d.ts +4 -0
  24. package/dist/ui/collection_editor/LayoutModeSwitch.d.ts +5 -0
  25. package/dist/ui/collection_editor/PropertyEditView.d.ts +8 -0
  26. package/dist/ui/collection_editor/PropertyTree.d.ts +11 -12
  27. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
  28. package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +7 -0
  29. package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
  30. package/dist/ui/collection_editor/properties/ReferencePropertyField.d.ts +2 -1
  31. package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
  32. package/dist/useCollectionEditorPlugin.d.ts +8 -11
  33. package/dist/utils/collections.d.ts +6 -0
  34. package/package.json +23 -35
  35. package/src/ConfigControllerProvider.tsx +64 -66
  36. package/src/index.ts +1 -0
  37. package/src/types/collection_editor_controller.tsx +6 -5
  38. package/src/types/collection_inference.ts +4 -1
  39. package/src/types/config_controller.tsx +4 -1
  40. package/src/types/config_permissions.ts +1 -1
  41. package/src/types/persisted_collection.ts +2 -3
  42. package/src/ui/CollectionViewHeaderAction.tsx +10 -5
  43. package/src/ui/EditorCollectionAction.tsx +12 -70
  44. package/src/ui/EditorCollectionActionStart.tsx +87 -0
  45. package/src/ui/EditorEntityAction.tsx +51 -0
  46. package/src/ui/HomePageEditorCollectionAction.tsx +21 -14
  47. package/src/ui/NewCollectionButton.tsx +1 -1
  48. package/src/ui/NewCollectionCard.tsx +3 -3
  49. package/src/ui/PropertyAddColumnComponent.tsx +11 -6
  50. package/src/ui/collection_editor/CollectionDetailsForm.tsx +157 -50
  51. package/src/ui/collection_editor/CollectionEditorDialog.tsx +117 -37
  52. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +23 -32
  53. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +46 -41
  54. package/src/ui/collection_editor/EntityActionsEditTab.tsx +163 -0
  55. package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +41 -0
  56. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +11 -7
  57. package/src/ui/collection_editor/EnumForm.tsx +11 -7
  58. package/src/ui/collection_editor/GetCodeDialog.tsx +60 -28
  59. package/src/ui/collection_editor/LayoutModeSwitch.tsx +54 -0
  60. package/src/ui/collection_editor/PropertyEditView.tsx +264 -78
  61. package/src/ui/collection_editor/PropertyFieldPreview.tsx +8 -10
  62. package/src/ui/collection_editor/PropertyTree.tsx +184 -138
  63. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +26 -19
  64. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +9 -7
  65. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +33 -9
  66. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +42 -9
  67. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +32 -20
  68. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +54 -47
  69. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -1
  70. package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -7
  71. package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
  72. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +7 -3
  73. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
  74. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +34 -19
  75. package/src/ui/collection_editor/properties/StringPropertyField.tsx +1 -10
  76. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +1 -0
  77. package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +2 -0
  78. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +2 -2
  79. package/src/ui/collection_editor/templates/pages_template.ts +1 -6
  80. package/src/ui/collection_editor/utils/strings.ts +13 -6
  81. package/src/ui/collection_editor/utils/supported_fields.tsx +1 -0
  82. package/src/ui/collection_editor/utils/update_property_for_widget.ts +9 -0
  83. package/src/useCollectionEditorPlugin.tsx +38 -32
  84. package/src/utils/collections.ts +46 -0
  85. package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
  86. package/dist/ui/collection_editor/PropertySelectItem.d.ts +0 -8
  87. package/src/ui/RootCollectionSuggestions.tsx +0 -63
  88. package/src/ui/collection_editor/PropertySelectItem.tsx +0 -32
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useMemo, useState } from "react";
1
+ import React, { useEffect, useMemo, useState } from "react";
2
2
 
3
3
  import { Field, getIn, useFormex } from "@firecms/formex";
4
4
  import {
@@ -16,15 +16,14 @@ import {
16
16
  } from "@firecms/core";
17
17
  import {
18
18
  AddIcon,
19
- AutoAwesomeIcon,
19
+ AutorenewIcon,
20
20
  Button,
21
21
  CircularProgress,
22
- cn,
22
+ cls,
23
23
  CodeIcon,
24
24
  DebouncedTextField,
25
25
  defaultBorderMixin,
26
26
  IconButton,
27
- Paper,
28
27
  Tooltip,
29
28
  Typography,
30
29
  } from "@firecms/ui";
@@ -43,9 +42,9 @@ type CollectionEditorFormProps = {
43
42
  setDirty?: (dirty: boolean) => void;
44
43
  reservedGroups?: string[];
45
44
  extraIcon: React.ReactNode;
46
- getUser: (uid: string) => User | null;
45
+ getUser?: (uid: string) => User | null;
47
46
  getData?: () => Promise<object[]>;
48
- doCollectionInference: (collection: PersistedCollection) => Promise<Partial<EntityCollection> | null> | undefined;
47
+ doCollectionInference?: (collection: PersistedCollection) => Promise<Partial<EntityCollection> | null> | undefined;
49
48
  propertyConfigs: Record<string, PropertyConfig>;
50
49
  collectionEditable: boolean;
51
50
  };
@@ -108,6 +107,8 @@ export function CollectionPropertiesEditorForm({
108
107
  return;
109
108
 
110
109
  setInferringProperties(true);
110
+
111
+ console.debug("CollectionEditor: inferring properties from data", doCollectionInference, values);
111
112
  // @ts-ignore
112
113
  doCollectionInference(values)
113
114
  .then((newCollection) => {
@@ -158,20 +159,20 @@ export function CollectionPropertiesEditorForm({
158
159
  }
159
160
  : undefined;
160
161
 
161
- const getCurrentPropertiesOrder = useCallback((namespace?: string) => {
162
- if (!namespace) return currentPropertiesOrderRef.current[""];
162
+ const getCurrentPropertiesOrder = (namespace?: string) => {
163
+ if (!namespace) return currentPropertiesOrderRef.current[""] ?? getIn(values, namespaceToPropertiesOrderPath());
163
164
  return currentPropertiesOrderRef.current[namespace] ?? getIn(values, namespaceToPropertiesOrderPath(namespace));
164
- }, [values]);
165
+ };
165
166
 
166
- const updatePropertiesOrder = useCallback((newPropertiesOrder: string[], namespace?: string) => {
167
+ const updatePropertiesOrder = (newPropertiesOrder: string[], namespace?: string) => {
167
168
  const propertiesOrderPath = namespaceToPropertiesOrderPath(namespace);
168
169
 
169
170
  setFieldValue(propertiesOrderPath, newPropertiesOrder, false);
170
171
  currentPropertiesOrderRef.current[namespace ?? ""] = newPropertiesOrder;
171
172
 
172
- }, [setFieldValue]);
173
+ };
173
174
 
174
- const deleteProperty = useCallback((propertyKey?: string, namespace?: string) => {
175
+ const deleteProperty = (propertyKey?: string, namespace?: string) => {
175
176
  const fullId = propertyKey ? getFullId(propertyKey, namespace) : undefined;
176
177
  if (!fullId)
177
178
  throw Error("collection editor miss config");
@@ -179,15 +180,17 @@ export function CollectionPropertiesEditorForm({
179
180
  setFieldValue(idToPropertiesPath(fullId), undefined, false);
180
181
 
181
182
  const currentPropertiesOrder = getCurrentPropertiesOrder(namespace);
182
- const newPropertiesOrder = currentPropertiesOrder.filter((p) => p !== propertyKey);
183
- updatePropertiesOrder(newPropertiesOrder, namespace);
183
+ if (currentPropertiesOrder) {
184
+ const newPropertiesOrder = currentPropertiesOrder.filter((p) => p !== propertyKey);
185
+ updatePropertiesOrder(newPropertiesOrder, namespace);
186
+ }
184
187
 
185
188
  setNewPropertyDialogOpen(false);
186
189
 
187
190
  setSelectedPropertyIndex(undefined);
188
191
  setSelectedPropertyKey(undefined);
189
192
  setSelectedPropertyNamespace(undefined);
190
- }, [getCurrentPropertiesOrder, setFieldValue, updatePropertiesOrder]);
193
+ };
191
194
 
192
195
  const onPropertyMove = (propertiesOrder: string[], namespace?: string) => {
193
196
  setFieldValue(namespaceToPropertiesOrderPath(namespace), propertiesOrder, false);
@@ -207,8 +210,8 @@ export function CollectionPropertiesEditorForm({
207
210
  ...(values.properties ?? {}),
208
211
  [id]: property
209
212
  }, false);
210
- const newPropertiesOrder = [...(values.propertiesOrder ?? Object.keys(values.properties)), id];
211
213
 
214
+ const newPropertiesOrder = [...(values.propertiesOrder ?? Object.keys(values.properties)), id];
212
215
  updatePropertiesOrder(newPropertiesOrder);
213
216
 
214
217
  setNewPropertyDialogOpen(false);
@@ -231,12 +234,6 @@ export function CollectionPropertiesEditorForm({
231
234
 
232
235
  // If the id has changed we need to a little cleanup
233
236
  if (previousId && previousId !== id) {
234
- console.debug("onPropertyChanged, id change", {
235
- id,
236
- property,
237
- previousId,
238
- namespace
239
- })
240
237
 
241
238
  const previousFullId = getFullId(previousId, namespace);
242
239
  const previousPropertyPath = idToPropertiesPath(previousFullId);
@@ -273,7 +270,7 @@ export function CollectionPropertiesEditorForm({
273
270
 
274
271
  };
275
272
 
276
- const onPropertyErrorInternal = useCallback((id: string, namespace?: string, error?: Record<string, any>) => {
273
+ const onPropertyErrorInternal = (id: string, namespace?: string, error?: Record<string, any>) => {
277
274
  const propertyPath = id ? getFullId(id, namespace) : undefined;
278
275
  console.debug("onPropertyErrorInternal", {
279
276
  id,
@@ -286,7 +283,7 @@ export function CollectionPropertiesEditorForm({
286
283
  onPropertyError(id, namespace, hasError ? error : undefined);
287
284
  setFieldError(idToPropertiesPath(propertyPath), hasError ? "Property error" : undefined);
288
285
  }
289
- }, [])
286
+ }
290
287
 
291
288
  const closePropertyDialog = () => {
292
289
  setSelectedPropertyIndex(undefined);
@@ -301,9 +298,9 @@ export function CollectionPropertiesEditorForm({
301
298
  ? values.propertiesOrder
302
299
  : Object.keys(values.properties)) as string[];
303
300
 
304
- const owner = useMemo(() => values.ownerId ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
301
+ const owner = useMemo(() => values.ownerId && getUser ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
305
302
 
306
- const onPropertyClick = useCallback((propertyKey: string, namespace?: string) => {
303
+ const onPropertyClick = (propertyKey: string, namespace?: string) => {
307
304
  console.debug("CollectionEditor: onPropertyClick", {
308
305
  propertyKey,
309
306
  namespace
@@ -311,11 +308,12 @@ export function CollectionPropertiesEditorForm({
311
308
  setSelectedPropertyIndex(usedPropertiesOrder.indexOf(propertyKey));
312
309
  setSelectedPropertyKey(propertyKey);
313
310
  setSelectedPropertyNamespace(namespace);
314
- }, [usedPropertiesOrder]);
311
+ };
315
312
 
316
313
  const body = (
317
- <div className={"grid grid-cols-12 gap-2 h-full bg-gray-50 dark:bg-gray-900"}>
318
- <div className={cn(
314
+ <div className={"grid grid-cols-12 gap-2 h-full bg-white dark:bg-surface-950"}>
315
+ <div className={cls(
316
+ "bg-surface-50 dark:bg-surface-900",
319
317
  "p-4 md:p-8 pb-20 md:pb-20",
320
318
  "col-span-12 lg:col-span-5 h-full overflow-auto",
321
319
  !asDialog && "border-r " + defaultBorderMixin
@@ -349,7 +347,8 @@ export function CollectionPropertiesEditorForm({
349
347
  </div>}
350
348
 
351
349
  <div className="ml-1 mt-2 flex flex-row gap-2">
352
- <Tooltip title={"Get the code for this collection"}>
350
+ <Tooltip title={"Get the code for this collection"}
351
+ asChild={true}>
353
352
  <IconButton
354
353
  variant={"filled"}
355
354
  disabled={inferringProperties}
@@ -357,17 +356,20 @@ export function CollectionPropertiesEditorForm({
357
356
  <CodeIcon/>
358
357
  </IconButton>
359
358
  </Tooltip>
360
- {inferPropertiesFromData && <Tooltip title={"Add new properties based on data"}>
359
+ {inferPropertiesFromData && <Tooltip title={"Add new properties based on data"}
360
+ asChild={true}>
361
361
  <IconButton
362
362
  variant={"filled"}
363
363
  disabled={inferringProperties}
364
364
  onClick={inferPropertiesFromData}>
365
- {inferringProperties ? <CircularProgress size={"small"}/> : <AutoAwesomeIcon/>}
365
+ {inferringProperties ? <CircularProgress size={"small"}/> : <AutorenewIcon/>}
366
366
  </IconButton>
367
367
  </Tooltip>}
368
- <Tooltip title={"Add new property"}>
368
+ <Tooltip title={"Add new property"}
369
+ asChild={true}>
369
370
  <Button
370
371
  variant={"outlined"}
372
+ color={"primary"}
371
373
  onClick={() => setNewPropertyDialogOpen(true)}>
372
374
  <AddIcon/>
373
375
  </Button>
@@ -402,8 +404,8 @@ export function CollectionPropertiesEditorForm({
402
404
 
403
405
  {!asDialog &&
404
406
  <div className={"col-span-12 lg:col-span-7 p-4 md:py-8 md:px-4 h-full overflow-auto pb-20 md:pb-20"}>
405
- <Paper
406
- className="sticky top-8 p-4 min-h-full border border-transparent w-full flex flex-col justify-center ">
407
+ <div
408
+ className="sticky top-8 min-h-full w-full flex flex-col justify-center">
407
409
 
408
410
  {selectedPropertyFullId &&
409
411
  selectedProperty &&
@@ -436,6 +438,7 @@ export function CollectionPropertiesEditorForm({
436
438
  : "Select a property to edit it"}
437
439
  </Typography>
438
440
  <Button variant={"outlined"}
441
+ color={"primary"}
439
442
  onClick={() => setNewPropertyDialogOpen(true)}
440
443
  >
441
444
  <AddIcon/>
@@ -447,7 +450,7 @@ export function CollectionPropertiesEditorForm({
447
450
  <Typography variant={"label"} className="flex items-center justify-center">
448
451
  {"This property is defined as a property builder in code"}
449
452
  </Typography>}
450
- </Paper>
453
+ </div>
451
454
  </div>}
452
455
 
453
456
  {asDialog && <PropertyFormDialog
@@ -469,6 +472,7 @@ export function CollectionPropertiesEditorForm({
469
472
  getData={getData}
470
473
  propertyConfigs={propertyConfigs}
471
474
  collectionEditable={collectionEditable}
475
+ onCancel={closePropertyDialog}
472
476
  onOkClicked={asDialog
473
477
  ? closePropertyDialog
474
478
  : undefined
@@ -496,11 +500,12 @@ export function CollectionPropertiesEditorForm({
496
500
  collectionEditable={collectionEditable}
497
501
  existingPropertyKeys={values.propertiesOrder as string[]}/>
498
502
 
499
- <GetCodeDialog
500
- collection={values}
501
- open={codeDialogOpen}
502
- onOpenChange={setCodeDialogOpen}/>
503
-
503
+ <ErrorBoundary>
504
+ <GetCodeDialog
505
+ collection={values}
506
+ open={codeDialogOpen}
507
+ onOpenChange={setCodeDialogOpen}/>
508
+ </ErrorBoundary>
504
509
  </>
505
510
  );
506
511
  }
@@ -0,0 +1,163 @@
1
+ import React from "react";
2
+ import {
3
+ ConfirmationDialog,
4
+ EntityAction,
5
+ EntityCollection,
6
+ resolveEntityAction,
7
+ useCustomizationController
8
+ } from "@firecms/core";
9
+ import {
10
+ AddIcon,
11
+ Alert,
12
+ Button,
13
+ Container,
14
+ DeleteIcon,
15
+ IconButton,
16
+ Paper,
17
+ Table,
18
+ TableBody,
19
+ TableCell,
20
+ TableRow,
21
+ Tooltip,
22
+ Typography,
23
+ } from "@firecms/ui";
24
+ import { PersistedCollection } from "../../types/persisted_collection";
25
+ import { useFormex } from "@firecms/formex";
26
+ import { EntityActionsSelectDialog } from "./EntityActionsSelectDialog";
27
+
28
+ export function EntityActionsEditTab({
29
+ collection,
30
+ }: {
31
+ collection: PersistedCollection,
32
+ }) {
33
+
34
+ const { entityActions: contextEntityActions } = useCustomizationController();
35
+
36
+ const [addEntityActionDialogOpen, setAddEntityActionDialogOpen] = React.useState<boolean>(false);
37
+ const [actionToDelete, setActionToDelete] = React.useState<string | undefined>();
38
+
39
+ const {
40
+ values,
41
+ setFieldValue
42
+ } = useFormex<EntityCollection>();
43
+
44
+ const resolvedEntityActions = values.entityActions?.filter((e): e is string => typeof e === "string")
45
+ .map(e => resolveEntityAction(e, contextEntityActions))
46
+ .filter(Boolean) as EntityAction<any>[] ?? [];
47
+ const hardCodedEntityActions = collection.entityActions?.filter((e): e is EntityAction<any> => typeof e !== "string") ?? [];
48
+ const totalEntityActions = resolvedEntityActions.length + hardCodedEntityActions.length;
49
+
50
+ return (
51
+ <div className={"overflow-auto my-auto"}>
52
+ <Container maxWidth={"2xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
53
+ <div className={"flex flex-col gap-16"}>
54
+ <div className={"flex-grow flex flex-col gap-4 items-start"}>
55
+ <Typography variant={"h5"}>
56
+ Custom actions
57
+ </Typography>
58
+
59
+ {totalEntityActions === 0 &&
60
+ <Alert action={<Button variant="text"
61
+ size={"small"}
62
+ href={"https://firecms.co/docs/custom_actions"}
63
+ component={"a"}
64
+ rel="noopener noreferrer"
65
+ target="_blank">More info</Button>}>
66
+ Define your own custom actions by uploading them with the CLI.
67
+ </Alert>
68
+ }
69
+
70
+ {<>
71
+ <Paper className={"flex flex-col gap-4 p-2 w-full"}>
72
+ <Table>
73
+ <TableBody>
74
+ {resolvedEntityActions.map((action) => (
75
+ <TableRow key={action.key}>
76
+ <TableCell
77
+ align="left">
78
+ <Typography variant={"subtitle2"} className={"flex-grow"}>
79
+ {action.name}
80
+ </Typography>
81
+ </TableCell>
82
+ <TableCell
83
+ align="right">
84
+ <Tooltip title={"Remove"}
85
+ asChild={true}>
86
+ <IconButton size="small"
87
+ onClick={(e) => {
88
+ e.preventDefault();
89
+ e.stopPropagation();
90
+ setActionToDelete(action.key);
91
+ }}
92
+ color="inherit">
93
+ <DeleteIcon size={"small"}/>
94
+ </IconButton>
95
+ </Tooltip>
96
+ </TableCell>
97
+ </TableRow>
98
+ ))}
99
+ {hardCodedEntityActions.map((action) => (
100
+ <TableRow key={action.key}>
101
+ <TableCell
102
+ align="left">
103
+ <Typography variant={"subtitle2"} className={"flex-grow"}>
104
+ {action.name}
105
+ </Typography>
106
+ <Typography variant={"caption"} className={"flex-grow"}>
107
+ This action is defined in code with
108
+ key <code>{action.key}</code>
109
+ </Typography>
110
+ </TableCell>
111
+ </TableRow>
112
+ ))}
113
+ </TableBody>
114
+ </Table>
115
+
116
+ <Button
117
+ onClick={() => {
118
+ setAddEntityActionDialogOpen(true);
119
+ }}
120
+ variant={"text"}
121
+ startIcon={<AddIcon/>}>
122
+ Add custom entity action
123
+ </Button>
124
+ </Paper>
125
+
126
+ </>}
127
+
128
+
129
+ </div>
130
+
131
+ </div>
132
+ </Container>
133
+
134
+ <div style={{ height: "52px" }}/>
135
+
136
+ {actionToDelete &&
137
+ <ConfirmationDialog open={Boolean(actionToDelete)}
138
+ onAccept={() => {
139
+ setFieldValue("entityActions", values.entityActions?.filter(e => e !== actionToDelete));
140
+ setActionToDelete(undefined);
141
+ }}
142
+ onCancel={() => setActionToDelete(undefined)}
143
+ title={<>Remove this action?</>}
144
+ body={<>This will <b>not
145
+ delete any data</b>, only
146
+ the action in the CMS</>}/>}
147
+
148
+ <EntityActionsSelectDialog
149
+ open={addEntityActionDialogOpen}
150
+ onClose={(selectedActionKey) => {
151
+ if (selectedActionKey) {
152
+ console.log("Selected action key:", selectedActionKey);
153
+ const value = [...(values.entityActions ?? []), selectedActionKey]
154
+ // only actions that are defined in the registry
155
+ .filter((e): e is string => typeof e === "string" && (contextEntityActions ?? []).some(action => action.key === e));
156
+ ;
157
+ setFieldValue("entityActions", value);
158
+ }
159
+ setAddEntityActionDialogOpen(false);
160
+ }}/>
161
+ </div>
162
+ );
163
+ }
@@ -0,0 +1,41 @@
1
+ import { useCustomizationController } from "@firecms/core";
2
+ import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography } from "@firecms/ui";
3
+ import React from "react";
4
+
5
+ export function EntityActionsSelectDialog({
6
+ open,
7
+ onClose
8
+ }: { open: boolean, onClose: (selectedActionKey?: string) => void }) {
9
+ const {
10
+ entityActions
11
+ } = useCustomizationController();
12
+
13
+ return <Dialog
14
+ maxWidth={"md"}
15
+ open={open}>
16
+ <DialogTitle>Select custom action</DialogTitle>
17
+ <DialogContent className={"flex flex-col gap-4"}>
18
+ {entityActions?.map((action) => {
19
+ return <Button
20
+ key={action.key}
21
+ onClick={() => onClose(action.key)}
22
+ fullWidth
23
+ variant={"text"}
24
+ >
25
+ {action.name} ({action.key})
26
+ </Button>;
27
+ })}
28
+ {(entityActions ?? []).length === 0 &&
29
+ <Typography variant={"body2"}>
30
+ No custom actions defined. Define your custom actions in the customization settings, before using this
31
+ dialog.
32
+ </Typography>
33
+ }
34
+ </DialogContent>
35
+ <DialogActions>
36
+ <Button variant={"outlined"}
37
+ color={"primary"}
38
+ onClick={() => onClose()}>Cancel</Button>
39
+ </DialogActions>
40
+ </Dialog>
41
+ }
@@ -1,8 +1,11 @@
1
1
  import { useCustomizationController } from "@firecms/core";
2
- import { Button, Dialog, DialogActions, DialogContent, Typography } from "@firecms/ui";
2
+ import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography } from "@firecms/ui";
3
3
  import React from "react";
4
4
 
5
- export function EntityCustomViewsSelectDialog({ open, onClose }: { open: boolean, onClose: (selectedViewKey?: string) => void }) {
5
+ export function EntityCustomViewsSelectDialog({
6
+ open,
7
+ onClose
8
+ }: { open: boolean, onClose: (selectedViewKey?: string) => void }) {
6
9
  const {
7
10
  entityViews,
8
11
  } = useCustomizationController();
@@ -10,10 +13,8 @@ export function EntityCustomViewsSelectDialog({ open, onClose }: { open: boolean
10
13
  return <Dialog
11
14
  maxWidth={"md"}
12
15
  open={open}>
16
+ <DialogTitle>Select custom view</DialogTitle>
13
17
  <DialogContent className={"flex flex-col gap-4"}>
14
- <Typography variant={"h6"}>
15
- Select view
16
- </Typography>
17
18
  {entityViews?.map((view) => {
18
19
  return <Button
19
20
  key={view.key}
@@ -26,12 +27,15 @@ export function EntityCustomViewsSelectDialog({ open, onClose }: { open: boolean
26
27
  })}
27
28
  {(entityViews ?? []).length === 0 &&
28
29
  <Typography variant={"body2"}>
29
- No custom views defined
30
+ No custom views defined. Define your custom views in the customization settings, before using this
31
+ dialog.
30
32
  </Typography>
31
33
  }
32
34
  </DialogContent>
33
35
  <DialogActions>
34
- <Button variant={"outlined"} onClick={() => onClose()}>Cancel</Button>
36
+ <Button variant={"outlined"}
37
+ color={"primary"}
38
+ onClick={() => onClose()}>Cancel</Button>
35
39
  </DialogActions>
36
40
  </Dialog>
37
41
  }
@@ -1,9 +1,9 @@
1
1
  import React, { useEffect } from "react";
2
2
  import equal from "react-fast-compare"
3
3
 
4
- import { ArrayContainer, EnumValueConfig, EnumValues, FieldCaption, } from "@firecms/core";
4
+ import { ArrayContainer, ArrayEntryParams, EnumValueConfig, EnumValues, FieldCaption, } from "@firecms/core";
5
5
  import {
6
- AutoAwesomeIcon,
6
+ AutorenewIcon,
7
7
  Badge,
8
8
  Button,
9
9
  CircularProgress,
@@ -11,6 +11,7 @@ import {
11
11
  Dialog,
12
12
  DialogActions,
13
13
  DialogContent,
14
+ DialogTitle,
14
15
  IconButton,
15
16
  ListIcon,
16
17
  Paper,
@@ -121,7 +122,10 @@ function EnumFormFields({
121
122
  const inferredValuesRef = React.useRef(new Set());
122
123
  const inferredValues = inferredValuesRef.current;
123
124
 
124
- const buildEntry = (index: number, internalId: number) => {
125
+ const buildEntry = ({
126
+ index,
127
+ internalId
128
+ }:ArrayEntryParams) => {
125
129
  const justAdded = lastInternalIdAdded === internalId;
126
130
  const entryError = errors?.enumValues && errors?.enumValues[index];
127
131
  return <EnumEntry index={index}
@@ -179,7 +183,7 @@ function EnumFormFields({
179
183
  variant={"text"}
180
184
  size={"small"}
181
185
  onClick={inferValues}>
182
- {inferring ? <CircularProgress size={"small"}/> : <AutoAwesomeIcon/>}
186
+ {inferring ? <CircularProgress size={"smallest"}/> : <AutorenewIcon/>}
183
187
  Infer values from data
184
188
  </Button>}
185
189
  </div>
@@ -193,7 +197,7 @@ function EnumFormFields({
193
197
  size={"small"}
194
198
  buildEntry={buildEntry}
195
199
  onInternalIdAdded={setLastInternalIdAdded}
196
- includeAddButton={true}
200
+ canAddElements={true}
197
201
  onValueChange={(value) => setFieldValue(enumValuesPath, value)}
198
202
  newDefaultEntry={{ id: "", label: "" }}/>
199
203
 
@@ -263,7 +267,7 @@ const EnumEntry = React.memo(
263
267
  size="small"
264
268
  autoFocus={autoFocus}
265
269
  autoComplete="off"
266
- endAdornment={inferredEntry && <AutoAwesomeIcon size={"small"}/>}
270
+ endAdornment={inferredEntry && <AutorenewIcon size={"small"}/>}
267
271
  error={Boolean(entryError?.label)}/>
268
272
 
269
273
  {!disabled &&
@@ -324,7 +328,7 @@ function EnumEntryDialog({
324
328
  open={open}
325
329
  onOpenChange={(open) => !open ? onClose() : undefined}
326
330
  >
327
-
331
+ <DialogTitle hidden>Enum form dialog</DialogTitle>
328
332
  <DialogContent>
329
333
  {index !== undefined &&
330
334
  <div>