@firecms/core 3.1.0 → 3.2.0-canary.4c3b8f2

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
@@ -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) {
@@ -3,7 +3,7 @@ import { VirtualTableWhereFilterOp } from "../../VirtualTable";
3
3
  import { Entity, EntityCollection, EntityReference } from "../../../types";
4
4
  import { ReferencePreview } from "../../../preview";
5
5
  import { getReferenceFrom } from "../../../util";
6
- import { useNavigationController, useReferenceDialog } from "../../../hooks";
6
+ import { useNavigationController, useReferenceDialog, useTranslation } from "../../../hooks";
7
7
  import { Button, Checkbox, Label, Select, SelectItem } from "@firecms/ui";
8
8
 
9
9
  interface ReferenceFilterFieldProps {
@@ -44,6 +44,8 @@ export function ReferenceFilterField({
44
44
  setHidden
45
45
  }: ReferenceFilterFieldProps) {
46
46
 
47
+ const { t } = useTranslation();
48
+
47
49
  const possibleOperations: (keyof typeof operationLabels)[] = isArray
48
50
  ? ["array-contains"]
49
51
  : ["==", "!=", ">", "<", ">=", "<="];
@@ -58,6 +60,17 @@ export function ReferenceFilterField({
58
60
  const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
59
61
  const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined | null>(fieldValue);
60
62
 
63
+ React.useEffect(() => {
64
+ if (value) {
65
+ const [op, val] = value;
66
+ setOperation(op);
67
+ setInternalValue(val);
68
+ } else {
69
+ setOperation(possibleOperations[0] as VirtualTableWhereFilterOp);
70
+ setInternalValue(undefined);
71
+ }
72
+ }, [value, possibleOperations[0]]);
73
+
61
74
  const selectedEntityIds = internalValue
62
75
  ? (Array.isArray(internalValue) ? internalValue.map((ref) => {
63
76
  if (!(ref?.isEntityReference && ref?.isEntityReference())) {
@@ -192,7 +205,7 @@ export function ReferenceFilterField({
192
205
  updateFilter(operation, null);
193
206
  else updateFilter(operation, undefined);
194
207
  }} />
195
- Filter for null values
208
+ {t("filter_for_null_values")}
196
209
  </Label>}
197
210
 
198
211
  </div>
@@ -61,6 +61,17 @@ export function StringNumberFilterField({
61
61
  const [operation, setOperation] = useState<VirtualTableWhereFilterOp | "is-null">(fieldOperation === "==" && fieldValue === null ? "is-null" : fieldOperation);
62
62
  const [internalValue, setInternalValue] = useState<string | number | string[] | number[] | null | undefined>(fieldValue);
63
63
 
64
+ React.useEffect(() => {
65
+ if (value) {
66
+ const [op, val] = value;
67
+ setOperation(op === "==" && val === null ? "is-null" : op);
68
+ setInternalValue(val);
69
+ } else {
70
+ setOperation(possibleOperations[0]);
71
+ setInternalValue(undefined);
72
+ }
73
+ }, [value, possibleOperations[0]]);
74
+
64
75
  const isNullOperation = operation === "is-null";
65
76
 
66
77
  function updateFilter(op: VirtualTableWhereFilterOp | "is-null", val: string | number | string[] | number[] | null | undefined) {
@@ -1,4 +1,6 @@
1
+ import React from "react";
1
2
  import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography } from "@firecms/ui";
3
+ import { useTranslation } from "../hooks/useTranslation";
2
4
 
3
5
  export interface UnsavedChangesDialogProps {
4
6
  open: boolean;
@@ -15,7 +17,7 @@ export function UnsavedChangesDialog({
15
17
  body,
16
18
  title
17
19
  }: UnsavedChangesDialogProps) {
18
-
20
+ const { t } = useTranslation();
19
21
  return (
20
22
  <Dialog
21
23
  onEscapeKeyDown={() => {
@@ -29,15 +31,15 @@ export function UnsavedChangesDialog({
29
31
  {body}
30
32
 
31
33
  <Typography>
32
- Are you sure you want to leave this page?
34
+ {t("are_you_sure_leave")}
33
35
  </Typography>
34
36
 
35
37
  </DialogContent>
36
38
  <DialogActions>
37
39
  <Button variant="text"
38
- onClick={handleCancel} autoFocus> Cancel </Button>
40
+ onClick={handleCancel} autoFocus> {t("cancel")} </Button>
39
41
  <Button
40
- onClick={handleOk}> Ok </Button>
42
+ onClick={handleOk}> {t("ok")} </Button>
41
43
  </DialogActions>
42
44
  </Dialog>
43
45
  );
@@ -3,6 +3,7 @@
3
3
  * @jest-environment jsdom
4
4
  */
5
5
  import React from 'react';
6
+ /* eslint-disable i18next/no-literal-string */
6
7
  import { render, act } from '@testing-library/react';
7
8
  import { VirtualTable } from './VirtualTable';
8
9
  import { VirtualTableProps } from './VirtualTableProps';
@@ -2,6 +2,7 @@ import React, { RefObject, useCallback, useEffect, useState } from "react";
2
2
  import equal from "react-fast-compare";
3
3
 
4
4
  import { VirtualTableColumn, VirtualTableSort, VirtualTableWhereFilterOp } from "./VirtualTableProps";
5
+ import { useTranslation } from "../../hooks";
5
6
  import { ErrorBoundary } from "../ErrorBoundary";
6
7
  import {
7
8
  ArrowUpwardIcon,
@@ -213,6 +214,8 @@ function FilterForm<M>({
213
214
  setHidden
214
215
  }: FilterFormProps<M>) {
215
216
 
217
+ const { t } = useTranslation();
218
+
216
219
  const id = column.key;
217
220
 
218
221
  const [filterInternal, setFilterInternal] = useState<[VirtualTableWhereFilterOp, any] | undefined>(filter);
@@ -258,16 +261,15 @@ function FilterForm<M>({
258
261
  {filterField && <div className="m-4 w-[400px]">
259
262
  {filterField}
260
263
  </div>}
261
- <div className="flex justify-end m-4">
262
- <Button
263
- className="mr-4"
264
- disabled={!filterIsSet}
265
- variant={"text"}
266
- type="reset"
267
- aria-label="filter clear"
268
- onClick={reset}>Clear</Button>
269
- <Button
270
- type="submit">Filter</Button>
264
+ <div className="flex justify-end p-4 pt-0 gap-2">
265
+ <Button variant={"text"}
266
+ size={"small"}
267
+ aria-label="filter clear"
268
+ onClick={reset}>{t("clear")}</Button>
269
+
270
+ <Button variant={"outlined"}
271
+ size={"small"}
272
+ type="submit">{t("filter")}</Button>
271
273
  </div>
272
274
  </form>
273
275
  );
@@ -1,3 +1,7 @@
1
+ // Note: entity action 'name' fields (Edit, Copy, Delete) are plain strings defined
2
+ // at module level. They cannot use hooks. Consumers who need to translate these
3
+ // should override the action name by creating their own EntityAction objects or
4
+ // by using the entityActions prop with custom names for their locale.
1
5
  import { DeleteIcon, EditIcon, FileCopyIcon } from "@firecms/ui";
2
6
  import { EntityAction } from "../../types";
3
7
  import { DeleteEntityDialog } from "../DeleteEntityDialog";