@firecms/core 3.1.0-canary.24c8270 → 3.1.0-canary.501d471

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 (224) hide show
  1. package/dist/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
  2. package/dist/components/ErrorBoundary.d.ts +3 -1
  3. package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
  4. package/dist/components/LanguageToggle.d.ts +1 -0
  5. package/dist/components/UnsavedChangesDialog.d.ts +1 -0
  6. package/dist/components/VirtualTable/VirtualTableHeader.d.ts +1 -0
  7. package/dist/components/VirtualTable/VirtualTableHeaderRow.d.ts +1 -1
  8. package/dist/components/VirtualTable/VirtualTableProps.d.ts +6 -1
  9. package/dist/components/VirtualTable/types.d.ts +1 -0
  10. package/dist/components/index.d.ts +1 -0
  11. package/dist/core/DrawerNavigationGroup.d.ts +2 -2
  12. package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
  13. package/dist/editor/components/editor-bubble-item.d.ts +8 -0
  14. package/dist/editor/components/editor-bubble.d.ts +8 -0
  15. package/dist/editor/components/image-bubble.d.ts +5 -0
  16. package/dist/editor/components/index.d.ts +16 -0
  17. package/dist/editor/components/table-bubble.d.ts +5 -0
  18. package/dist/editor/editor.d.ts +30 -0
  19. package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
  20. package/dist/editor/extensions/Image/index.d.ts +6 -0
  21. package/dist/editor/extensions/Image.d.ts +6 -0
  22. package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
  23. package/dist/editor/extensions/clipboard.d.ts +7 -0
  24. package/dist/editor/extensions/custom-keymap.d.ts +1 -0
  25. package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
  26. package/dist/editor/hooks/useProseMirror.d.ts +13 -0
  27. package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
  28. package/dist/editor/index.d.ts +2 -0
  29. package/dist/editor/markdown.d.ts +5 -0
  30. package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
  31. package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
  32. package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
  33. package/dist/editor/nodeViews/index.d.ts +6 -0
  34. package/dist/editor/plugins/index.d.ts +2 -0
  35. package/dist/editor/plugins/inputrules.d.ts +6 -0
  36. package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
  37. package/dist/editor/plugins/slashCommandPlugin.d.ts +12 -0
  38. package/dist/editor/schema.d.ts +2 -0
  39. package/dist/editor/selectors/ai-selector.d.ts +0 -0
  40. package/dist/editor/selectors/color-selector.d.ts +10 -0
  41. package/dist/editor/selectors/link-selector.d.ts +8 -0
  42. package/dist/editor/selectors/node-selector.d.ts +15 -0
  43. package/dist/editor/selectors/text-buttons.d.ts +1 -0
  44. package/dist/editor/types.d.ts +5 -0
  45. package/dist/editor/useProseMirror.d.ts +16 -0
  46. package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
  47. package/dist/editor/utils/remove_classes.d.ts +1 -0
  48. package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
  49. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  50. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  51. package/dist/hooks/index.d.ts +1 -0
  52. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  53. package/dist/hooks/useCollapsedGroups.d.ts +3 -3
  54. package/dist/hooks/useTranslation.d.ts +17 -0
  55. package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
  56. package/dist/index.d.ts +5 -0
  57. package/dist/index.es.js +29889 -18645
  58. package/dist/index.es.js.map +1 -1
  59. package/dist/index.umd.js +29883 -18659
  60. package/dist/index.umd.js.map +1 -1
  61. package/dist/locales/de.d.ts +2 -0
  62. package/dist/locales/en.d.ts +10 -0
  63. package/dist/locales/es.d.ts +10 -0
  64. package/dist/locales/fr.d.ts +2 -0
  65. package/dist/locales/hi.d.ts +2 -0
  66. package/dist/locales/it.d.ts +2 -0
  67. package/dist/locales/pt.d.ts +7 -0
  68. package/dist/types/collections.d.ts +38 -0
  69. package/dist/types/customization_controller.d.ts +2 -1
  70. package/dist/types/firecms.d.ts +2 -1
  71. package/dist/types/index.d.ts +1 -0
  72. package/dist/types/navigation.d.ts +2 -2
  73. package/dist/types/plugins.d.ts +7 -0
  74. package/dist/types/properties.d.ts +9 -8
  75. package/dist/types/storage.d.ts +1 -0
  76. package/dist/types/translations.d.ts +669 -0
  77. package/dist/util/index.d.ts +1 -0
  78. package/dist/util/lazy_eager.d.ts +7 -0
  79. package/dist/util/objects.d.ts +1 -0
  80. package/dist/util/useStorageUploadController.d.ts +10 -1
  81. package/package.json +45 -9
  82. package/src/app/Scaffold.tsx +7 -5
  83. package/src/components/AIIcon.tsx +3 -1
  84. package/src/components/ArrayContainer.tsx +6 -4
  85. package/src/components/ClearFilterSortButton.tsx +6 -3
  86. package/src/components/ConfirmationDialog.tsx +4 -2
  87. package/src/components/DeleteEntityDialog.tsx +10 -7
  88. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +9 -3
  89. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
  90. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
  91. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
  92. package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
  93. package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
  94. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +16 -43
  95. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
  96. package/src/components/EntityCollectionView/EntityCollectionView.tsx +24 -18
  97. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -3
  98. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -2
  99. package/src/components/EntityCollectionView/FiltersDialog.tsx +8 -5
  100. package/src/components/EntityCollectionView/ViewModeToggle.tsx +11 -8
  101. package/src/components/EntityJsonPreview.tsx +2 -1
  102. package/src/components/EntityView.tsx +3 -2
  103. package/src/components/ErrorBoundary.tsx +27 -15
  104. package/src/components/HomePage/DefaultHomePage.tsx +19 -13
  105. package/src/components/HomePage/HomePageDnD.tsx +3 -1
  106. package/src/components/HomePage/NavigationGroup.tsx +3 -1
  107. package/src/components/HomePage/RenameGroupDialog.tsx +15 -13
  108. package/src/components/LanguageToggle.tsx +66 -0
  109. package/src/components/NotFoundPage.tsx +5 -3
  110. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -7
  111. package/src/components/ReferenceWidget.tsx +3 -2
  112. package/src/components/SearchIconsView.tsx +3 -1
  113. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +11 -0
  114. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +15 -2
  115. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +11 -0
  116. package/src/components/UnsavedChangesDialog.tsx +6 -4
  117. package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
  118. package/src/components/VirtualTable/VirtualTable.tsx +5 -3
  119. package/src/components/VirtualTable/VirtualTableHeader.tsx +21 -18
  120. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +8 -3
  121. package/src/components/VirtualTable/VirtualTableProps.tsx +7 -1
  122. package/src/components/VirtualTable/types.tsx +1 -0
  123. package/src/components/common/default_entity_actions.tsx +4 -0
  124. package/src/components/common/useDataSourceTableController.tsx +5 -14
  125. package/src/components/index.tsx +1 -0
  126. package/src/core/DefaultAppBar.tsx +14 -10
  127. package/src/core/DefaultDrawer.tsx +8 -2
  128. package/src/core/DrawerNavigationGroup.tsx +5 -3
  129. package/src/core/EntityEditView.tsx +53 -7
  130. package/src/core/EntityEditViewFormActions.tsx +24 -17
  131. package/src/core/EntitySidePanel.tsx +6 -4
  132. package/src/core/FireCMS.tsx +33 -6
  133. package/src/core/field_configs.tsx +4 -2
  134. package/src/editor/components/SlashCommandMenu.tsx +516 -0
  135. package/src/editor/components/editor-bubble-item.tsx +32 -0
  136. package/src/editor/components/editor-bubble.tsx +118 -0
  137. package/src/editor/components/image-bubble.tsx +156 -0
  138. package/src/editor/components/index.ts +14 -0
  139. package/src/editor/components/table-bubble.tsx +165 -0
  140. package/src/editor/editor.tsx +455 -0
  141. package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
  142. package/src/editor/extensions/Image/index.ts +133 -0
  143. package/src/editor/extensions/Image.ts +159 -0
  144. package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
  145. package/src/editor/extensions/clipboard.ts +72 -0
  146. package/src/editor/extensions/custom-keymap.ts +24 -0
  147. package/src/editor/extensions/drag-and-drop.tsx +480 -0
  148. package/src/editor/hooks/useProseMirror.ts +124 -0
  149. package/src/editor/hooks/useProseMirrorContext.ts +15 -0
  150. package/src/editor/index.ts +2 -0
  151. package/src/editor/markdown.ts +172 -0
  152. package/src/editor/nodeViews/ImageComponent.tsx +20 -0
  153. package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
  154. package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
  155. package/src/editor/nodeViews/index.ts +35 -0
  156. package/src/editor/plugins/index.ts +58 -0
  157. package/src/editor/plugins/inputrules.ts +82 -0
  158. package/src/editor/plugins/placeholderPlugin.ts +55 -0
  159. package/src/editor/plugins/slashCommandPlugin.ts +61 -0
  160. package/src/editor/schema.ts +240 -0
  161. package/src/editor/selectors/ai-selector.tsx +111 -0
  162. package/src/editor/selectors/color-selector.tsx +200 -0
  163. package/src/editor/selectors/link-selector.tsx +118 -0
  164. package/src/editor/selectors/node-selector.tsx +157 -0
  165. package/src/editor/selectors/text-buttons.tsx +86 -0
  166. package/src/editor/types.ts +6 -0
  167. package/src/editor/useProseMirror.ts +126 -0
  168. package/src/editor/utils/prosemirror-utils.ts +108 -0
  169. package/src/editor/utils/remove_classes.ts +17 -0
  170. package/src/editor/utils/useDebouncedCallback.ts +25 -0
  171. package/src/form/EntityForm.tsx +80 -7
  172. package/src/form/EntityFormActions.tsx +19 -12
  173. package/src/form/PropertyFieldBinding.tsx +7 -5
  174. package/src/form/components/LocalChangesMenu.tsx +13 -13
  175. package/src/form/components/StorageItemPreview.tsx +3 -2
  176. package/src/form/components/StorageUploadProgress.tsx +18 -3
  177. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +18 -5
  178. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +22 -9
  179. package/src/form/field_bindings/BlockFieldBinding.tsx +26 -9
  180. package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
  181. package/src/form/field_bindings/KeyValueFieldBinding.tsx +46 -24
  182. package/src/form/field_bindings/MapFieldBinding.tsx +27 -11
  183. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +73 -36
  184. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +15 -1
  185. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +25 -11
  186. package/src/form/field_bindings/ReferenceFieldBinding.tsx +25 -11
  187. package/src/form/field_bindings/RepeatFieldBinding.tsx +21 -6
  188. package/src/form/field_bindings/SelectFieldBinding.tsx +7 -5
  189. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +28 -10
  190. package/src/form/field_bindings/SwitchFieldBinding.tsx +31 -14
  191. package/src/form/field_bindings/TextFieldBinding.tsx +10 -7
  192. package/src/form/field_bindings/UserSelectFieldBinding.tsx +7 -5
  193. package/src/hooks/index.tsx +1 -0
  194. package/src/hooks/useBuildNavigationController.tsx +20 -13
  195. package/src/hooks/useCollapsedGroups.ts +7 -6
  196. package/src/hooks/useTranslation.ts +31 -0
  197. package/src/i18n/FireCMSi18nProvider.tsx +160 -0
  198. package/src/index.ts +5 -0
  199. package/src/locales/de.ts +718 -0
  200. package/src/locales/en.ts +730 -0
  201. package/src/locales/es.ts +730 -0
  202. package/src/locales/fr.ts +718 -0
  203. package/src/locales/hi.ts +718 -0
  204. package/src/locales/it.ts +718 -0
  205. package/src/locales/pt.ts +727 -0
  206. package/src/preview/PropertyPreview.tsx +3 -2
  207. package/src/preview/components/ReferencePreview.tsx +2 -1
  208. package/src/preview/components/UrlComponentPreview.tsx +4 -2
  209. package/src/preview/components/UserPreview.tsx +3 -1
  210. package/src/preview/property_previews/MapPropertyPreview.tsx +49 -27
  211. package/src/routes/FireCMSRoute.tsx +63 -54
  212. package/src/types/collections.ts +40 -0
  213. package/src/types/customization_controller.tsx +2 -1
  214. package/src/types/firecms.tsx +2 -1
  215. package/src/types/index.ts +1 -0
  216. package/src/types/navigation.ts +2 -2
  217. package/src/types/plugins.tsx +8 -0
  218. package/src/types/properties.ts +12 -10
  219. package/src/types/storage.ts +2 -1
  220. package/src/types/translations.ts +752 -0
  221. package/src/util/index.ts +1 -0
  222. package/src/util/lazy_eager.tsx +33 -0
  223. package/src/util/objects.ts +15 -0
  224. package/src/util/useStorageUploadController.tsx +23 -29
