@firecms/core 3.1.0 → 3.2.0-canary.9c3d298

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 (191) 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/index.d.ts +1 -0
  7. package/dist/core/DrawerNavigationGroup.d.ts +2 -2
  8. package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
  9. package/dist/editor/components/editor-bubble-item.d.ts +8 -0
  10. package/dist/editor/components/editor-bubble.d.ts +8 -0
  11. package/dist/editor/components/image-bubble.d.ts +5 -0
  12. package/dist/editor/components/index.d.ts +16 -0
  13. package/dist/editor/components/table-bubble.d.ts +5 -0
  14. package/dist/editor/editor.d.ts +30 -0
  15. package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
  16. package/dist/editor/extensions/Image/index.d.ts +6 -0
  17. package/dist/editor/extensions/Image.d.ts +6 -0
  18. package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
  19. package/dist/editor/extensions/clipboard.d.ts +7 -0
  20. package/dist/editor/extensions/custom-keymap.d.ts +1 -0
  21. package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
  22. package/dist/editor/hooks/useProseMirror.d.ts +13 -0
  23. package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
  24. package/dist/editor/index.d.ts +2 -0
  25. package/dist/editor/markdown.d.ts +5 -0
  26. package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
  27. package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
  28. package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
  29. package/dist/editor/nodeViews/index.d.ts +6 -0
  30. package/dist/editor/plugins/index.d.ts +2 -0
  31. package/dist/editor/plugins/inputrules.d.ts +6 -0
  32. package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
  33. package/dist/editor/plugins/slashCommandPlugin.d.ts +12 -0
  34. package/dist/editor/schema.d.ts +2 -0
  35. package/dist/editor/selectors/ai-selector.d.ts +0 -0
  36. package/dist/editor/selectors/color-selector.d.ts +10 -0
  37. package/dist/editor/selectors/link-selector.d.ts +8 -0
  38. package/dist/editor/selectors/node-selector.d.ts +15 -0
  39. package/dist/editor/selectors/text-buttons.d.ts +1 -0
  40. package/dist/editor/types.d.ts +5 -0
  41. package/dist/editor/useProseMirror.d.ts +16 -0
  42. package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
  43. package/dist/editor/utils/remove_classes.d.ts +1 -0
  44. package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
  45. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  46. package/dist/hooks/index.d.ts +1 -0
  47. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  48. package/dist/hooks/useCollapsedGroups.d.ts +3 -3
  49. package/dist/hooks/useTranslation.d.ts +17 -0
  50. package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
  51. package/dist/index.d.ts +4 -0
  52. package/dist/index.es.js +12898 -2265
  53. package/dist/index.es.js.map +1 -1
  54. package/dist/index.umd.js +12877 -2264
  55. package/dist/index.umd.js.map +1 -1
  56. package/dist/locales/de.d.ts +2 -0
  57. package/dist/locales/en.d.ts +10 -0
  58. package/dist/locales/es.d.ts +10 -0
  59. package/dist/locales/fr.d.ts +2 -0
  60. package/dist/locales/hi.d.ts +2 -0
  61. package/dist/locales/it.d.ts +2 -0
  62. package/dist/locales/pt.d.ts +7 -0
  63. package/dist/types/customization_controller.d.ts +2 -1
  64. package/dist/types/firecms.d.ts +2 -1
  65. package/dist/types/index.d.ts +1 -0
  66. package/dist/types/navigation.d.ts +2 -2
  67. package/dist/types/plugins.d.ts +7 -0
  68. package/dist/types/storage.d.ts +1 -0
  69. package/dist/types/translations.d.ts +646 -0
  70. package/dist/util/useStorageUploadController.d.ts +10 -1
  71. package/package.json +45 -9
  72. package/src/app/Scaffold.tsx +7 -5
  73. package/src/components/AIIcon.tsx +3 -1
  74. package/src/components/ArrayContainer.tsx +6 -4
  75. package/src/components/ClearFilterSortButton.tsx +6 -3
  76. package/src/components/ConfirmationDialog.tsx +4 -2
  77. package/src/components/DeleteEntityDialog.tsx +10 -7
  78. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
  79. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
  80. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
  81. package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
  82. package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
  83. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +16 -43
  84. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
  85. package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -18
  86. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -3
  87. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -2
  88. package/src/components/EntityCollectionView/FiltersDialog.tsx +8 -5
  89. package/src/components/EntityCollectionView/ViewModeToggle.tsx +11 -8
  90. package/src/components/EntityView.tsx +3 -2
  91. package/src/components/ErrorBoundary.tsx +27 -15
  92. package/src/components/HomePage/DefaultHomePage.tsx +19 -13
  93. package/src/components/HomePage/HomePageDnD.tsx +3 -1
  94. package/src/components/HomePage/NavigationGroup.tsx +3 -1
  95. package/src/components/HomePage/RenameGroupDialog.tsx +15 -13
  96. package/src/components/LanguageToggle.tsx +66 -0
  97. package/src/components/NotFoundPage.tsx +5 -3
  98. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -7
  99. package/src/components/ReferenceWidget.tsx +3 -2
  100. package/src/components/SearchIconsView.tsx +3 -1
  101. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +11 -0
  102. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +15 -2
  103. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +11 -0
  104. package/src/components/UnsavedChangesDialog.tsx +6 -4
  105. package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
  106. package/src/components/VirtualTable/VirtualTableHeader.tsx +12 -10
  107. package/src/components/common/default_entity_actions.tsx +4 -0
  108. package/src/components/common/useDataSourceTableController.tsx +12 -4
  109. package/src/components/index.tsx +1 -0
  110. package/src/core/DefaultAppBar.tsx +14 -10
  111. package/src/core/DefaultDrawer.tsx +8 -2
  112. package/src/core/DrawerNavigationGroup.tsx +5 -3
  113. package/src/core/EntityEditView.tsx +4 -3
  114. package/src/core/EntityEditViewFormActions.tsx +24 -17
  115. package/src/core/EntitySidePanel.tsx +6 -5
  116. package/src/core/FireCMS.tsx +33 -6
  117. package/src/editor/components/SlashCommandMenu.tsx +516 -0
  118. package/src/editor/components/editor-bubble-item.tsx +32 -0
  119. package/src/editor/components/editor-bubble.tsx +118 -0
  120. package/src/editor/components/image-bubble.tsx +156 -0
  121. package/src/editor/components/index.ts +14 -0
  122. package/src/editor/components/table-bubble.tsx +165 -0
  123. package/src/editor/editor.tsx +455 -0
  124. package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
  125. package/src/editor/extensions/Image/index.ts +133 -0
  126. package/src/editor/extensions/Image.ts +159 -0
  127. package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
  128. package/src/editor/extensions/clipboard.ts +72 -0
  129. package/src/editor/extensions/custom-keymap.ts +24 -0
  130. package/src/editor/extensions/drag-and-drop.tsx +480 -0
  131. package/src/editor/hooks/useProseMirror.ts +124 -0
  132. package/src/editor/hooks/useProseMirrorContext.ts +15 -0
  133. package/src/editor/index.ts +2 -0
  134. package/src/editor/markdown.ts +172 -0
  135. package/src/editor/nodeViews/ImageComponent.tsx +20 -0
  136. package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
  137. package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
  138. package/src/editor/nodeViews/index.ts +35 -0
  139. package/src/editor/plugins/index.ts +58 -0
  140. package/src/editor/plugins/inputrules.ts +82 -0
  141. package/src/editor/plugins/placeholderPlugin.ts +55 -0
  142. package/src/editor/plugins/slashCommandPlugin.ts +61 -0
  143. package/src/editor/schema.ts +240 -0
  144. package/src/editor/selectors/ai-selector.tsx +111 -0
  145. package/src/editor/selectors/color-selector.tsx +200 -0
  146. package/src/editor/selectors/link-selector.tsx +118 -0
  147. package/src/editor/selectors/node-selector.tsx +157 -0
  148. package/src/editor/selectors/text-buttons.tsx +86 -0
  149. package/src/editor/types.ts +6 -0
  150. package/src/editor/useProseMirror.ts +126 -0
  151. package/src/editor/utils/prosemirror-utils.ts +108 -0
  152. package/src/editor/utils/remove_classes.ts +17 -0
  153. package/src/editor/utils/useDebouncedCallback.ts +25 -0
  154. package/src/form/EntityForm.tsx +16 -3
  155. package/src/form/EntityFormActions.tsx +19 -12
  156. package/src/form/PropertyFieldBinding.tsx +3 -2
  157. package/src/form/components/LocalChangesMenu.tsx +13 -13
  158. package/src/form/components/StorageItemPreview.tsx +3 -2
  159. package/src/form/components/StorageUploadProgress.tsx +18 -3
  160. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -4
  161. package/src/form/field_bindings/BlockFieldBinding.tsx +5 -2
  162. package/src/form/field_bindings/KeyValueFieldBinding.tsx +23 -18
  163. package/src/form/field_bindings/MapFieldBinding.tsx +4 -3
  164. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +33 -19
  165. package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -1
  166. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +4 -3
  167. package/src/hooks/index.tsx +1 -0
  168. package/src/hooks/useBuildNavigationController.tsx +45 -18
  169. package/src/hooks/useCollapsedGroups.ts +7 -6
  170. package/src/hooks/useTranslation.ts +31 -0
  171. package/src/i18n/FireCMSi18nProvider.tsx +160 -0
  172. package/src/index.ts +4 -0
  173. package/src/internal/useBuildSideEntityController.tsx +22 -20
  174. package/src/locales/de.ts +691 -0
  175. package/src/locales/en.ts +703 -0
  176. package/src/locales/es.ts +703 -0
  177. package/src/locales/fr.ts +691 -0
  178. package/src/locales/hi.ts +691 -0
  179. package/src/locales/it.ts +691 -0
  180. package/src/locales/pt.ts +700 -0
  181. package/src/preview/components/UrlComponentPreview.tsx +4 -2
  182. package/src/preview/components/UserPreview.tsx +3 -1
  183. package/src/types/customization_controller.tsx +2 -1
  184. package/src/types/firecms.tsx +2 -1
  185. package/src/types/index.ts +1 -0
  186. package/src/types/navigation.ts +2 -2
  187. package/src/types/plugins.tsx +8 -0
  188. package/src/types/properties.ts +1 -0
  189. package/src/types/storage.ts +2 -1
  190. package/src/types/translations.ts +725 -0
  191. package/src/util/useStorageUploadController.tsx +23 -29
