@firecms/collection_editor 3.0.0-canary.21 → 3.0.0-canary.211

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 (74) hide show
  1. package/LICENSE +114 -21
  2. package/dist/ConfigControllerProvider.d.ts +2 -2
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.es.js +10060 -4770
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/index.umd.js +10750 -3
  7. package/dist/index.umd.js.map +1 -1
  8. package/dist/types/collection_editor_controller.d.ts +4 -2
  9. package/dist/types/collection_inference.d.ts +1 -1
  10. package/dist/types/config_permissions.d.ts +2 -2
  11. package/dist/types/persisted_collection.d.ts +1 -1
  12. package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
  13. package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
  14. package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
  15. package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +3 -1
  16. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +3 -2
  17. package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +1 -1
  18. package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -1
  19. package/dist/ui/collection_editor/LayoutModeSwitch.d.ts +5 -0
  20. package/dist/ui/collection_editor/PropertyEditView.d.ts +8 -0
  21. package/dist/ui/collection_editor/PropertyTree.d.ts +9 -9
  22. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
  23. package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +7 -0
  24. package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
  25. package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
  26. package/dist/useCollectionEditorPlugin.d.ts +8 -11
  27. package/dist/utils/collections.d.ts +6 -0
  28. package/package.json +24 -35
  29. package/src/ConfigControllerProvider.tsx +67 -64
  30. package/src/index.ts +1 -0
  31. package/src/types/collection_editor_controller.tsx +7 -4
  32. package/src/types/collection_inference.ts +1 -1
  33. package/src/types/config_permissions.ts +1 -1
  34. package/src/types/persisted_collection.ts +2 -3
  35. package/src/ui/CollectionViewHeaderAction.tsx +10 -5
  36. package/src/ui/EditorCollectionAction.tsx +10 -63
  37. package/src/ui/EditorCollectionActionStart.tsx +88 -0
  38. package/src/ui/HomePageEditorCollectionAction.tsx +19 -13
  39. package/src/ui/NewCollectionButton.tsx +1 -1
  40. package/src/ui/NewCollectionCard.tsx +3 -3
  41. package/src/ui/PropertyAddColumnComponent.tsx +11 -6
  42. package/src/ui/collection_editor/CollectionDetailsForm.tsx +89 -12
  43. package/src/ui/collection_editor/CollectionEditorDialog.tsx +101 -34
  44. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +8 -7
  45. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +37 -36
  46. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +6 -5
  47. package/src/ui/collection_editor/EnumForm.tsx +10 -6
  48. package/src/ui/collection_editor/GetCodeDialog.tsx +56 -26
  49. package/src/ui/collection_editor/LayoutModeSwitch.tsx +54 -0
  50. package/src/ui/collection_editor/PropertyEditView.tsx +257 -79
  51. package/src/ui/collection_editor/PropertyFieldPreview.tsx +7 -10
  52. package/src/ui/collection_editor/PropertyTree.tsx +9 -7
  53. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +26 -19
  54. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +3 -5
  55. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +26 -9
  56. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +42 -9
  57. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +32 -20
  58. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +54 -47
  59. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -1
  60. package/src/ui/collection_editor/properties/MapPropertyField.tsx +7 -6
  61. package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
  62. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +2 -0
  63. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
  64. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +34 -19
  65. package/src/ui/collection_editor/properties/StringPropertyField.tsx +1 -10
  66. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +1 -0
  67. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +2 -2
  68. package/src/ui/collection_editor/templates/pages_template.ts +1 -6
  69. package/src/useCollectionEditorPlugin.tsx +33 -32
  70. package/src/utils/collections.ts +36 -0
  71. package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
  72. package/dist/ui/collection_editor/PropertySelectItem.d.ts +0 -8
  73. package/src/ui/RootCollectionSuggestions.tsx +0 -63
  74. 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,10 +16,10 @@ 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,