@@ -11,6 +11,7 @@ import {
11
11
  } from "../types";
12
12
 
13
13
  import { resolveProperty } from "../util";
14
+ import { jsonStringifyReplacer } from "../util/objects";
14
15
 
15
16
  import { PropertyPreviewProps } from "./PropertyPreviewProps";
16
17
  import { useAuthController, useCustomizationController } from "../hooks";
@@ -234,7 +235,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
234
235
  content = buildWrongValueType(propertyKey, property.dataType, value);
235
236
  }
236
237
  } else {
237
- content = JSON.stringify(value);
238
+ content = JSON.stringify(value, jsonStringifyReplacer);
238
239
  }
239
240
 
240
241
  return content === undefined || content === null || (Array.isArray(content) && content.length === 0)
@@ -246,6 +247,6 @@ function buildWrongValueType(name: string | undefined, dataType: string, value:
246
247
  console.warn(`Unexpected value for property ${name}, of type ${dataType}`, value);
247
248
  return (
248
249
  <ErrorView title={"Unexpected value"}
249
- error={`${JSON.stringify(value)}`} />
250
+ error={`${JSON.stringify(value, jsonStringifyReplacer)}`} />
250
251
  );
251
252
  }
@@ -6,6 +6,7 @@ import { PreviewSize } from "../PropertyPreviewProps";
6
6
  import { Skeleton } from "@firecms/ui";