@@ -1,4 +1,5 @@
1
1
  import React, { useCallback, useEffect, useMemo, useState } from "react";
2
+ import { useLocation } from "react-router-dom";
2
3
 
3
4
  import { useDataSource, useFireCMSContext, useNavigationController } from "../../hooks";
4
5
  import { useDataOrder } from "../../hooks/data/useDataOrder";
@@ -94,7 +95,7 @@ export function useDataSourceTableController<M extends Record<string, any> = any
94
95
  const [searchString, setSearchString] = React.useState<string | undefined>();
95
96
 
96
97
  const checkFilterCombination = useCallback((filterValues: FilterValues<any>,
97
- sortBy?: [string, "asc" | "desc"]) => {
98
+ sortBy?: [string, "asc" | "desc"]) => {
98
99
  if (!dataSource.isFilterCombinationValid)
99
100
  return true;
100
101
  return dataSource.isFilterCombinationValid({
@@ -106,8 +107,8 @@ export function useDataSourceTableController<M extends Record<string, any> = any
106
107
  }, []);
107
108
 
108
109
  const onScroll = ({
109
- scrollOffset
110
- }: {
110
+ scrollOffset
111
+ }: {
111
112
  scrollOffset: number
112
113
  }) => {
113
114
  if (scrollRestoration) {
@@ -128,10 +129,12 @@ export function useDataSourceTableController<M extends Record<string, any> = any
128
129
  return initialSort;
129
130
  }, [initialSort, forceFilter]);
130
131
 
132
+ const location = useLocation();
133
+
131
134
  const {
132
135
  filterValues: initialFilterUrl,
133
136
  sortBy: initialSortUrl,
134
- } = parseFilterAndSort(window.location.search);
137
+ } = parseFilterAndSort(location.search);
135
138
 
136
139
  const [filterValues, setFilterValues] = React.useState<FilterValues<Extract<keyof M, string>> | undefined>(forceFilter ?? (updateUrl ? initialFilterUrl : undefined) ?? initialFilter ?? undefined);
137
140
  const [sortBy, setSortBy] = React.useState<[Extract<keyof M, string>, "asc" | "desc"] | undefined>((updateUrl ? initialSortUrl : undefined) ?? initialSortInternal);
@@ -328,6 +331,11 @@ function encodeFilterAndSort(filterValues?: FilterValues<string>, sortBy?: [stri
328
331
  } else if (val instanceof EntityReference) {
329
332
  encodedValue = encodeRef(val);
330
333
  }
334
+ } else if (typeof val === "string") {
335
+ // JSON.stringify wraps the string in quotes (e.g. "4" → '"4"')
336
+ // so that decodeString's JSON.parse restores the string type,
337
+ // not a number. Without this, "4" round-trips as the number 4.
338
+ encodedValue = JSON.stringify(val);
331
339
  }
332
340
  } catch (e) {
333
341
  encodedValue = val;
@@ -42,3 +42,4 @@ export * from "./FieldCaption";
42
42
  export * from "./EntityPreview";
43
43
 
44
44
  export * from "./AIIcon";
45
+ export * from "./LanguageToggle";
@@ -1,10 +1,11 @@
1
1
  import React from "react";
2
2
 
3
3
  import { Link, useNavigate } from "react-router-dom";
4
- import { ErrorBoundary, FireCMSLogo } from "../components";
4
+ import { ErrorBoundary, FireCMSLogo, LanguageToggle } from "../components";
5
5
  import {
6
6
  Avatar,
7
7
  BrightnessMediumIcon,
8
+ CheckIcon,
8
9
  cls,
9
10
  DarkModeIcon,
10
11
  IconButton,
@@ -15,7 +16,7 @@ import {
15
16
  Skeleton,
16
17
  Typography
17
18
  } from "@firecms/ui";
18
- import { useAuthController, useLargeLayout, useModeController, useNavigationController } from "../hooks";
19
+ import { useAuthController, useLargeLayout, useModeController, useNavigationController, useTranslation } from "../hooks";
19
20
  import { User } from "../types";
20
21
  import { useApp } from "../app/useApp";
21
22
  import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
@@ -85,6 +86,8 @@ export const DefaultAppBar = function DefaultAppBar({
85
86
  mode,
86
87
  setMode
87
88
  } = useModeController();
89
+
90
+ const { i18n, t } = useTranslation();
88
91
 
89
92
  const navigate = useNavigate();
90
93
 
@@ -192,18 +195,19 @@ export const DefaultAppBar = function DefaultAppBar({
192
195
  <Menu
193
196
  trigger={<IconButton
194
197
  color="inherit"
195
- aria-label="Open drawer"
196
- size="large">
198
+ aria-label="Open drawer">
197
199
  {mode === "dark"
198
- ? <DarkModeIcon />
199
- : <LightModeIcon />}
200
+ ? <DarkModeIcon size="small" />
201
+ : <LightModeIcon size="small" />}
200
202
  </IconButton>}>
201
- <MenuItem onClick={() => setMode("dark")}><DarkModeIcon size={"smallest"} /> Dark</MenuItem>
202
- <MenuItem onClick={() => setMode("light")}><LightModeIcon size={"smallest"} /> Light </MenuItem>
203
+ <MenuItem onClick={() => setMode("dark")}><DarkModeIcon size={"smallest"} /> {t("dark_mode")}</MenuItem>
204
+ <MenuItem onClick={() => setMode("light")}><LightModeIcon size={"smallest"} /> {t("light_mode")}</MenuItem>
203
205
  <MenuItem onClick={() => setMode("system")}> <BrightnessMediumIcon
204
- size={"smallest"} />System</MenuItem>
206
+ size={"smallest"} />{t("system_mode")}</MenuItem>
205
207
  </Menu>}
206
208
 
209
+ <LanguageToggle />
210
+
207
211
  <Menu trigger={avatarComponent}>
208
212
  {user && <div className={"px-4 py-2 mb-2"}>
209
213
  {user.displayName && <Typography variant={"body1"} color={"secondary"}>
@@ -222,7 +226,7 @@ export const DefaultAppBar = function DefaultAppBar({
222
226
  navigate("/");
223
227
  }}>
224
228
  <LogoutIcon />
225
- Log Out
229
+ {t("log_out")}
226
230
  </MenuItem>}
227
231
 
228
232
  </Menu>
@@ -1,6 +1,11 @@
1
1
  import React from "react";
2
2
 
3
- import { useCollapsedGroups, useLargeLayout, useNavigationController } from "../hooks";
3
+ import {
4
+ useCollapsedGroups,
5
+ useLargeLayout,
6
+ useNavigationController,
7
+ useTranslation
8
+ } from "../hooks";
4
9
 
5
10
  import { Link, useNavigate } from "react-router-dom";
6
11
  import { CMSAnalyticsEvent, NavigationEntry, NavigationResult } from "../types";
@@ -34,6 +39,7 @@ export function DefaultDrawer({
34
39
 
35
40
  const analyticsController = useAnalyticsController();
36
41
  const navigation = useNavigationController();
42
+ const { t } = useTranslation();
37
43
 
38
44
  const tooltipsOpen = drawerHovered && !drawerOpen && !adminMenuOpen;
39
45
  const largeLayout = useLargeLayout();
@@ -122,7 +128,7 @@ export function DefaultDrawer({
122
128
  }}
123
129
  key={entry.id}>
124
130
  {<IconForView collectionOrView={entry.view} />}
125
- {entry.name}
131
+ {t(entry.name as any)}
126
132
  </MenuItem>)}
127
133
 
128
134
  </Menu>}
@@ -3,12 +3,13 @@ import { cls, ExpandMoreIcon, Typography } from "@firecms/ui";
3
3
  import { NavigationEntry } from "../types";
4
4
  import { IconForView } from "../util";
5
5
  import { DrawerNavigationItem } from "./DrawerNavigationItem";
6
+ import { useTranslation } from "../hooks/useTranslation";
6
7
 
7
8
  export interface DrawerNavigationGroupProps {
8
9
  /**
9
- * Group name to display in header
10
+ * Group name to display in header. When null, uses the translated default group name.
10
11
  */
11
- group: string;
12
+ group: string | null;
12
13
  /**
13
14
  * Navigation entries in this group
14
15
  */
@@ -58,6 +59,7 @@ export function DrawerNavigationGroup({
58
59
  headerActions,
59
60
  onItemClick
60
61
  }: DrawerNavigationGroupProps) {
62
+ const { t } = useTranslation();
61
63
  return (
62
64
  <div
63
65
  className={"bg-surface-50 dark:bg-surface-800/30 my-4 rounded-lg ml-3 mr-1"}
@@ -81,7 +83,7 @@ export function DrawerNavigationGroup({
81
83
  color={"secondary"}
82
84
  className="font-medium flex-grow line-clamp-1"
83
85
  >
84
- {(group || "Views").toUpperCase()}
86
+ {(group || t("views_group")).toUpperCase()}
85
87
  </Typography>
86
88
  {headerActions && (
87
89
  <div onClick={(e) => e.stopPropagation()}>
@@ -32,6 +32,7 @@ import { EntityForm, EntityFormProps } from "../form";
32
32
  import { EntityEditViewFormActions } from "./EntityEditViewFormActions";
33
33
  import { EntityJsonPreview } from "../components/EntityJsonPreview";
34
34
  import { createFormexStub } from "../util/createFormexStub";
35
+ import { useTranslation } from "../hooks/useTranslation";
35
36
 
36
37
  export const MAIN_TAB_VALUE = "__main_##Q$SC^#S6";
37
38
  export const JSON_TAB_VALUE = "__json";
@@ -170,6 +171,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
170
171
  }) {
171
172
 
172
173
  const context = useFireCMSContext();
174
+ const { t } = useTranslation();
173
175
 
174
176
  const [usedEntity, setUsedEntity] = useState<Entity<M> | undefined>(entity);
175
177
 
@@ -342,8 +344,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
342
344
  openEntityMode={layout} />
343
345
  : <div className="flex items-center justify-center w-full h-full p-3">
344
346
  <Typography variant={"label"}>
345
- You need to save your entity before
346
- adding additional collections
347
+ {t("youd_need_to_save_before_additional_collections")}
347
348
  </Typography>
348
349
  </div>)
349
350
  }
@@ -451,7 +452,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
451
452
  let result = <div className="relative flex flex-col h-full w-full bg-white dark:bg-surface-900">
452
453
 
453
454
  {shouldShowTopBar && <div
454
- className={cls("h-14 items-center overflow-visible overflow-x-scroll w-full no-scrollbar border-b pl-2 pr-2 flex gap-2 bg-surface-50 dark:bg-surface-900", defaultBorderMixin)}>
455
+ className={cls("h-14 items-center overflow-hidden w-full border-b pl-2 pr-2 flex gap-2 bg-surface-50 dark:bg-surface-900", defaultBorderMixin)}>
455
456
 
456
457
  {barActions?.({
457
458
  path: fullIdPath ?? path,
@@ -28,7 +28,8 @@ import {
28
28
  useCustomizationController,
29
29
  useFireCMSContext,
30
30
  useSideEntityController,
31
- useSnackbarController
31
+ useSnackbarController,
32
+ useTranslation
32
33
  } from "../hooks";
33
34
  import { EntityFormActionsProps } from "../form/EntityFormActions";
34
35
  import { SideDialogController, useSideDialogContext } from "./SideDialogs";
@@ -56,6 +57,7 @@ export function EntityEditViewFormActions({
56
57
  const sideEntityController = useSideEntityController();
57
58
  const sideDialogContext = useSideDialogContext();
58
59
  const customizationController = useCustomizationController();
60
+ const { t } = useTranslation();
59
61
 
60
62
  const entityActions = useMemo((): EntityAction[] => {
61
63
  const customEntityActions = (collection.entityActions ?? [])
@@ -90,7 +92,8 @@ export function EntityEditViewFormActions({
90
92
  openEntityMode,
91
93
  navigateBack,
92
94
  formContext,
93
- formex
95
+ formex,
96
+ t
94
97
  })
95
98
  : buildSideActions({
96
99
  savingError,
@@ -106,7 +109,8 @@ export function EntityEditViewFormActions({
106
109
  openEntityMode,
107
110
  navigateBack,
108
111
  formContext,
109
- formex
112
+ formex,
113
+ t
110
114
  });
111
115
  }
112
116
 
@@ -123,8 +127,9 @@ type ActionsViewProps<M extends object> = {
123
127
  pluginActions?: React.ReactNode[],
124
128
  openEntityMode: "side_panel" | "full_screen";
125
129
  navigateBack: () => void;
126
- formContext: FormContext,
130
+ formContext: FormContext;
127
131
  formex: FormexController<any>;
132
+ t: any;
128
133
  };
129
134
 
130
135
  function buildBottomActions<M extends object>({
@@ -141,7 +146,8 @@ function buildBottomActions<M extends object>({
141
146
  openEntityMode,
142
147
  navigateBack,
143
148
  formContext,
144
- formex
149
+ formex,
150
+ t
145
151
  }: ActionsViewProps<M>) {
146
152
 
147
153
  const hasErrors = Object.keys(formex.errors).length > 0 && formex.submitCount > 0;
@@ -191,7 +197,7 @@ function buildBottomActions<M extends object>({
191
197
  color="primary"
192
198
  disabled={disabled || formex.isSubmitting}
193
199
  type="reset">
194
- {status === "existing" ? "Discard" : "Clear"}
200
+ {status === "existing" ? t("discard") : t("clear")}
195
201
  </Button>
196
202
  <Button variant={canClose ? "text" : "filled"}
197
203
  color="primary"
@@ -200,9 +206,9 @@ function buildBottomActions<M extends object>({
200
206
  onClick={() => {
201
207
  sideDialogContext.setPendingClose(false);
202
208
  }}>
203
- {status === "existing" && "Save"}
204
- {status === "copy" && "Create copy"}
205
- {status === "new" && "Create"}
209
+ {status === "existing" && t("save")}
210
+ {status === "copy" && t("create_copy")}
211
+ {status === "new" && t("create")}
206
212
  </Button>
207
213
  {canClose && <LoadingButton variant="filled"
208
214
  color="primary"
@@ -212,9 +218,9 @@ function buildBottomActions<M extends object>({
212
218
  onClick={() => {
213
219
  sideDialogContext.setPendingClose?.(true);
214
220
  }}>
215
- {status === "existing" && "Save and close"}
216
- {status === "copy" && "Create copy and close"}
217
- {status === "new" && "Create and close"}
221
+ {status === "existing" && t("save_and_close")}
222
+ {status === "copy" && t("create_copy_and_close")}
223
+ {status === "new" && t("create_and_close")}
218
224
  </LoadingButton>}
219
225
  </DialogActions>;
220
226
  }
@@ -233,7 +239,8 @@ function buildSideActions<M extends object>({
233
239
  openEntityMode,
234
240
  navigateBack,
235
241
  formContext,
236
- formex
242
+ formex,
243
+ t
237
244
  }: ActionsViewProps<M>) {
238
245
 
239
246
  const hasErrors = Object.keys(formex.errors).length > 0 && formex.submitCount > 0;
@@ -249,13 +256,13 @@ function buildSideActions<M extends object>({
249
256
  onClick={() => {
250
257
  sideDialogContext.setPendingClose?.(false);
251
258
  }}>
252
- {status === "existing" && "Save"}
253
- {status === "copy" && "Create copy"}
254
- {status === "new" && "Create"}
259
+ {status === "existing" && t("save")}
260
+ {status === "copy" && t("create_copy")}
261
+ {status === "new" && t("create")}
255
262
  </LoadingButton>
256
263
 
257
264
  <Button fullWidth={true} variant="text" disabled={disabled || formex.isSubmitting} type="reset">
258
- {status === "existing" ? "Discard" : "Clear"}
265
+ {status === "existing" ? t("discard") : t("clear")}
259
266
  </Button>
260
267
 
261
268
  {pluginActions}
@@ -1,7 +1,7 @@
1
1
  import React, { useCallback, useEffect, useMemo } from "react";
2
2
 
3
3
  import { EntityCollection, EntitySidePanelProps } from "../types";
4
- import { useNavigationController, useSideEntityController } from "../hooks";
4
+ import { useNavigationController, useSideEntityController, useTranslation } from "../hooks";
5
5
 
6
6
  import { ErrorBoundary } from "../components";
7
7
  import { EntityEditView, OnUpdateParams } from "./EntityEditView";
@@ -41,6 +41,7 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
41
41
  const sideEntityController = useSideEntityController();
42
42
  const navigationController = useNavigationController();
43
43
  const sideDialogsController = useSideDialogContext();
44
+ const { t } = useTranslation();
44
45
 
45
46
  const onClose = () => {
46
47
  if (props.onClose) {
@@ -99,10 +100,10 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
99
100
 
100
101
  const onValuesModified = useCallback((modified: boolean) => {
101
102
  setBlockedNavigationMessage(modified
102
- ? <> You have unsaved changes in this <b>{collection?.singularName ?? collection?.name}</b>.</>
103
+ ? t("unsaved_changes", { collectionName: collection?.singularName ?? collection?.name ?? "" })
103
104
  : undefined)
104
105
  setBlocked(modified);
105
- }, [collection?.name, setBlocked, setBlockedNavigationMessage]);
106
+ }, [collection?.name, setBlocked, setBlockedNavigationMessage, t]);
106
107
 
107
108
  if (!props || !collection) {
108
109
  return <div className={"w-full"} />;
@@ -136,9 +137,9 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
136
137
  const key = (status === "new" || status === "copy") ? path + "#new" : path + "/" + entityId;
137
138
  saveEntityToMemoryCache(key, values);
138
139
  if (entityId)
139
- navigate(location.pathname);
140
+ navigate(location.pathname + location.search);
140
141
  else
141
- navigate(location.pathname + "#new");
142
+ navigate(location.pathname + location.search + "#new");
142
143
  }}>
143
144
  <OpenInFullIcon size={"smallest"} />
144
145
  </IconButton>}
@@ -1,11 +1,13 @@
1
1
  "use client";
2
2
 
3
- import React, { useMemo } from "react";
3
+ import React, { useEffect, useMemo } from "react";
4
4
  import { CenteredView, Typography } from "@firecms/ui";
5
+ import { AuthController } from "../types";
5
6
  import { CustomizationController, FireCMSContext, FireCMSPlugin, FireCMSProps, User } from "../types";
6
- import { AuthControllerContext } from "../contexts";
7
+ import { AuthControllerContext, ModeControllerProvider } from "../contexts";
7
8
  import { useBuildSideEntityController } from "../internal/useBuildSideEntityController";
8
- import { useCustomizationController, useFireCMSContext } from "../hooks";
9
+ import { useCustomizationController, useFireCMSContext, useTranslation, ModeController } from "../hooks";
10
+ import { useBuildModeController } from "../hooks/useBuildModeController";
9
11
  import { useBuildSideDialogsController } from "../internal/useBuildSideDialogsController";
10
12
  import { ErrorView } from "../components";
11
13
  import { StorageSourceContext } from "../contexts/StorageSourceContext";
@@ -59,6 +61,10 @@ export function FireCMS<USER extends User>(props: FireCMSProps<USER>) {
59
61
  console.warn("The `plugins` prop is deprecated in the FireCMS component. You should pass your plugins to `useBuildNavigationController` instead.");
60
62
  }
61
63
 
64
+ const { t, i18n } = useTranslation();
65
+
66
+ const modeController = useBuildModeController();
67
+
62
68
  const plugins = navigationController.plugins ?? _pluginsProp;
63
69
  const userManagement = plugins?.find(p => p.userManagement)?.userManagement
64
70
  ?? _userManagement
@@ -106,6 +112,24 @@ export function FireCMS<USER extends User>(props: FireCMSProps<USER>) {
106
112
  authController
107
113
  });
108
114
 
115
+ // Inject plugin translations into the existing i18next instance
116
+ useEffect(() => {
117
+ if (!i18n) return;
118
+ plugins?.forEach(plugin => {
119
+ if (plugin.i18n) {
120
+ Object.keys(plugin.i18n).forEach(locale => {
121
+ i18n.addResourceBundle(
122
+ locale,
123
+ "firecms_core",
124
+ plugin.i18n![locale],
125
+ true, // deep merge
126
+ true // overwrite
127
+ );
128
+ });
129
+ }
130
+ });
131
+ }, [i18n, plugins]);
132
+
109
133
  if (accessResponse?.message) {
110
134
  console.warn(accessResponse.message);
111
135
  }
@@ -133,12 +157,15 @@ export function FireCMS<USER extends User>(props: FireCMSProps<USER>) {
133
157
  if (accessResponse?.blocked) {
134
158
  return (
135
159
  <CenteredView maxWidth={"md"} fullScreen={true} className={"flex flex-col gap-2"}>
160
+ {/* eslint-disable-next-line i18next/no-literal-string */}
136
161
  <Typography variant={"h4"} gutterBottom>
137
- License needed
162
+ {t("license_needed")}
138
163
  </Typography>
139
164
  <Typography>
140
- You need a valid license to use FireCMS PRO. Please reach out at <a
141
- href={"mailto:hello@firecms.co"}>hello@firecms.co</a> for more information.
165
+ {(() => {
166
+ const parts = t("license_description", { email: "%%EMAIL%%" }).split("%%EMAIL%%");
167
+ return <>{parts[0]}<a href={"mailto:hello@firecms.co"}>hello@firecms.co</a>{parts[1]}</>;
168
+ })()}
142
169
  </Typography>
143
170
  {accessResponse?.message &&
144
171
  <Typography>{accessResponse?.message}</Typography>}