@firecms/core 3.1.0-canary.9e89e98 → 3.1.0-canary.a3b8228

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 (216) hide show
  1. package/dist/components/EntityCollectionTable/internal/popup_field/useDraggable.d.ts +2 -2
  2. package/dist/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
  3. package/dist/components/ErrorBoundary.d.ts +4 -2
  4. package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
  5. package/dist/components/LanguageToggle.d.ts +1 -0
  6. package/dist/components/UnsavedChangesDialog.d.ts +1 -0
  7. package/dist/components/VirtualTable/VirtualTableHeader.d.ts +1 -1
  8. package/dist/components/index.d.ts +1 -0
  9. package/dist/core/DrawerNavigationGroup.d.ts +2 -2
  10. package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
  11. package/dist/editor/components/editor-bubble-item.d.ts +8 -0
  12. package/dist/editor/components/editor-bubble.d.ts +8 -0
  13. package/dist/editor/components/image-bubble.d.ts +5 -0
  14. package/dist/editor/components/index.d.ts +16 -0
  15. package/dist/editor/components/table-bubble.d.ts +5 -0
  16. package/dist/editor/editor.d.ts +30 -0
  17. package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
  18. package/dist/editor/extensions/Image/index.d.ts +6 -0
  19. package/dist/editor/extensions/Image.d.ts +6 -0
  20. package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
  21. package/dist/editor/extensions/clipboard.d.ts +7 -0
  22. package/dist/editor/extensions/custom-keymap.d.ts +1 -0
  23. package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
  24. package/dist/editor/hooks/useProseMirror.d.ts +13 -0
  25. package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
  26. package/dist/editor/index.d.ts +2 -0
  27. package/dist/editor/markdown.d.ts +5 -0
  28. package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
  29. package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
  30. package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
  31. package/dist/editor/nodeViews/index.d.ts +6 -0
  32. package/dist/editor/plugins/index.d.ts +2 -0
  33. package/dist/editor/plugins/inputrules.d.ts +6 -0
  34. package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
  35. package/dist/editor/plugins/slashCommandPlugin.d.ts +12 -0
  36. package/dist/editor/schema.d.ts +2 -0
  37. package/dist/editor/selectors/ai-selector.d.ts +0 -0
  38. package/dist/editor/selectors/color-selector.d.ts +10 -0
  39. package/dist/editor/selectors/link-selector.d.ts +8 -0
  40. package/dist/editor/selectors/node-selector.d.ts +15 -0
  41. package/dist/editor/selectors/text-buttons.d.ts +1 -0
  42. package/dist/editor/types.d.ts +5 -0
  43. package/dist/editor/useProseMirror.d.ts +16 -0
  44. package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
  45. package/dist/editor/utils/remove_classes.d.ts +1 -0
  46. package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
  47. package/dist/form/components/ErrorFocus.d.ts +1 -1
  48. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  49. package/dist/hooks/index.d.ts +1 -0
  50. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  51. package/dist/hooks/useCollapsedGroups.d.ts +3 -3
  52. package/dist/hooks/useTranslation.d.ts +17 -0
  53. package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
  54. package/dist/index.d.ts +4 -0
  55. package/dist/index.es.js +13009 -2312
  56. package/dist/index.es.js.map +1 -1
  57. package/dist/index.umd.js +12997 -2320
  58. package/dist/index.umd.js.map +1 -1
  59. package/dist/internal/useRestoreScroll.d.ts +1 -1
  60. package/dist/locales/de.d.ts +2 -0
  61. package/dist/locales/en.d.ts +10 -0
  62. package/dist/locales/es.d.ts +10 -0
  63. package/dist/locales/fr.d.ts +2 -0
  64. package/dist/locales/hi.d.ts +2 -0
  65. package/dist/locales/it.d.ts +2 -0
  66. package/dist/locales/pt.d.ts +7 -0
  67. package/dist/types/analytics.d.ts +1 -1
  68. package/dist/types/customization_controller.d.ts +2 -1
  69. package/dist/types/firecms.d.ts +2 -1
  70. package/dist/types/index.d.ts +1 -0
  71. package/dist/types/navigation.d.ts +2 -2
  72. package/dist/types/plugins.d.ts +23 -0
  73. package/dist/types/storage.d.ts +1 -0
  74. package/dist/types/translations.d.ts +646 -0
  75. package/dist/util/entities.d.ts +1 -1
  76. package/dist/util/resolutions.d.ts +2 -2
  77. package/dist/util/useStorageUploadController.d.ts +10 -1
  78. package/package.json +49 -13
  79. package/src/app/Scaffold.tsx +7 -5
  80. package/src/components/AIIcon.tsx +3 -1
  81. package/src/components/ArrayContainer.tsx +6 -4
  82. package/src/components/ClearFilterSortButton.tsx +6 -3
  83. package/src/components/ConfirmationDialog.tsx +4 -2
  84. package/src/components/DeleteEntityDialog.tsx +10 -7
  85. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
  86. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
  87. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +1 -1
  88. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
  89. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +11 -11
  90. package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
  91. package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
  92. package/src/components/EntityCollectionView/EntityBoardCard.tsx +1 -1
  93. package/src/components/EntityCollectionView/EntityCard.tsx +4 -0
  94. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +39 -46
  95. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
  96. package/src/components/EntityCollectionView/EntityCollectionView.tsx +54 -17
  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/EntityView.tsx +3 -2
  102. package/src/components/ErrorBoundary.tsx +27 -15
  103. package/src/components/HomePage/DefaultHomePage.tsx +19 -13
  104. package/src/components/HomePage/HomePageDnD.tsx +3 -1
  105. package/src/components/HomePage/NavigationGroup.tsx +3 -1
  106. package/src/components/HomePage/RenameGroupDialog.tsx +15 -13
  107. package/src/components/LanguageToggle.tsx +66 -0
  108. package/src/components/NotFoundPage.tsx +5 -3
  109. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -7
  110. package/src/components/ReferenceWidget.tsx +3 -2
  111. package/src/components/SearchIconsView.tsx +3 -1
  112. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +11 -0
  113. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +15 -2
  114. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +11 -0
  115. package/src/components/UnsavedChangesDialog.tsx +6 -4
  116. package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
  117. package/src/components/VirtualTable/VirtualTable.tsx +116 -113
  118. package/src/components/VirtualTable/VirtualTableHeader.tsx +54 -52
  119. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +1 -1
  120. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +3 -3
  121. package/src/components/common/default_entity_actions.tsx +4 -0
  122. package/src/components/common/useDataSourceTableController.tsx +12 -4
  123. package/src/components/index.tsx +1 -0
  124. package/src/core/DefaultAppBar.tsx +15 -11
  125. package/src/core/DefaultDrawer.tsx +8 -2
  126. package/src/core/DrawerNavigationGroup.tsx +5 -3
  127. package/src/core/EntityEditView.tsx +4 -3
  128. package/src/core/EntityEditViewFormActions.tsx +24 -17
  129. package/src/core/EntitySidePanel.tsx +32 -29
  130. package/src/core/FireCMS.tsx +33 -6
  131. package/src/core/field_configs.tsx +14 -9
  132. package/src/editor/components/SlashCommandMenu.tsx +516 -0
  133. package/src/editor/components/editor-bubble-item.tsx +32 -0
  134. package/src/editor/components/editor-bubble.tsx +118 -0
  135. package/src/editor/components/image-bubble.tsx +156 -0
  136. package/src/editor/components/index.ts +14 -0
  137. package/src/editor/components/table-bubble.tsx +165 -0
  138. package/src/editor/editor.tsx +455 -0
  139. package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
  140. package/src/editor/extensions/Image/index.ts +133 -0
  141. package/src/editor/extensions/Image.ts +159 -0
  142. package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
  143. package/src/editor/extensions/clipboard.ts +72 -0
  144. package/src/editor/extensions/custom-keymap.ts +24 -0
  145. package/src/editor/extensions/drag-and-drop.tsx +480 -0
  146. package/src/editor/hooks/useProseMirror.ts +124 -0
  147. package/src/editor/hooks/useProseMirrorContext.ts +15 -0
  148. package/src/editor/index.ts +2 -0
  149. package/src/editor/markdown.ts +172 -0
  150. package/src/editor/nodeViews/ImageComponent.tsx +20 -0
  151. package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
  152. package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
  153. package/src/editor/nodeViews/index.ts +35 -0
  154. package/src/editor/plugins/index.ts +58 -0
  155. package/src/editor/plugins/inputrules.ts +82 -0
  156. package/src/editor/plugins/placeholderPlugin.ts +55 -0
  157. package/src/editor/plugins/slashCommandPlugin.ts +61 -0
  158. package/src/editor/schema.ts +240 -0
  159. package/src/editor/selectors/ai-selector.tsx +111 -0
  160. package/src/editor/selectors/color-selector.tsx +200 -0
  161. package/src/editor/selectors/link-selector.tsx +118 -0
  162. package/src/editor/selectors/node-selector.tsx +157 -0
  163. package/src/editor/selectors/text-buttons.tsx +86 -0
  164. package/src/editor/types.ts +6 -0
  165. package/src/editor/useProseMirror.ts +126 -0
  166. package/src/editor/utils/prosemirror-utils.ts +108 -0
  167. package/src/editor/utils/remove_classes.ts +17 -0
  168. package/src/editor/utils/useDebouncedCallback.ts +25 -0
  169. package/src/form/EntityForm.tsx +85 -63
  170. package/src/form/EntityFormActions.tsx +19 -12
  171. package/src/form/PropertyFieldBinding.tsx +6 -5
  172. package/src/form/components/ErrorFocus.tsx +3 -3
  173. package/src/form/components/LocalChangesMenu.tsx +13 -13
  174. package/src/form/components/StorageItemPreview.tsx +3 -2
  175. package/src/form/components/StorageUploadProgress.tsx +18 -3
  176. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -4
  177. package/src/form/field_bindings/BlockFieldBinding.tsx +5 -2
  178. package/src/form/field_bindings/KeyValueFieldBinding.tsx +23 -18
  179. package/src/form/field_bindings/MapFieldBinding.tsx +4 -3
  180. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +34 -20
  181. package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -1
  182. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +87 -86
  183. package/src/hooks/index.tsx +1 -0
  184. package/src/hooks/useBuildNavigationController.tsx +49 -22
  185. package/src/hooks/useCollapsedGroups.ts +7 -6
  186. package/src/hooks/useTranslation.ts +31 -0
  187. package/src/hooks/useValidateAuthenticator.tsx +1 -1
  188. package/src/i18n/FireCMSi18nProvider.tsx +160 -0
  189. package/src/index.ts +4 -0
  190. package/src/internal/useBuildDataSource.ts +1 -2
  191. package/src/internal/useBuildSideEntityController.tsx +22 -20
  192. package/src/locales/de.ts +691 -0
  193. package/src/locales/en.ts +703 -0
  194. package/src/locales/es.ts +703 -0
  195. package/src/locales/fr.ts +691 -0
  196. package/src/locales/hi.ts +691 -0
  197. package/src/locales/it.ts +691 -0
  198. package/src/locales/pt.ts +700 -0
  199. package/src/preview/PropertyPreview.tsx +1 -0
  200. package/src/preview/components/UrlComponentPreview.tsx +4 -2
  201. package/src/preview/components/UserPreview.tsx +3 -1
  202. package/src/types/analytics.ts +10 -0
  203. package/src/types/customization_controller.tsx +2 -1
  204. package/src/types/firecms.tsx +2 -1
  205. package/src/types/index.ts +1 -0
  206. package/src/types/navigation.ts +2 -2
  207. package/src/types/plugins.tsx +26 -0
  208. package/src/types/properties.ts +1 -0
  209. package/src/types/storage.ts +2 -1
  210. package/src/types/translations.ts +725 -0
  211. package/src/util/entities.ts +1 -1
  212. package/src/util/join_collections.ts +10 -8
  213. package/src/util/previews.ts +2 -2
  214. package/src/util/property_utils.tsx +1 -1
  215. package/src/util/resolutions.ts +5 -3
  216. package/src/util/useStorageUploadController.tsx +23 -29