7
7
  import { ErrorBoundary, ErrorView } from "../../components";
8
8
  import { EntityPreview, EntityPreviewContainer } from "../../components/EntityPreview";
9
+ import { jsonStringifyReplacer } from "../../util/objects";
9
10
 
10
11
  export type ReferencePreviewProps = {
11
12
  disabled?: boolean;
@@ -29,7 +30,7 @@ export const ReferencePreview = function ReferencePreview(props: ReferencePrevie
29
30
  onClick={props.onClick}
30
31
  size={props.size ?? "medium"}>
31
32
  <ErrorView error={"Unexpected value. Click to edit"}
32
- tooltip={JSON.stringify(reference)}/>
33
+ tooltip={JSON.stringify(reference, jsonStringifyReplacer)}/>
33
34
  </EntityPreviewContainer>;
34
35
  }
35
36
  return <ErrorBoundary>
@@ -6,6 +6,7 @@ import { PreviewType } from "../../types";
6
6
  import { PreviewSize } from "../PropertyPreviewProps";
7
7
  import { cls, DescriptionIcon, OpenInNewIcon, Tooltip, Typography } from "@firecms/ui";
8
8
  import { EmptyValue } from "./EmptyValue";
9
+ import { useTranslation } from "../../hooks/useTranslation";
9
10
 
10
11
  /**
11
12
  * @group Preview components
@@ -27,6 +28,8 @@ export function UrlComponentPreview({
27
28
  fill?: boolean
28
29
  }): React.ReactElement {
29
30
 
31
+ const { t } = useTranslation();
32
+
30
33
  if (!previewType) {
31
34
  if (!url || !url.trim()) return <EmptyValue />;
32
35
  return (
@@ -51,8 +54,7 @@ export function UrlComponentPreview({
51
54
  return <audio controls
52
55
  className={"max-w-100%"}
53
56
  src={url}>
54
- Your browser does not support the
55
- <code>audio</code> element.
57
+ {t("browser_does_not_support_audio")}
56
58
  </audio>;
57
59
  } else if (previewType === "video") {
58
60
  return <VideoPreview size={size} src={url} interactive={interactive} />;
@@ -4,6 +4,7 @@ import { useInternalUserManagementController } from "../../hooks";
4
4
  import { UserDisplay } from "../../components/UserDisplay";
5
5
  import { EmptyValue } from "./EmptyValue";
6
6
  import { Typography } from "@firecms/ui";
7
+ import { useTranslation } from "../../hooks/useTranslation";
7
8
 
8
9
  /**
9
10
  * Preview component for displaying user information.
@@ -13,6 +14,7 @@ import { Typography } from "@firecms/ui";
13
14
  */
14
15
  export function UserPreview({ value }: PropertyPreviewProps<string>) {
15
16
  const { getUser } = useInternalUserManagementController();
17
+ const { t } = useTranslation();
16
18
 
17
19
  if (!value) {
18
20
  return <EmptyValue/>;
@@ -20,7 +22,7 @@ export function UserPreview({ value }: PropertyPreviewProps<string>) {
20
22
 
21
23
  const user = getUser(value);
22
24
  if (!user) {
23
- return <Typography variant={"caption"} color={"secondary"}>User not found: {value}</Typography>;
25
+ return <Typography variant={"caption"} color={"secondary"}>{t("user_not_found", { value })}</Typography>;
24
26
  }
25
27
 
26
28
  return <UserDisplay user={user}/>;
@@ -6,6 +6,7 @@ import { PropertyPreview } from "../PropertyPreview";
6
6
  import { cls, defaultBorderMixin, Typography } from "@firecms/ui";
7
7
  import { ErrorBoundary } from "../../components";
8
8
  import { EmptyValue } from "../components/EmptyValue";
9
+ import { DatePreview } from "../components/DatePreview";
9
10
 
10
11
  /**
11
12
  * @group Preview components
@@ -111,37 +112,58 @@ export function KeyValuePreview({ value }: { value: any }) {
111
112
  return <div
112
113
  className="flex flex-col gap-1 w-full">
113
114
  {
114
- Object.entries(value).map(([key, childValue]: [string, any]) => (
115
- <div
116
- key={`map_preview_table_${key}}`}
117
- className={cls(defaultBorderMixin, "last:border-b-0 border-b")}>
115
+ Object.entries(value).map(([key, childValue]: [string, any]) => {
116
+ const isTimestampObj = childValue && typeof childValue === "object" && (
117
+ childValue instanceof Date ||
118
+ ("_seconds" in childValue && "_nanoseconds" in childValue && typeof childValue._seconds === "number" && typeof childValue._nanoseconds === "number") ||
119
+ ("seconds" in childValue && "nanoseconds" in childValue && typeof childValue.seconds === "number" && typeof childValue.nanoseconds === "number")
120
+ );
121
+
122
+ const isScalar = childValue && (typeof childValue !== "object" || isTimestampObj);
123
+
124
+ return (
118
125
  <div
119
- className={"flex flex-row pt-0.5 pb-0.5 gap-2"}>
120
- <div
121
- key={`table-cell-title-${key}-${key}`}
122
- className="min-w-[140px] w-[25%] py-1">
123
- <Typography variant={"caption"}
124
- className={"font-semibold break-words"}
125
- color={"secondary"}>
126
- {key}
127
- </Typography>
128
- </div>
126
+ key={`map_preview_table_${key}}`}
127
+ className={cls(defaultBorderMixin, "last:border-b-0 border-b")}>
129
128
  <div
130
- className="flex-grow max-w-[75%]">
131
- {childValue && typeof childValue !== "object" && <Typography>
132
- <ErrorBoundary>
133
- {childValue.toString()}
134
- </ErrorBoundary>
135
- </Typography>}
129
+ className={"flex flex-row pt-0.5 pb-0.5 gap-2"}>
130
+ <div
131
+ key={`table-cell-title-${key}-${key}`}
132
+ className="min-w-[140px] w-[25%] py-1">
133
+ <Typography variant={"caption"}
134
+ className={"font-semibold break-words"}
135
+ color={"secondary"}>
136
+ {key}
137
+ </Typography>
138
+ </div>
139
+ <div
140
+ className="flex-grow max-w-[75%]">
141
+ {isScalar && (isTimestampObj ? (
142
+ <ErrorBoundary>
143
+ <DatePreview date={
144
+ childValue instanceof Date ? childValue :
145
+ typeof childValue.toDate === "function" ? childValue.toDate() :
146
+ "_seconds" in childValue ? new Date(childValue._seconds * 1000 + childValue._nanoseconds / 1000000) :
147
+ new Date(childValue.seconds * 1000 + childValue.nanoseconds / 1000000)
148
+ } />
149
+ </ErrorBoundary>
150
+ ) : (
151
+ <Typography>
152
+ <ErrorBoundary>
153
+ {childValue.toString()}
154
+ </ErrorBoundary>
155
+ </Typography>
156
+ ))}
157
+ </div>
136
158
  </div>
159
+ {typeof childValue === "object" && !isTimestampObj &&
160
+ <div className={cls(defaultBorderMixin, "border-l pl-4")}>
161
+ <KeyValuePreview value={childValue}/>
162
+ </div>
163
+ }
137
164
  </div>
138
- {typeof childValue === "object" &&
139
- <div className={cls(defaultBorderMixin, "border-l pl-4")}>
140
- <KeyValuePreview value={childValue}/>
141
- </div>
142
- }
143
- </div>
144
- ))
165
+ );
166
+ })
145
167
  }
146
168
  </div>;
147
169
  }
@@ -1,7 +1,6 @@
1
1
  import { Blocker, useBlocker, useLocation } from "react-router";
2
- import { EntityEditView } from "../core/EntityEditView";
2
+ import React, { useEffect, useRef, useState } from "react";
3
3
  import { useNavigationController } from "../hooks";
4
- import { useEffect, useRef, useState } from "react";
5
4
  import { useNavigate } from "react-router-dom";
6
5
  import {
7
6
  getNavigationEntriesFromPath,
@@ -11,7 +10,11 @@ import {
11
10
  } from "../util/navigation_from_path";
12
11
  import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
13
12
  import { toArray } from "../util/arrays";
14
- import { EntityCollectionView, NotFoundPage } from "../components";
13
+ import { NotFoundPage } from "../components";
14
+ import { lazyEager } from "../util/lazy_eager";
15
+
16
+ const EntityEditView = lazyEager<typeof import("../core/EntityEditView")["EntityEditView"]>(() => import("../core/EntityEditView"), "EntityEditView");
17
+ const EntityCollectionView = lazyEager<typeof import("../components/EntityCollectionView/EntityCollectionView")["EntityCollectionView"]>(() => import("../components/EntityCollectionView/EntityCollectionView"), "EntityCollectionView");
15
18
  import { UnsavedChangesDialog } from "../components/UnsavedChangesDialog";
16
19
  import { EntityCollection } from "../types";
17
20
 
@@ -88,15 +91,17 @@ export function FireCMSRoute() {
88
91
  collection = navigation.getCollection(navigationEntries[0].path);
89
92
  if (!collection)
90
93
  return null;
91
- return <EntityCollectionView
92
- key={`collection_view_${collection.id ?? collection.path}`}
93
- isSubCollection={false}
94
- parentCollectionIds={[]}
95
- fullPath={collection.path}
96
- fullIdPath={collection.id}
97
- updateUrl={true}
98
- {...collection}
99
- Actions={toArray(collection.Actions)} />
94
+ return <React.Suspense fallback={null}>
95
+ <EntityCollectionView
96
+ key={`collection_view_${collection.id ?? collection.path}`}
97
+ isSubCollection={false}
98
+ parentCollectionIds={[]}
99
+ fullPath={collection.path}
100
+ fullIdPath={collection.id}
101
+ updateUrl={true}
102
+ {...collection}
103
+ Actions={toArray(collection.Actions)} />
104
+ </React.Suspense>;
100
105
  }
101
106
 
102
107
  if (isSidePanel) {
@@ -109,15 +114,17 @@ export function FireCMSRoute() {
109
114
  collection = navigation.getCollection(firstEntry.path);
110
115
  if (!collection)
111
116
  return null;
112
- return <EntityCollectionView
113
- key={`collection_view_${collection.id ?? collection.path}`}
114
- fullIdPath={collection.id}
115
- isSubCollection={false}
116
- parentCollectionIds={[]}
117
- fullPath={collection.path}
118
- updateUrl={true}
119
- {...collection}
120
- Actions={toArray(collection.Actions)} />;
117
+ return <React.Suspense fallback={null}>
118
+ <EntityCollectionView
119
+ key={`collection_view_${collection.id ?? collection.path}`}
120
+ fullIdPath={collection.id}
121
+ isSubCollection={false}
122
+ parentCollectionIds={[]}
123
+ fullPath={collection.path}
124
+ updateUrl={true}
125
+ {...collection}
126
+ Actions={toArray(collection.Actions)} />
127
+ </React.Suspense>;
121
128
  }
122
129
  }
123
130
 
@@ -215,39 +222,41 @@ function EntityFullScreenRoute({
215
222
  const fullIdPath = isNew ? lastCollectionEntry!.path : lastEntityEntry!.path;
216
223
  const collectionPath = navigation.resolveIdsFrom(fullIdPath);
217
224
  return <>
218
- <EntityEditView
219
- key={collection.id + "_" + (isNew ? "new" : (isCopy ? entityId + "_copy" : entityId))}
220
- entityId={isNew ? undefined : entityId}
221
- fullIdPath={fullIdPath}
222
- collection={collection}
223
- layout={"full_screen"}
224
- path={collectionPath}
225
- copy={isCopy}
226
- selectedTab={selectedTab ?? undefined}
227
- onValuesModified={(modified) => blocked.current = modified}
228
- onSaved={(params) => {
229
- const newSelectedTab = params.selectedTab;
230
- const newEntityId = params.entityId;
231
- if (newSelectedTab) {
232
- navigate(`${basePath}/${newEntityId}/${newSelectedTab}`, { replace: true });
233
- } else {
234
- navigate(`${basePath}/${newEntityId}`, { replace: true });
235
- }
236
- }}
237
- onTabChange={(params) => {
238
- setSelectedTab(params.selectedTab);
239
- if (isNew) {
240
- return;
241
- }
242
- const newSelectedTab = params.selectedTab;
243
- if (newSelectedTab) {
244
- navigate(`${basePath}/${entityId}/${newSelectedTab}`, { replace: true });
245
- } else {
246
- navigate(`${basePath}/${entityId}`, { replace: true });
247
- }
248
- }}
249
- parentCollectionIds={parentCollectionIds}
250
- />
225
+ <React.Suspense fallback={null}>
226
+ <EntityEditView
227
+ key={collection.id + "_" + (isNew ? "new" : (isCopy ? entityId + "_copy" : entityId))}
228
+ entityId={isNew ? undefined : entityId}
229
+ fullIdPath={fullIdPath}
230
+ collection={collection}
231
+ layout={"full_screen"}
232
+ path={collectionPath}
233
+ copy={isCopy}
234
+ selectedTab={selectedTab ?? undefined}
235
+ onValuesModified={(modified) => blocked.current = modified}
236
+ onSaved={(params) => {
237
+ const newSelectedTab = params.selectedTab;
238
+ const newEntityId = params.entityId;
239
+ if (newSelectedTab) {
240
+ navigate(`${basePath}/${newEntityId}/${newSelectedTab}`, { replace: true });
241
+ } else {
242
+ navigate(`${basePath}/${newEntityId}`, { replace: true });
243
+ }
244
+ }}
245
+ onTabChange={(params) => {
246
+ setSelectedTab(params.selectedTab);
247
+ if (isNew) {
248
+ return;
249
+ }
250
+ const newSelectedTab = params.selectedTab;
251
+ if (newSelectedTab) {
252
+ navigate(`${basePath}/${entityId}/${newSelectedTab}`, { replace: true });
253
+ } else {
254
+ navigate(`${basePath}/${entityId}`, { replace: true });
255
+ }
256
+ }}
257
+ parentCollectionIds={parentCollectionIds}
258
+ />
259
+ </React.Suspense>
251
260
 
252
261
  <UnsavedChangesDialog
253
262
  open={blocker?.state === "blocked"}
@@ -163,6 +163,31 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
163
163
  */
164
164
  subcollections?: EntityCollection<any, any>[];
165
165
 
166
+ /**
167
+ * You can group subcollections and custom views into dropdown menus
168
+ * in the entity view tabs. Views listed in a group will be removed
169
+ * from the top-level tabs and shown under a single dropdown instead.
170
+ *
171
+ * @example
172
+ * ```tsx
173
+ * const productsCollection = buildCollection({
174
+ * id: "products",
175
+ * path: "products",
176
+ * name: "Products",
177
+ * properties: { ... },
178
+ * subcollections: [localesCollection, reviewsCollection],
179
+ * entityViews: [sampleView],
180
+ * viewGroups: [
181
+ * {
182
+ * name: "Related data",
183
+ * views: ["locales", "reviews", "sample_view"]
184
+ * }
185
+ * ]
186
+ * });
187
+ * ```
188
+ */
189
+ viewGroups?: ViewGroup[];
190
+
166
191
  /**
167
192
  * This interface defines all the callbacks that can be used when an entity
168
193
  * is being created, updated or deleted.
@@ -413,6 +438,21 @@ export interface KanbanConfig<M extends Record<string, any> = any> {
413
438
  columnProperty: Extract<keyof M, string>;
414
439
  }
415
440
 
441
+ /**
442
+ * You can group subcollections and custom views into dropdown menus in the entity view tabs.
443
+ * @group Collections
444
+ */
445
+ export interface ViewGroup {
446
+ /**
447
+ * Name of the group
448
+ */
449
+ name: string;
450
+ /**
451
+ * Array of subcollection paths/ids or custom view keys
452
+ */
453
+ views: string[];
454
+ }
455
+
416
456
  /**
417
457
  * View mode for displaying a collection.
418
458
  * @group Collections
@@ -42,7 +42,8 @@ export type CustomizationController = {
42
42
  dateTimeFormat?: string;
43
43
 
44
44
  /**
45
- * Locale of the CMS, currently only affecting dates
45
+ * Locale of the CMS.
46
+ * Affects date formatting.
46
47
  */
47
48
  locale?: Locale;
48
49
 
@@ -111,7 +111,8 @@ export type FireCMSProps<USER extends User> = {
111
111
  dateTimeFormat?: string;
112
112
 
113
113
  /**
114
- * Locale of the CMS, currently only affecting dates
114
+ * Locale of the CMS.
115
+ * Affects date formatting.
115
116
  */
116
117
  locale?: Locale;
117
118
 
@@ -28,3 +28,4 @@ export * from "./export_import";
28
28
  export * from "./modify_collections";
29
29
  export * from "./analytics_controller";
30
30
  export * from "./customization_controller";
31
+ export * from "./translations";
@@ -252,7 +252,7 @@ export interface NavigationEntry {
252
252
  collection?: EntityCollection;
253
253
  view?: CMSView;
254
254
  description?: string;
255
- group: string;
255
+ group: string | null;
256
256
  }
257
257
 
258
258
  export type NavigationResult = {
@@ -261,7 +261,7 @@ export type NavigationResult = {
261
261
 
262
262
  navigationEntries: NavigationEntry[],
263
263
 
264
- groups: string[],
264
+ groups: (string | null)[],
265
265
 
266
266
  onNavigationEntriesUpdate: (entries: NavigationGroupMapping[]) => void;
267
267
  };
@@ -28,6 +28,14 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
28
28
  */
29
29
  loading?: boolean;
30
30
 
31
+ /**
32
+ * Override any FireCMS UI string, or provide translations for additional
33
+ * locales, specific for this plugin.
34
+ * Keys can be existing `FireCMSTranslations` keys, or new keys if using
35
+ * module augmentation.
36
+ */
37
+ i18n?: Record<string, Record<string, string>>;
38
+
31
39
  /**
32
40
  * You can use this prop to add higher order components to the CMS.
33
41
  * The components will be added to the root of the CMS, so any component
@@ -180,6 +180,17 @@ export interface BaseProperty<T extends CMSType, CustomProps = any> {
180
180
  * @see https://jsonlogic.com/ for JSON Logic syntax
181
181
  */
182
182
  conditions?: PropertyConditions;
183
+
184
+ /**
185
+ * Set this property to true to provide the UX to explicitly set the value to `null`.
186
+ * Defaults to `false`.
187
+ */
188
+ nullable?: boolean;
189
+
190
+ /**
191
+ * @deprecated Use `nullable` instead.
192
+ */
193
+ clearable?: boolean;
183
194
  }
184
195
 
185
196
  /**
@@ -620,11 +631,6 @@ export interface NumberProperty extends BaseProperty<number> {
620
631
  * Rules for validating this property
621
632
  */
622
633
  validation?: NumberPropertyValidationSchema,
623
-
624
- /**
625
- * Add an icon to clear the value and set it to `null`. Defaults to `false`
626
- */
627
- clearable?: boolean;
628
634
  }
629
635
 
630
636
  /**
@@ -735,11 +741,6 @@ export interface StringProperty extends BaseProperty<string> {
735
741
  */
736
742
  validation?: StringPropertyValidationSchema;
737
743
 
738
- /**
739
- * Add an icon to clear the value and set it to `null`. Defaults to `false`
740
- */
741
- clearable?: boolean;
742
-
743
744
  /**
744
745
  * You can use this property (a string) to behave as a reference to another
745
746
  * collection. The stored value is the ID of the entity in the
@@ -1110,6 +1111,7 @@ export type StorageConfig = {
1110
1111
  */
1111
1112
  imageResize?: ImageResize;
1112
1113
 
1114
+
1113
1115
  /**
1114
1116
  * Specific metadata set in your uploaded file.
1115
1117
  * For the default Firebase implementation, the values passed here are of type
@@ -6,7 +6,8 @@ export interface UploadFileProps {
6
6
  fileName?: string,
7
7
  path?: string,
8
8
  metadata?: any,
9
- bucket?: string
9
+ bucket?: string,
10
+ onProgress?: (progress: number) => void
10
11
  }
11
12
 
12
13
  /**