@@ -43,7 +43,7 @@ type CollectionEditorFormProps = {
43
43
  setDirty?: (dirty: boolean) => void;
44
44
  reservedGroups?: string[];
45
45
  extraIcon: React.ReactNode;
46
- getUser: (uid: string) => User | null;
46
+ getUser?: (uid: string) => User | null;
47
47
  getData?: () => Promise<object[]>;
48
48
  doCollectionInference: (collection: PersistedCollection) => Promise<Partial<EntityCollection> | null> | undefined;
49
49
  propertyConfigs: Record<string, PropertyConfig>;
@@ -158,20 +158,20 @@ export function CollectionPropertiesEditorForm({
158
158
  }
159
159
  : undefined;
160
160
 
161
- const getCurrentPropertiesOrder = useCallback((namespace?: string) => {
162
- if (!namespace) return currentPropertiesOrderRef.current[""];
161
+ const getCurrentPropertiesOrder = (namespace?: string) => {
162
+ if (!namespace) return currentPropertiesOrderRef.current[""] ?? getIn(values, namespaceToPropertiesOrderPath());
163
163
  return currentPropertiesOrderRef.current[namespace] ?? getIn(values, namespaceToPropertiesOrderPath(namespace));
164
- }, [values]);
164
+ };
165
165
 
166
- const updatePropertiesOrder = useCallback((newPropertiesOrder: string[], namespace?: string) => {
166
+ const updatePropertiesOrder = (newPropertiesOrder: string[], namespace?: string) => {
167
167
  const propertiesOrderPath = namespaceToPropertiesOrderPath(namespace);
168
168
 
169
169
  setFieldValue(propertiesOrderPath, newPropertiesOrder, false);
170
170
  currentPropertiesOrderRef.current[namespace ?? ""] = newPropertiesOrder;
171
171
 
172
- }, [setFieldValue]);
172
+ };
173
173
 
174
- const deleteProperty = useCallback((propertyKey?: string, namespace?: string) => {
174
+ const deleteProperty = (propertyKey?: string, namespace?: string) => {
175
175
  const fullId = propertyKey ? getFullId(propertyKey, namespace) : undefined;
176
176
  if (!fullId)
177
177
  throw Error("collection editor miss config");
@@ -179,15 +179,17 @@ export function CollectionPropertiesEditorForm({
179
179
  setFieldValue(idToPropertiesPath(fullId), undefined, false);
180
180
 
181
181
  const currentPropertiesOrder = getCurrentPropertiesOrder(namespace);
182
- const newPropertiesOrder = currentPropertiesOrder.filter((p) => p !== propertyKey);
183
- updatePropertiesOrder(newPropertiesOrder, namespace);
182
+ if (currentPropertiesOrder) {
183
+ const newPropertiesOrder = currentPropertiesOrder.filter((p) => p !== propertyKey);
184
+ updatePropertiesOrder(newPropertiesOrder, namespace);
185
+ }
184
186
 
185
187
  setNewPropertyDialogOpen(false);
186
188
 
187
189
  setSelectedPropertyIndex(undefined);
188
190
  setSelectedPropertyKey(undefined);
189
191
  setSelectedPropertyNamespace(undefined);
190
- }, [getCurrentPropertiesOrder, setFieldValue, updatePropertiesOrder]);
192
+ };
191
193
 
192
194
  const onPropertyMove = (propertiesOrder: string[], namespace?: string) => {
193
195
  setFieldValue(namespaceToPropertiesOrderPath(namespace), propertiesOrder, false);
@@ -207,8 +209,8 @@ export function CollectionPropertiesEditorForm({
207
209
  ...(values.properties ?? {}),
208
210
  [id]: property
209
211
  }, false);
210
- const newPropertiesOrder = [...(values.propertiesOrder ?? Object.keys(values.properties)), id];
211
212
 
213
+ const newPropertiesOrder = [...(values.propertiesOrder ?? Object.keys(values.properties)), id];
212
214
  updatePropertiesOrder(newPropertiesOrder);
213
215
 
214
216
  setNewPropertyDialogOpen(false);
@@ -231,12 +233,6 @@ export function CollectionPropertiesEditorForm({
231
233
 
232
234
  // If the id has changed we need to a little cleanup
233
235
  if (previousId && previousId !== id) {
234
- console.debug("onPropertyChanged, id change", {
235
- id,
236
- property,
237
- previousId,
238
- namespace
239
- })
240
236
 
241
237
  const previousFullId = getFullId(previousId, namespace);
242
238
  const previousPropertyPath = idToPropertiesPath(previousFullId);
@@ -273,7 +269,7 @@ export function CollectionPropertiesEditorForm({
273
269
 
274
270
  };
275
271
 
276
- const onPropertyErrorInternal = useCallback((id: string, namespace?: string, error?: Record<string, any>) => {
272
+ const onPropertyErrorInternal = (id: string, namespace?: string, error?: Record<string, any>) => {
277
273
  const propertyPath = id ? getFullId(id, namespace) : undefined;
278
274
  console.debug("onPropertyErrorInternal", {
279
275
  id,
@@ -286,7 +282,7 @@ export function CollectionPropertiesEditorForm({
286
282
  onPropertyError(id, namespace, hasError ? error : undefined);
287
283
  setFieldError(idToPropertiesPath(propertyPath), hasError ? "Property error" : undefined);
288
284
  }
289
- }, [])
285
+ }
290
286
 
291
287
  const closePropertyDialog = () => {
292
288
  setSelectedPropertyIndex(undefined);
@@ -301,9 +297,9 @@ export function CollectionPropertiesEditorForm({
301
297
  ? values.propertiesOrder
302
298
  : Object.keys(values.properties)) as string[];
303
299
 
304
- const owner = useMemo(() => values.ownerId ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
300
+ const owner = useMemo(() => values.ownerId && getUser ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
305
301
 
306
- const onPropertyClick = useCallback((propertyKey: string, namespace?: string) => {
302
+ const onPropertyClick = (propertyKey: string, namespace?: string) => {
307
303
  console.debug("CollectionEditor: onPropertyClick", {
308
304
  propertyKey,
309
305
  namespace
@@ -311,11 +307,11 @@ export function CollectionPropertiesEditorForm({
311
307
  setSelectedPropertyIndex(usedPropertiesOrder.indexOf(propertyKey));
312
308
  setSelectedPropertyKey(propertyKey);
313
309
  setSelectedPropertyNamespace(namespace);
314
- }, [usedPropertiesOrder]);
310
+ };
315
311
 
316
312
  const body = (
317
- <div className={"grid grid-cols-12 gap-2 h-full bg-gray-50 dark:bg-gray-900"}>
318
- <div className={cn(
313
+ <div className={"grid grid-cols-12 gap-2 h-full bg-surface-50 dark:bg-surface-900"}>
314
+ <div className={cls(
319
315
  "p-4 md:p-8 pb-20 md:pb-20",
320
316
  "col-span-12 lg:col-span-5 h-full overflow-auto",
321
317
  !asDialog && "border-r " + defaultBorderMixin
@@ -349,7 +345,8 @@ export function CollectionPropertiesEditorForm({
349
345
  </div>}
350
346
 
351
347
  <div className="ml-1 mt-2 flex flex-row gap-2">
352
- <Tooltip title={"Get the code for this collection"}>
348
+ <Tooltip title={"Get the code for this collection"}
349
+ asChild={true}>
353
350
  <IconButton
354
351
  variant={"filled"}
355
352
  disabled={inferringProperties}
@@ -357,15 +354,17 @@ export function CollectionPropertiesEditorForm({
357
354
  <CodeIcon/>
358
355
  </IconButton>
359
356
  </Tooltip>
360
- {inferPropertiesFromData && <Tooltip title={"Add new properties based on data"}>
357
+ {inferPropertiesFromData && <Tooltip title={"Add new properties based on data"}
358
+ asChild={true}>
361
359
  <IconButton
362
360
  variant={"filled"}
363
361
  disabled={inferringProperties}
364
362
  onClick={inferPropertiesFromData}>
365
- {inferringProperties ? <CircularProgress size={"small"}/> : <AutoAwesomeIcon/>}
363
+ {inferringProperties ? <CircularProgress size={"small"}/> : <AutorenewIcon/>}
366
364
  </IconButton>
367
365
  </Tooltip>}
368
- <Tooltip title={"Add new property"}>
366
+ <Tooltip title={"Add new property"}
367
+ asChild={true}>
369
368
  <Button
370
369
  variant={"outlined"}
371
370
  onClick={() => setNewPropertyDialogOpen(true)}>
@@ -469,6 +468,7 @@ export function CollectionPropertiesEditorForm({
469
468
  getData={getData}
470
469
  propertyConfigs={propertyConfigs}
471
470
  collectionEditable={collectionEditable}
471
+ onCancel={closePropertyDialog}
472
472
  onOkClicked={asDialog
473
473
  ? closePropertyDialog
474
474
  : undefined
@@ -496,11 +496,12 @@ export function CollectionPropertiesEditorForm({
496
496
  collectionEditable={collectionEditable}
497
497
  existingPropertyKeys={values.propertiesOrder as string[]}/>
498
498
 
499
- <GetCodeDialog
500
- collection={values}
501
- open={codeDialogOpen}
502
- onOpenChange={setCodeDialogOpen}/>
503
-
499
+ <ErrorBoundary>
500
+ <GetCodeDialog
501
+ collection={values}
502
+ open={codeDialogOpen}
503
+ onOpenChange={setCodeDialogOpen}/>
504
+ </ErrorBoundary>
504
505
  </>
505
506
  );
506
507
  }
@@ -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}
@@ -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>
@@ -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>
@@ -1,35 +1,47 @@
1
- import { EntityCollection, useSnackbarController } from "@firecms/core";
2
- import { Button, ContentCopyIcon, Dialog, DialogActions, DialogContent, Typography, } from "@firecms/ui";
1
+ import { EntityCollection, isEmptyObject, useSnackbarController } from "@firecms/core";
2
+ import { Button, ContentCopyIcon, Dialog, DialogActions, DialogContent, DialogTitle, Typography, } from "@firecms/ui";
3
3
  import React from "react";
4
4
  import JSON5 from "json5";
5
5
  import { Highlight, themes } from "prism-react-renderer"
6
6
  import { camelCase } from "./utils/strings";
7
+ import { clone } from "@firecms/formex";
7
8
 
8
- export function GetCodeDialog({ collection, onOpenChange, open }: { onOpenChange: (open: boolean) => void, collection: any, open: any }) {
9
+ export function GetCodeDialog({
10
+ collection,
11
+ onOpenChange,
12
+ open
13
+ }: { onOpenChange: (open: boolean) => void, collection: any, open: any }) {
9
14
 
10
15
  const snackbarController = useSnackbarController();
11
16
 
12
- const code = "import { EntityCollection } from \"firecms\";\n\nconst " + (collection.name ? camelCase(collection.name) : "my") + "Collection:EntityCollection = " + JSON5.stringify(collectionToCode(collection), null, "\t");
17
+ const code = collection
18
+ ? "import { EntityCollection } from \"@firecms/core\";\n\nconst " + (collection?.name ? camelCase(collection.name) : "my") + "Collection:EntityCollection = " + JSON5.stringify(collectionToCode({ ...collection }), null, "\t")
19
+ : "No collection selected";
13
20
  return <Dialog open={open}
14
21
  onOpenChange={onOpenChange}
15
22
  maxWidth={"4xl"}>
23
+ <DialogTitle variant={"h6"}>Code for {collection.name}</DialogTitle>
16
24
  <DialogContent>
17
- <Typography variant={"h6"} className={"my-4"}>
18
- Code for {collection.name}
19
- </Typography>
25
+
20
26
  <Typography variant={"body2"} className={"my-4 mb-8"}>
21
27
  If you want to customise the collection in code, you can add this collection code to your CMS
22
28
  app configuration.
23
29
  More info in the <a
24
30
  rel="noopener noreferrer"
25
- href={"https://firecms.co/docs/customization_quickstart"}>docs</a>.
31
+ href={"https://firecms.co/docs/cloud/quickstart"}>docs</a>.
26
32
  </Typography>
27
33
  <Highlight
28
34
  theme={themes.vsDark}
29
35
  code={code}
30
36
  language="typescript"
31
37
  >
32
- {({ className, style, tokens, getLineProps, getTokenProps }) => (
38
+ {({
39
+ className,
40
+ style,
41
+ tokens,
42
+ getLineProps,
43
+ getTokenProps
44
+ }) => (
33
45
  <pre style={style} className={"p-4 rounded text-sm"}>
34
46
  {tokens.map((line, i) => (
35
47
  <div key={i} {...getLineProps({ line })}>
@@ -66,24 +78,40 @@ export function GetCodeDialog({ collection, onOpenChange, open }: { onOpenChange
66
78
 
67
79
  function collectionToCode(collection: EntityCollection): object {
68
80
 
69
- const propertyCleanup = (property: any) => {
70
-
71
- const updatedProperty = {
72
- ...property
73
- };
74
-
75
- delete updatedProperty.fromBuilder;
76
- delete updatedProperty.resolved;
77
- delete updatedProperty.propertiesOrder;
78
- delete updatedProperty.editable;
81
+ const propertyCleanup = (value: any): any => {
82
+ if (value === undefined || value === null) {
83
+ return value;
84
+ }
85
+ const valueCopy = clone(value);
86
+ if (typeof valueCopy === "function") {
87
+ return valueCopy;
88
+ }
89
+ if (Array.isArray(valueCopy)) {
90
+ return valueCopy.map((v: any) => propertyCleanup(v));
91
+ }
92
+ if (typeof valueCopy === "object") {
93
+ if (valueCopy === null)
94
+ return valueCopy;
95
+ Object.keys(valueCopy).forEach((key) => {
96
+ if (!isEmptyObject(valueCopy)) {
97
+ const childRes = propertyCleanup(valueCopy[key]);
98
+ if (childRes !== null && childRes !== undefined && childRes !== false && !isEmptyObject(childRes)) {
99
+ valueCopy[key] = childRes;
100
+ } else {
101
+ delete valueCopy[key];
102
+ }
103
+ }
104
+ });
105
+ delete valueCopy.fromBuilder;
106
+ delete valueCopy.resolved;
107
+ delete valueCopy.propertiesOrder;
108
+ delete valueCopy.propertyConfig;
109
+ delete valueCopy.resolvedProperties;
110
+ delete valueCopy.editable;
79
111
 
80
- if (updatedProperty.type === "map") {
81
- return {
82
- ...updatedProperty,
83
- properties: updatedProperty.properties.map(propertyCleanup)
84
- }
85
112
  }
86
- return updatedProperty;
113
+
114
+ return valueCopy;
87
115
  }
88
116
 
89
117
  return {
@@ -99,7 +127,9 @@ function collectionToCode(collection: EntityCollection): object {
99
127
  customId: collection.customId,
100
128
  initialFilter: collection.initialFilter,
101
129
  initialSort: collection.initialSort,
102
- properties: Object.entries(collection.properties ?? {})
130
+ properties: Object.entries({
131
+ ...(collection.properties ?? {})
132
+ })
103
133
  .map(([key, value]) => ({
104
134
  [key]: propertyCleanup(value)
105
135
  }))
@@ -0,0 +1,54 @@
1
+ import { Card, cls, SquareIcon, Tooltip, Typography, VerticalSplitIcon } from "@firecms/ui";
2
+
3
+ export function LayoutModeSwitch({
4
+ value,
5
+ onChange,
6
+ className
7
+ }: {
8
+ value: "side_panel" | "full_screen";
9
+ onChange: (value: "side_panel" | "full_screen") => void;
10
+ className?: string;
11
+ }) {
12
+
13
+ return <div className={cls(className)}>
14
+ <Typography variant={"label"} color={"secondary"} className={"ml-3.5"}>Document view</Typography>
15
+ <div className={cls("flex flex-row gap-4")}>
16
+
17
+ <Tooltip title={"Documents are open in a side panel"}>
18
+ <Card
19
+ onClick={() => onChange("side_panel")}
20
+ className={cls(
21
+ "my-2 rounded-md mx-0 p-4 focus:outline-none transition ease-in-out duration-150 flex flex-row gap-4 items-center",
22
+ "text-surface-700 dark:text-surface-accent-300",
23
+ "hover:text-primary-dark dark:hover:text-primary focus:ring-primary hover:ring-1 hover:ring-primary",
24
+ value === "side_panel" ? "border-primary dark:border-primary" : "border-surface-400 dark:border-surface-600",
25
+ )}
26
+ >
27
+ <VerticalSplitIcon/>
28
+ <Typography variant={"label"}>
29
+ Side panel
30
+ </Typography>
31
+ </Card>
32
+ </Tooltip>
33
+
34
+ <Tooltip title={"Documents are open full-screen"}>
35
+ <Card
36
+ onClick={() => onChange("full_screen")}
37
+ className={cls(
38
+ "my-2 rounded-md mx-0 p-4 focus:outline-none transition ease-in-out duration-150 flex flex-row gap-4 items-center",
39
+ "text-surface-700 dark:text-surface-accent-300",
40
+ "hover:text-primary-dark dark:hover:text-primary focus:ring-primary hover:ring-1 hover:ring-primary",
41
+ value === "full_screen" ? "border-primary dark:border-primary" : "border-surface-400 dark:border-surface-600",
42
+ )}
43
+ >
44
+ <SquareIcon/>
45
+ <Typography variant={"label"}>
46
+ Full screen
47
+ </Typography>
48
+ </Card>
49
+ </Tooltip>
50
+
51
+ </div>
52
+ <Typography variant={"caption"} color={"secondary"} className={"ml-3.5"}>Should documents be opened full screen or in an inline side dialog</Typography>
53
+ </div>
54
+ }