@@ -12,6 +12,7 @@ import {
12
12
  ViewColumnIcon,
13
13
  ViewKanbanIcon
14
14
  } from "@firecms/ui";
15
+ import { useTranslation } from "../../hooks/useTranslation";
15
16
 
16
17
  export type KanbanPropertyOption = {
17
18
  key: string;
@@ -72,6 +73,8 @@ export function ViewModeToggle({
72
73
  onKanbanPropertyChange
73
74
  }: ViewModeToggleProps) {
74
75
 
76
+ const { t } = useTranslation();
77
+
75
78
  if (!onViewModeChange) {
76
79
  return null;
77
80
  }
@@ -84,9 +87,9 @@ export function ViewModeToggle({
84
87
  };
85
88
 
86
89
  const getViewModeName = () => {
87
- if (viewMode === "kanban") return "Board";
88
- if (viewMode === "cards") return "Cards";
89
- return "List";
90
+ if (viewMode === "kanban") return t("board");
91
+ if (viewMode === "cards") return t("cards");
92
+ return t("list");
90
93
  };
91
94
 
92
95
  const showSizeSelector = size && onSizeChanged && (viewMode === "table" || viewMode === "cards");
@@ -100,17 +103,17 @@ export function ViewModeToggle({
100
103
  const allOptions: ToggleButtonOption<ViewMode>[] = [
101
104
  {
102
105
  value: "table",
103
- label: "List",
106
+ label: t("list"),
104
107
  icon: <ListIcon size="small" />
105
108
  },
106
109
  {
107
110
  value: "cards",
108
- label: "Cards",
111
+ label: t("cards"),
109
112
  icon: <AppsIcon size="small" />
110
113
  },
111
114
  {
112
115
  value: "kanban",
113
- label: "Board",
116
+ label: t("board"),
114
117
  icon: <ViewKanbanIcon size="small" />
115
118
  }
116
119
  ];
@@ -150,7 +153,7 @@ export function ViewModeToggle({
150
153
  <div className="flex flex-row items-center justify-between gap-2">
151
154
  <div className="flex items-center gap-2 text-sm text-surface-600 dark:text-surface-300">
152
155
  <ViewColumnIcon size="small" />
153
- <span>Size</span>
156
+ <span>{t("size_label")}</span>
154
157
  </div>
155
158
  <Select
156
159
  value={size}
@@ -173,7 +176,7 @@ export function ViewModeToggle({
173
176
  <div className="flex flex-row items-center justify-between gap-2">
174
177
  <div className="flex items-center gap-2 text-sm text-surface-600 dark:text-surface-300">
175
178
  <ViewKanbanIcon size="small" />
176
- <span>Group by</span>
179
+ <span>{t("group_by")}</span>
177
180
  </div>
178
181
  <Select
179
182
  value={selectedKanbanProperty}
@@ -4,7 +4,7 @@ import { resolveCollection } from "../util";
4
4
  import { cls, defaultBorderMixin, IconButton, OpenInNewIcon, Typography } from "@firecms/ui";
5
5
  import { CustomizationController } from "../types/customization_controller";
6
6
  import { useCustomizationController } from "../hooks/useCustomizationController";
7
- import { useAuthController } from "../hooks";
7
+ import { useAuthController, useTranslation } from "../hooks";
8
8
  import { PropertyCollectionView } from "./PropertyCollectionView";
9
9
 
10
10
  /**
@@ -26,6 +26,7 @@ export function EntityView<M extends Record<string, any>>(
26
26
  }: EntityViewProps<M>) {
27
27
 
28
28
  const authController = useAuthController();
29
+ const { t } = useTranslation();
29
30
  const customizationController: CustomizationController = useCustomizationController();
30
31
  const resolvedCollection: ResolvedEntityCollection<M> = useMemo(() => resolveCollection<M>({
31
32
  collection,
@@ -48,7 +49,7 @@ export function EntityView<M extends Record<string, any>>(
48
49
  color={"secondary"}
49
50
  component={"span"}
50
51
  className="break-words">
51
- Id
52
+ {t("id")}
52
53
  </Typography>
53
54
  </div>
54
55
  <div className="col-span-8">
@@ -1,18 +1,20 @@
1
1
  import React, { ErrorInfo, PropsWithChildren } from "react";
2
+ import { useTranslation } from "../hooks/useTranslation";
2
3
 
3
4
  import { ErrorIcon, Typography } from "@firecms/ui";
4
5
 
5
6
  export class ErrorBoundary extends React.Component<PropsWithChildren<Record<string, unknown>>, {
6
- error: Error | null
7
+ hasError: boolean,
8
+ error?: Error
7
9
  }> {
8
10
  constructor(props: any) {
9
11
  super(props);
10
- this.state = { error: null };
12
+ this.state = { hasError: false };
11
13
  }
12
14
 
13
15
  // eslint-disable-next-line n/handle-callback-err
14
16
  static getDerivedStateFromError(error: Error) {
15
- return { error };
17
+ return { hasError: true, error };
16
18
  }
17
19
 
18
20
  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
@@ -21,20 +23,30 @@ export class ErrorBoundary extends React.Component<PropsWithChildren<Record<stri
21
23
  }
22
24
 
23
25
  render() {
24
- if (this.state.error) {
25
- return (
26
- <div className="flex flex-col m-2">
27
- <div className="flex items-center m-2">
28
- <ErrorIcon color={"error"} size={"small"}/>
29
- <div className="ml-4">Error</div>
30
- </div>
31
- <Typography variant={"caption"}>
32
- {this.state.error?.message ?? "See the error in the console"}
33
- </Typography>
34
- </div>
35
- );
26
+ if (this.state.hasError) {
27
+ // You can render any custom fallback UI
28
+ return <FallbackView message={this.state.error?.message}/>;
36
29
  }
37
30
 
38
31
  return this.props.children;
39
32
  }
40
33
  }
34
+
35
+ function FallbackView({ message }: { message?: string }) {
36
+ const { t } = useTranslation();
37
+ return (
38
+ <div className="h-full w-full bg-slate-100 flex items-center justify-center p-4">
39
+ <div
40
+ className="flex flex-col items-center justify-center m-4 bg-white dark:bg-gray-800 p-8 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
41
+ <div className="flex items-center mb-4 text-red-500">
42
+ <ErrorIcon/>
43
+ <div className="ml-4">{t("error")}</div>
44
+ </div>
45
+ <div className="flex justify-center text-gray-500 dark:text-gray-400">
46
+ {/* Error message is purposely removed since it's hard to access state here, but typical ErrorBoundary fallback doesn't always show the raw message */}
47
+ {t("see_console_details")}
48
+ </div>
49
+ </div>
50
+ </div>
51
+ );
52
+ }
@@ -30,8 +30,13 @@ import { NavigationCardBinding } from "./NavigationCardBinding";
30
30
  import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
31
31
  import { restrictToVerticalAxis, restrictToWindowEdges } from "@dnd-kit/modifiers";
32
32
  import { RenameGroupDialog } from "./RenameGroupDialog";
33
+ import { useTranslation } from "../../hooks/useTranslation";
33
34
 
34
- export const DEFAULT_GROUP_NAME = "Views";
35
+ /**
36
+ * Internal sentinel key for ungrouped navigation entries.
37
+ * Not displayed — display components use t("views_group") when group is undefined.
38
+ */
39
+ const DEFAULT_GROUP_KEY = "__default__";
35
40
  export const ADMIN_GROUP_NAME = "Admin";
36
41
 
37
42
  export function DefaultHomePage({
@@ -47,6 +52,7 @@ export function DefaultHomePage({
47
52
  const context = useFireCMSContext();
48
53
  const customizationController = useCustomizationController();
49
54
  const navigationController = useNavigationController();
55
+ const { t } = useTranslation();
50
56
 
51
57
  if (!navigationController.topLevelNavigation)
52
58
  throw Error("Navigation not ready");
@@ -108,7 +114,7 @@ export function DefaultHomePage({
108
114
  const g =
109
115
  e.type === "admin"
110
116
  ? ADMIN_GROUP_NAME
111
- : e.group ?? DEFAULT_GROUP_NAME;
117
+ : e.group ?? DEFAULT_GROUP_KEY;
112
118
  (entriesByGroup[g] ??= []).push(e);
113
119
  });
114
120
 
@@ -119,7 +125,7 @@ export function DefaultHomePage({
119
125
 
120
126
  if (performingSearch) {
121
127
  const ordered = [
122
- ...new Set(src.map((e) => e.group ?? DEFAULT_GROUP_NAME))
128
+ ...new Set(src.map((e) => e.group ?? DEFAULT_GROUP_KEY))
123
129
  ];
124
130
  allProcessed = ordered
125
131
  .map((name) => ({
@@ -129,11 +135,11 @@ export function DefaultHomePage({
129
135
  .filter((g) => g.entries.length);
130
136
  } else {
131
137
  allProcessed = groupOrderFromNavController.map((g) => ({
132
- name: g,
133
- entries: entriesByGroup[g] || []
138
+ name: g ?? DEFAULT_GROUP_KEY,
139
+ entries: entriesByGroup[g ?? DEFAULT_GROUP_KEY] || []
134
140
  }));
135
141
  Object.keys(entriesByGroup).forEach((g) => {
136
- if (!groupOrderFromNavController.includes(g))
142
+ if (!groupOrderFromNavController.map(x => x ?? DEFAULT_GROUP_KEY).includes(g))
137
143
  allProcessed.push({
138
144
  name: g,
139
145
  entries: entriesByGroup[g]
@@ -141,9 +147,9 @@ export function DefaultHomePage({
141
147
  });
142
148
 
143
149
  // Ensure default group exists if there are plugin additional cards but no collections
144
- if (hasPluginAdditionalCards && !allProcessed.some(g => g.name === DEFAULT_GROUP_NAME)) {
150
+ if (hasPluginAdditionalCards && !allProcessed.some(g => g.name === DEFAULT_GROUP_KEY)) {
145
151
  allProcessed.push({
146
- name: DEFAULT_GROUP_NAME,
152
+ name: DEFAULT_GROUP_KEY,
147
153
  entries: []
148
154
  });
149
155
  }
@@ -151,7 +157,7 @@ export function DefaultHomePage({
151
157
  allProcessed = allProcessed.filter(
152
158
  (g) =>
153
159
  g.entries.length ||
154
- (g.name === DEFAULT_GROUP_NAME && hasPluginAdditionalCards)
160
+ (g.name === DEFAULT_GROUP_KEY && hasPluginAdditionalCards)
155
161
  );
156
162
  }
157
163
 
@@ -362,7 +368,7 @@ export function DefaultHomePage({
362
368
  >
363
369
  <SearchBar
364
370
  onTextSearch={updateSearch}
365
- placeholder="Search collections"
371
+ placeholder={t("search_collections")}
366
372
  autoFocus
367
373
  innerClassName="w-full"
368
374
  className="w-full flex-grow"
@@ -412,7 +418,7 @@ export function DefaultHomePage({
412
418
 
413
419
  const actionProps: PluginHomePageAdditionalCardsProps = {
414
420
  group:
415
- groupKey === DEFAULT_GROUP_NAME
421
+ groupKey === DEFAULT_GROUP_KEY
416
422
  ? undefined
417
423
  : groupKey,
418
424
  context
@@ -432,7 +438,7 @@ export function DefaultHomePage({
432
438
  >
433
439
  <NavigationGroup
434
440
  group={
435
- groupKey === DEFAULT_GROUP_NAME
441
+ groupKey === DEFAULT_GROUP_KEY
436
442
  ? undefined
437
443
  : groupKey
438
444
  }
@@ -535,7 +541,7 @@ export function DefaultHomePage({
535
541
  <NavigationGroup
536
542
  group={
537
543
  activeGroupData.name ===
538
- DEFAULT_GROUP_NAME
544
+ DEFAULT_GROUP_KEY
539
545
  ? undefined
540
546
  : activeGroupData.name
541
547
  }
@@ -30,6 +30,7 @@ import { CSS } from "@dnd-kit/utilities";
30
30
  import { NavigationCardBinding } from "./NavigationCardBinding";
31
31
  import { NavigationEntry } from "../../types";
32
32
  import { cls, defaultBorderMixin } from "@firecms/ui";
33
+ import { useTranslation } from "../../hooks";
33
34
 
34
35
  const animateLayoutChanges: AnimateLayoutChanges = (args) =>
35
36
  defaultAnimateLayoutChanges({
@@ -669,6 +670,7 @@ export function NewGroupDropZone({
669
670
  disabled: boolean;
670
671
  setIsHovering: (v: boolean) => void;
671
672
  }) {
673
+ const { t } = useTranslation();
672
674
  const {
673
675
  setNodeRef,
674
676
  isOver
@@ -709,7 +711,7 @@ export function NewGroupDropZone({
709
711
  )}>
710
712
  <div className="text-center p-4">
711
713
  <span className="block font-medium text-sm">
712
- Drop here to create a new group
714
+ {t("drop_here_create_group")}
713
715
  </span>
714
716
  </div>
715
717
  </div>
@@ -1,5 +1,6 @@
1
1
  import React, { PropsWithChildren, useState } from "react";
2
2
  import { cls, EditIcon, IconButton, Typography, ExpandablePanel } from "@firecms/ui";
3
+ import { useTranslation } from "../../hooks/useTranslation";
3
4
 
4
5
  export function NavigationGroup({
5
6
  children,
@@ -22,8 +23,9 @@ export function NavigationGroup({
22
23
  onToggleCollapsed?: () => void;
23
24
  }>) {
24
25
 
26
+ const { t } = useTranslation();
25
27
  const [isHovered, setIsHovered] = useState(false);
26
- const currentGroupName = group ?? "Views";
28
+ const currentGroupName = group ?? t("views_group");
27
29
 
28
30
  // Show caret only when not in preview and there is a toggle handler
29
31
  const showCaret = !isPreview && !!onToggleCollapsed;
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect, useRef, useState } from "react";
2
2
  import { Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField } from "@firecms/ui";
3
+ import { useTranslation } from "../../hooks/useTranslation";
3
4
 
4
5
  interface RenameGroupDialogProps {
5
6
  open: boolean;
@@ -18,7 +19,8 @@ export function RenameGroupDialog({
18
19
  }: RenameGroupDialogProps) {
19
20
  const [name, setName] = useState(initialName);
20
21
  const [error, setError] = useState<string | null>(null);
21
- const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null); // Create a ref for the input
22
+ const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
23
+ const { t } = useTranslation();
22
24
 
23
25
  useEffect(() => {
24
26
  if (open) {
@@ -38,9 +40,9 @@ export function RenameGroupDialog({
38
40
  const newName = event.target.value;
39
41
  setName(newName);
40
42
  if (!newName.trim()) {
41
- setError("Group name cannot be empty.");
43
+ setError(t("group_name_empty_error"));
42
44
  } else if (existingGroupNames.includes(newName.trim())) {
43
- setError("This group name already exists.");
45
+ setError(t("group_name_exists_error"));
44
46
  } else {
45
47
  setError(null);
46
48
  }
@@ -49,11 +51,11 @@ export function RenameGroupDialog({
49
51
  const handleSave = () => {
50
52
  const trimmedName = name.trim();
51
53
  if (!trimmedName) {
52
- setError("Group name cannot be empty.");
54
+ setError(t("group_name_empty_error"));
53
55
  return;
54
56
  }
55
57
  if (existingGroupNames.includes(trimmedName)) {
56
- setError("This group name already exists.");
58
+ setError(t("group_name_exists_error"));
57
59
  return;
58
60
  }
59
61
  if (!error) {
@@ -70,9 +72,9 @@ export function RenameGroupDialog({
70
72
  // because the error state might not have updated if the user types and immediately hits enter.
71
73
  let currentError = null;
72
74
  if (!trimmedName) {
73
- currentError = "Group name cannot be empty.";
75
+ currentError = t("group_name_empty_error");
74
76
  } else if (existingGroupNames.includes(trimmedName)) {
75
- currentError = "This group name already exists.";
77
+ currentError = t("group_name_exists_error");
76
78
  }
77
79
 
78
80
  if (!currentError && trimmedName) {
@@ -93,14 +95,14 @@ export function RenameGroupDialog({
93
95
 
94
96
  return (
95
97
  <Dialog open={open}>
96
- <DialogTitle>Rename Group</DialogTitle>
98
+ <DialogTitle>{t("rename_group")}</DialogTitle>
97
99
  <DialogContent>
98
100
  <TextField
99
- inputRef={inputRef} // Pass the ref to the TextField
100
- label="Group Name"
101
+ inputRef={inputRef}
102
+ label={t("group_name_label")}
101
103
  value={name}
102
104
  onChange={handleNameChange}
103
- onKeyDown={handleKeyDown} // Added onKeyDown handler
105
+ onKeyDown={handleKeyDown}
104
106
  error={!!error}
105
107
  aria-describedby={error ? "group-name-error" : undefined}
106
108
  />
@@ -109,11 +111,11 @@ export function RenameGroupDialog({
109
111
  <DialogActions>
110
112
  <Button onClick={onClose}
111
113
  variant="text">
112
- Cancel
114
+ {t("cancel")}
113
115
  </Button>
114
116
  <Button onClick={handleSave}
115
117
  disabled={!!error || !name.trim()}>
116
- Save
118
+ {t("save")}
117
119
  </Button>
118
120
  </DialogActions>
119
121
  </Dialog>
@@ -0,0 +1,66 @@
1
+ import React from "react";
2
+ import { CheckIcon, IconButton, Menu, MenuItem, TranslateIcon, Typography } from "@firecms/ui";
3
+ import { useTranslation } from "../hooks";
4
+
5
+ export function LanguageToggle() {
6
+ const { i18n } = useTranslation();
7
+
8
+ return (
9
+ <Menu
10
+ trigger={<IconButton
11
+ color="inherit"
12
+ aria-label="Change language">
13
+ <TranslateIcon size="small" />
14
+ </IconButton>}>
15
+ <MenuItem onClick={() => i18n.changeLanguage("en")}>
16
+ <div className="flex w-full items-center justify-between gap-4">
17
+ {/* eslint-disable-next-line i18next/no-literal-string */}
18
+ <Typography variant="body2" className={i18n.language === "en" ? "font-bold" : ""}>English</Typography>
19
+ {i18n.language === "en" && <CheckIcon size="small" />}
20
+ </div>
21
+ </MenuItem>
22
+ <MenuItem onClick={() => i18n.changeLanguage("es")}>
23
+ <div className="flex w-full items-center justify-between gap-4">
24
+ {/* eslint-disable-next-line i18next/no-literal-string */}
25
+ <Typography variant="body2" className={i18n.language === "es" ? "font-bold" : ""}>Español</Typography>
26
+ {i18n.language === "es" && <CheckIcon size="small" />}
27
+ </div>
28
+ </MenuItem>
29
+ <MenuItem onClick={() => i18n.changeLanguage("de")}>
30
+ <div className="flex w-full items-center justify-between gap-4">
31
+ {/* eslint-disable-next-line i18next/no-literal-string */}
32
+ <Typography variant="body2" className={i18n.language === "de" ? "font-bold" : ""}>Deutsch</Typography>
33
+ {i18n.language === "de" && <CheckIcon size="small" />}
34
+ </div>
35
+ </MenuItem>
36
+ <MenuItem onClick={() => i18n.changeLanguage("fr")}>
37
+ <div className="flex w-full items-center justify-between gap-4">
38
+ {/* eslint-disable-next-line i18next/no-literal-string */}
39
+ <Typography variant="body2" className={i18n.language === "fr" ? "font-bold" : ""}>Français</Typography>
40
+ {i18n.language === "fr" && <CheckIcon size="small" />}
41
+ </div>
42
+ </MenuItem>
43
+ <MenuItem onClick={() => i18n.changeLanguage("it")}>
44
+ <div className="flex w-full items-center justify-between gap-4">
45
+ {/* eslint-disable-next-line i18next/no-literal-string */}
46
+ <Typography variant="body2" className={i18n.language === "it" ? "font-bold" : ""}>Italiano</Typography>
47
+ {i18n.language === "it" && <CheckIcon size="small" />}
48
+ </div>
49
+ </MenuItem>
50
+ <MenuItem onClick={() => i18n.changeLanguage("hi")}>
51
+ <div className="flex w-full items-center justify-between gap-4">
52
+ {/* eslint-disable-next-line i18next/no-literal-string */}
53
+ <Typography variant="body2" className={i18n.language === "hi" ? "font-bold" : ""}>हिन्दी</Typography>
54
+ {i18n.language === "hi" && <CheckIcon size="small" />}
55
+ </div>
56
+ </MenuItem>
57
+ <MenuItem onClick={() => i18n.changeLanguage("pt")}>
58
+ <div className="flex w-full items-center justify-between gap-4">
59
+ {/* eslint-disable-next-line i18next/no-literal-string */}
60
+ <Typography variant="body2" className={i18n.language === "pt" ? "font-bold" : ""}>Português</Typography>
61
+ {i18n.language === "pt" && <CheckIcon size="small" />}
62
+ </div>
63
+ </MenuItem>
64
+ </Menu>
65
+ );
66
+ }
@@ -1,8 +1,10 @@
1
1
  import React from "react";
2
2
  import { Link } from "react-router-dom";
3
3
  import { Button, Typography } from "@firecms/ui";
4
+ import { useTranslation } from "../hooks/useTranslation";
4
5
 
5
6
  export function NotFoundPage() {
7
+ const { t } = useTranslation();
6
8
 
7
9
  return (
8
10
  <div className="flex w-full h-full">
@@ -10,15 +12,15 @@ export function NotFoundPage() {
10
12
  >
11
13
  <Typography variant={"h4"} align={"center"}
12
14
  gutterBottom={true}>
13
- Page not found
15
+ {t("page_not_found")}
14
16
  </Typography>
15
17
  <Typography align={"center"} gutterBottom={true}>
16
- This page does not exist or you may not have access to it
18
+ {t("page_not_found_body")}
17
19
  </Typography>
18
20
  <Button
19
21
  variant={"text"}
20
22
  component={Link}
21
- to={"/"}>Back to home</Button>
23
+ to={"/"}>{t("back_to_home")}</Button>
22
24
  </div>
23
25
  </div>
24
26
  );
@@ -12,7 +12,8 @@ import {
12
12
  useDataSource,
13
13
  useLargeLayout,
14
14
  useNavigationController,
15
- useSideEntityController
15
+ useSideEntityController,
16
+ useTranslation
16
17
  } from "../../hooks";
17
18
  import { ErrorView } from "../ErrorView";
18
19
  import { AddIcon, Button, DialogActions, Typography } from "@firecms/ui";
@@ -101,6 +102,7 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
101
102
  maxSelection,
102
103
  }: ReferenceSelectionInnerProps<M>) {
103
104
 
105
+ const { t } = useTranslation();
104
106
  const authController = useAuthController();
105
107
  const sideDialogContext = useSideDialogContext();
106
108
  const sideEntityController = useSideEntityController();
@@ -302,8 +304,8 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
302
304
  collectionOrView={collection}
303
305
  className={"text-surface-300 dark:text-surface-600"}/>
304
306
  {collection.singularName
305
- ? `Select ${collection.singularName}`
306
- : `Select from ${collection.name}`}
307
+ ? t("select_specific", { name: collection.singularName })
308
+ : t("select_from", { name: collection.name })}
307
309
  </Typography>}
308
310
  defaultSize={collection.defaultSize}
309
311
  properties={resolvedCollection.properties}
@@ -327,7 +329,7 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
327
329
  <Button
328
330
  onClick={onDone}
329
331
  variant="filled">
330
- Done
332
+ {t("done")}
331
333
  </Button>
332
334
  </DialogActions>
333
335
  </div>
@@ -347,7 +349,7 @@ function ReferenceDialogActions({
347
349
  onClear: () => void,
348
350
  onNewClick: () => void
349
351
  }) {
350
-
352
+ const { t } = useTranslation();
351
353
  const authController = useAuthController();
352
354
 
353
355
  const largeLayout = useLargeLayout();
@@ -363,7 +365,7 @@ function ReferenceDialogActions({
363
365
  ? <Button
364
366
  onClick={onClick}
365
367
  startIcon={<AddIcon/>}>
366
- Add {collection.singularName ?? collection.name}
368
+ {t("add_specific", { name: collection.singularName ?? collection.name })}
367
369
  </Button>
368
370
  : <Button
369
371
  onClick={onClick}>
@@ -374,7 +376,7 @@ function ReferenceDialogActions({
374
376
  <>
375
377
  <Button onClick={onClear}
376
378
  variant={"text"}>
377
- Clear
379
+ {t("clear")}
378
380
  </Button>
379
381
  {addButton}
380
382
  </>
@@ -3,7 +3,7 @@ import React, { useCallback, useMemo } from "react";
3
3
  import { Entity, EntityCollection, EntityReference, FilterValues } from "../types";
4
4
  import { getReferenceFrom } from "../util";
5
5
  import { PreviewSize, ReferencePreview } from "../preview";
6
- import { useNavigationController, useReferenceDialog } from "../hooks";
6
+ import { useNavigationController, useReferenceDialog, useTranslation } from "../hooks";
7
7
  import { Button, cls } from "@firecms/ui";
8
8
 
9
9
  export type ReferenceWidgetProps<M extends Record<string, any>> = {
@@ -50,6 +50,7 @@ export function ReferenceWidget<M extends Record<string, any>>({
50
50
  includeEntityLink
51
51
  }: ReferenceWidgetProps<M>) {
52
52
 
53
+ const { t } = useTranslation();
53
54
  const navigationController = useNavigationController();
54
55
 
55
56
  const collection: EntityCollection | undefined = useMemo(() => {
@@ -145,7 +146,7 @@ export function ReferenceWidget<M extends Record<string, any>>({
145
146
  {!value && <div className="justify-center text-left">
146
147
  <Button disabled={disabled}
147
148
  onClick={onEntryClick}>
148
- Edit {name}
149
+ {t("edit_name", { name: name ?? "" })}
149
150
  </Button>
150
151
  </div>}
151
152
 
@@ -2,6 +2,7 @@ import React from "react";
2
2
 
3
3
  import { coolIconKeys, debounce, Icon, IconButton, iconKeys, SearchBar, Tooltip } from "@firecms/ui";
4
4
  import { iconSynonyms, iconsSearch } from "../util";
5
+ import { useTranslation } from "../hooks/useTranslation";
5
6
 
6
7
  const UPDATE_SEARCH_INDEX_WAIT_MS = 220;
7
8
 
@@ -22,6 +23,7 @@ export function SearchIconsView({
22
23
  selectedIcon = "",
23
24
  onIconSelected
24
25
  }: SearchIconsProps) {
26
+ const { t } = useTranslation();
25
27
  const [keys, setKeys] = React.useState<string[] | null>(null);
26
28
  const [query, setQuery] = React.useState<string>("");
27
29
 
@@ -53,7 +55,7 @@ export function SearchIconsView({
53
55
  autoFocus={false}
54
56
  innerClassName={"w-full sticky top-0 z-10"}
55
57
  onTextSearch={(value?: string) => setQuery(value ?? "")}
56
- placeholder="Search for more icons…"
58
+ placeholder={t("search_for_more_icons")}
57
59
  />
58
60
 
59
61
  <div className={"flex max-w-full flex-wrap mt-4"}>
@@ -48,6 +48,17 @@ export function DateTimeFilterField({
48
48
  const [operation, setOperation] = useState<VirtualTableWhereFilterOp | "is-null">(fieldOperation === "==" && fieldValue === null ? "is-null" : fieldOperation);
49
49
  const [internalValue, setInternalValue] = useState<Date | null | undefined>(fieldValue);
50
50
 
51
+ React.useEffect(() => {
52
+ if (value) {
53
+ const [op, val] = value;
54
+ setOperation(op === "==" && val === null ? "is-null" : op);
55
+ setInternalValue(val);
56
+ } else {
57
+ setOperation(possibleOperations[0]);
58
+ setInternalValue(undefined);
59
+ }
60
+ }, [value, possibleOperations[0]]);
61
+
51
62
  const isNullOperation = operation === "is-null";
52
63
 
53
64
  function updateFilter(op: VirtualTableWhereFilterOp | "is-null", val: Date | undefined | null) {