@firecms/core 3.1.0-canary.1df3b2c → 3.1.0-canary.75005e4

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 (209) 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/EntityCollectionView/ViewModeToggle.d.ts +5 -10
  4. package/dist/components/ErrorBoundary.d.ts +4 -2
  5. package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
  6. package/dist/components/LanguageToggle.d.ts +1 -0
  7. package/dist/components/UnsavedChangesDialog.d.ts +1 -0
  8. package/dist/components/VirtualTable/VirtualTableHeader.d.ts +1 -1
  9. package/dist/components/index.d.ts +1 -0
  10. package/dist/core/DrawerNavigationGroup.d.ts +2 -2
  11. package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
  12. package/dist/editor/components/editor-bubble-item.d.ts +8 -0
  13. package/dist/editor/components/editor-bubble.d.ts +8 -0
  14. package/dist/editor/components/index.d.ts +14 -0
  15. package/dist/editor/editor.d.ts +30 -0
  16. package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
  17. package/dist/editor/extensions/Image/index.d.ts +6 -0
  18. package/dist/editor/extensions/Image.d.ts +6 -0
  19. package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
  20. package/dist/editor/extensions/clipboard.d.ts +7 -0
  21. package/dist/editor/extensions/custom-keymap.d.ts +1 -0
  22. package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
  23. package/dist/editor/hooks/useProseMirror.d.ts +14 -0
  24. package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
  25. package/dist/editor/index.d.ts +2 -0
  26. package/dist/editor/markdown.d.ts +5 -0
  27. package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
  28. package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
  29. package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
  30. package/dist/editor/nodeViews/index.d.ts +6 -0
  31. package/dist/editor/plugins/index.d.ts +2 -0
  32. package/dist/editor/plugins/inputrules.d.ts +6 -0
  33. package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
  34. package/dist/editor/plugins/slashCommandPlugin.d.ts +11 -0
  35. package/dist/editor/schema.d.ts +2 -0
  36. package/dist/editor/selectors/ai-selector.d.ts +0 -0
  37. package/dist/editor/selectors/color-selector.d.ts +10 -0
  38. package/dist/editor/selectors/link-selector.d.ts +8 -0
  39. package/dist/editor/selectors/node-selector.d.ts +15 -0
  40. package/dist/editor/selectors/text-buttons.d.ts +1 -0
  41. package/dist/editor/types.d.ts +5 -0
  42. package/dist/editor/useProseMirror.d.ts +16 -0
  43. package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
  44. package/dist/editor/utils/remove_classes.d.ts +1 -0
  45. package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
  46. package/dist/form/components/ErrorFocus.d.ts +1 -1
  47. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  48. package/dist/hooks/index.d.ts +1 -0
  49. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  50. package/dist/hooks/useCollapsedGroups.d.ts +3 -3
  51. package/dist/hooks/useTranslation.d.ts +17 -0
  52. package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
  53. package/dist/index.d.ts +4 -0
  54. package/dist/index.es.js +11441 -2215
  55. package/dist/index.es.js.map +1 -1
  56. package/dist/index.umd.js +11423 -2216
  57. package/dist/index.umd.js.map +1 -1
  58. package/dist/internal/useRestoreScroll.d.ts +1 -1
  59. package/dist/locales/de.d.ts +2 -0
  60. package/dist/locales/en.d.ts +10 -0
  61. package/dist/locales/es.d.ts +10 -0
  62. package/dist/locales/fr.d.ts +2 -0
  63. package/dist/locales/hi.d.ts +2 -0
  64. package/dist/locales/it.d.ts +2 -0
  65. package/dist/locales/pt.d.ts +7 -0
  66. package/dist/types/analytics.d.ts +1 -1
  67. package/dist/types/collections.d.ts +8 -0
  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/translations.d.ts +646 -0
  74. package/dist/util/entities.d.ts +1 -1
  75. package/dist/util/resolutions.d.ts +2 -2
  76. package/package.json +47 -13
  77. package/src/app/Scaffold.tsx +7 -5
  78. package/src/components/AIIcon.tsx +3 -1
  79. package/src/components/ArrayContainer.tsx +6 -4
  80. package/src/components/ClearFilterSortButton.tsx +6 -3
  81. package/src/components/ConfirmationDialog.tsx +4 -2
  82. package/src/components/DeleteEntityDialog.tsx +10 -7
  83. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
  84. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
  85. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +1 -1
  86. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
  87. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +11 -11
  88. package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
  89. package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
  90. package/src/components/EntityCollectionView/EntityBoardCard.tsx +1 -1
  91. package/src/components/EntityCollectionView/EntityCard.tsx +4 -0
  92. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +39 -46
  93. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
  94. package/src/components/EntityCollectionView/EntityCollectionView.tsx +73 -31
  95. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -3
  96. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -2
  97. package/src/components/EntityCollectionView/FiltersDialog.tsx +8 -5
  98. package/src/components/EntityCollectionView/ViewModeToggle.tsx +37 -37
  99. package/src/components/EntityView.tsx +3 -2
  100. package/src/components/ErrorBoundary.tsx +27 -15
  101. package/src/components/HomePage/DefaultHomePage.tsx +19 -13
  102. package/src/components/HomePage/HomePageDnD.tsx +3 -1
  103. package/src/components/HomePage/NavigationGroup.tsx +3 -1
  104. package/src/components/HomePage/RenameGroupDialog.tsx +15 -13
  105. package/src/components/LanguageToggle.tsx +66 -0
  106. package/src/components/NotFoundPage.tsx +5 -3
  107. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -7
  108. package/src/components/ReferenceWidget.tsx +3 -2
  109. package/src/components/SearchIconsView.tsx +3 -1
  110. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +11 -0
  111. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +15 -2
  112. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +11 -0
  113. package/src/components/UnsavedChangesDialog.tsx +6 -4
  114. package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
  115. package/src/components/VirtualTable/VirtualTable.tsx +116 -113
  116. package/src/components/VirtualTable/VirtualTableHeader.tsx +54 -52
  117. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +1 -1
  118. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +3 -3
  119. package/src/components/common/default_entity_actions.tsx +4 -0
  120. package/src/components/common/useDataSourceTableController.tsx +12 -4
  121. package/src/components/index.tsx +1 -0
  122. package/src/core/DefaultAppBar.tsx +15 -11
  123. package/src/core/DefaultDrawer.tsx +8 -2
  124. package/src/core/DrawerNavigationGroup.tsx +5 -3
  125. package/src/core/EntityEditView.tsx +4 -3
  126. package/src/core/EntityEditViewFormActions.tsx +24 -17
  127. package/src/core/EntitySidePanel.tsx +32 -29
  128. package/src/core/FireCMS.tsx +33 -6
  129. package/src/core/field_configs.tsx +14 -9
  130. package/src/editor/components/SlashCommandMenu.tsx +348 -0
  131. package/src/editor/components/editor-bubble-item.tsx +32 -0
  132. package/src/editor/components/editor-bubble.tsx +118 -0
  133. package/src/editor/components/index.ts +12 -0
  134. package/src/editor/editor.tsx +307 -0
  135. package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
  136. package/src/editor/extensions/Image/index.ts +133 -0
  137. package/src/editor/extensions/Image.ts +144 -0
  138. package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
  139. package/src/editor/extensions/clipboard.ts +72 -0
  140. package/src/editor/extensions/custom-keymap.ts +24 -0
  141. package/src/editor/extensions/drag-and-drop.tsx +472 -0
  142. package/src/editor/hooks/useProseMirror.ts +115 -0
  143. package/src/editor/hooks/useProseMirrorContext.ts +15 -0
  144. package/src/editor/index.ts +2 -0
  145. package/src/editor/markdown.ts +110 -0
  146. package/src/editor/nodeViews/ImageComponent.tsx +20 -0
  147. package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
  148. package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
  149. package/src/editor/nodeViews/index.ts +35 -0
  150. package/src/editor/plugins/index.ts +55 -0
  151. package/src/editor/plugins/inputrules.ts +82 -0
  152. package/src/editor/plugins/placeholderPlugin.ts +55 -0
  153. package/src/editor/plugins/slashCommandPlugin.ts +49 -0
  154. package/src/editor/schema.ts +228 -0
  155. package/src/editor/selectors/ai-selector.tsx +111 -0
  156. package/src/editor/selectors/color-selector.tsx +200 -0
  157. package/src/editor/selectors/link-selector.tsx +118 -0
  158. package/src/editor/selectors/node-selector.tsx +157 -0
  159. package/src/editor/selectors/text-buttons.tsx +86 -0
  160. package/src/editor/types.ts +6 -0
  161. package/src/editor/useProseMirror.ts +126 -0
  162. package/src/editor/utils/prosemirror-utils.ts +78 -0
  163. package/src/editor/utils/remove_classes.ts +17 -0
  164. package/src/editor/utils/useDebouncedCallback.ts +25 -0
  165. package/src/form/EntityForm.tsx +76 -63
  166. package/src/form/EntityFormActions.tsx +19 -12
  167. package/src/form/PropertyFieldBinding.tsx +6 -5
  168. package/src/form/components/ErrorFocus.tsx +3 -3
  169. package/src/form/components/LocalChangesMenu.tsx +13 -13
  170. package/src/form/components/StorageItemPreview.tsx +3 -2
  171. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -4
  172. package/src/form/field_bindings/BlockFieldBinding.tsx +5 -2
  173. package/src/form/field_bindings/KeyValueFieldBinding.tsx +23 -18
  174. package/src/form/field_bindings/MapFieldBinding.tsx +4 -3
  175. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -4
  176. package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -1
  177. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +87 -85
  178. package/src/hooks/index.tsx +1 -0
  179. package/src/hooks/useBuildNavigationController.tsx +49 -22
  180. package/src/hooks/useCollapsedGroups.ts +7 -6
  181. package/src/hooks/useTranslation.ts +31 -0
  182. package/src/hooks/useValidateAuthenticator.tsx +1 -1
  183. package/src/i18n/FireCMSi18nProvider.tsx +160 -0
  184. package/src/index.ts +4 -0
  185. package/src/internal/useBuildDataSource.ts +1 -2
  186. package/src/internal/useBuildSideEntityController.tsx +22 -20
  187. package/src/locales/de.ts +691 -0
  188. package/src/locales/en.ts +703 -0
  189. package/src/locales/es.ts +703 -0
  190. package/src/locales/fr.ts +691 -0
  191. package/src/locales/hi.ts +691 -0
  192. package/src/locales/it.ts +691 -0
  193. package/src/locales/pt.ts +700 -0
  194. package/src/preview/PropertyPreview.tsx +1 -0
  195. package/src/preview/components/UrlComponentPreview.tsx +4 -2
  196. package/src/preview/components/UserPreview.tsx +3 -1
  197. package/src/types/analytics.ts +10 -0
  198. package/src/types/collections.ts +9 -0
  199. package/src/types/customization_controller.tsx +2 -1
  200. package/src/types/firecms.tsx +2 -1
  201. package/src/types/index.ts +1 -0
  202. package/src/types/navigation.ts +2 -2
  203. package/src/types/plugins.tsx +26 -0
  204. package/src/types/translations.ts +725 -0
  205. package/src/util/entities.ts +1 -1
  206. package/src/util/join_collections.ts +10 -8
  207. package/src/util/previews.ts +2 -2
  208. package/src/util/property_utils.tsx +1 -1
  209. package/src/util/resolutions.ts +5 -3
@@ -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,
@@ -34,7 +35,7 @@ export type FilterFormFieldProps<CustomProps> = {
34
35
  };
35
36
 
36
37
  type VirtualTableHeaderProps<M extends Record<string, any>> = {
37
- resizeHandleRef: RefObject<HTMLDivElement>;
38
+ resizeHandleRef: RefObject<HTMLDivElement | null>;
38
39
  columnIndex: number;
39
40
  isResizingIndex: number;
40
41
  column: VirtualTableColumn<any>;
@@ -51,20 +52,20 @@ type VirtualTableHeaderProps<M extends Record<string, any>> = {
51
52
 
52
53
  export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
53
54
  function VirtualTableHeader<M extends Record<string, any>>({
54
- resizeHandleRef,
55
- columnIndex,
56
- isResizingIndex,
57
- sort,
58
- onColumnSort,
59
- onFilterUpdate,
60
- filter,
61
- column,
62
- onClickResizeColumn,
63
- createFilterField,
64
- AdditionalHeaderWidget,
65
- isDragging,
66
- isDraggable
67
- }: VirtualTableHeaderProps<M>) {
55
+ resizeHandleRef,
56
+ columnIndex,
57
+ isResizingIndex,
58
+ sort,
59
+ onColumnSort,
60
+ onFilterUpdate,
61
+ filter,
62
+ column,
63
+ onClickResizeColumn,
64
+ createFilterField,
65
+ AdditionalHeaderWidget,
66
+ isDragging,
67
+ isDraggable
68
+ }: VirtualTableHeaderProps<M>) {
68
69
 
69
70
  const [onHover, setOnHover] = useState(false);
70
71
 
@@ -129,11 +130,11 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
129
130
  <>
130
131
 
131
132
  {AdditionalHeaderWidget &&
132
- <AdditionalHeaderWidget onHover={onHover || openFilter}/>}
133
+ <AdditionalHeaderWidget onHover={onHover || openFilter} />}
133
134
 
134
135
  {column.sortable && (sort || hovered || openFilter) &&
135
136
  <Badge color="secondary"
136
- invisible={!sort}>
137
+ invisible={!sort}>
137
138
  <IconButton
138
139
  size={"small"}
139
140
  className={onHover || openFilter ? "bg-white dark:bg-surface-950" : undefined}
@@ -142,11 +143,11 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
142
143
  }}
143
144
  >
144
145
  {!sort &&
145
- <ArrowUpwardIcon/>}
146
+ <ArrowUpwardIcon />}
146
147
  {sort === "asc" &&
147
- <ArrowUpwardIcon/>}
148
+ <ArrowUpwardIcon />}
148
149
  {sort === "desc" &&
149
- <ArrowUpwardIcon className={"rotate-180"}/>}
150
+ <ArrowUpwardIcon className={"rotate-180"} />}
150
151
  </IconButton>
151
152
  </Badge>
152
153
  }
@@ -154,7 +155,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
154
155
 
155
156
  {column.filter && createFilterField && <div>
156
157
  <Badge color="secondary"
157
- invisible={!filter}>
158
+ invisible={!filter}>
158
159
 
159
160
  <Popover
160
161
  open={openFilter}
@@ -166,16 +167,16 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
166
167
  className={onHover || openFilter ? "bg-white dark:bg-surface-950" : undefined}
167
168
  size={"small"}
168
169
  onClick={handleSettingsClick}>
169
- <FilterListIcon size={"small"}/>
170
+ <FilterListIcon size={"small"} />
170
171
  </IconButton>}
171
172
  >
172
173
  <FilterForm column={column}
173
- filter={filter}
174
- onHover={onHover}
175
- onFilterUpdate={update}
176
- createFilterField={createFilterField}
177
- hidden={hidden}
178
- setHidden={setHidden}/>
174
+ filter={filter}
175
+ onHover={onHover}
176
+ onFilterUpdate={update}
177
+ createFilterField={createFilterField}
178
+ hidden={hidden}
179
+ setHidden={setHidden} />
179
180
 
180
181
  </Popover>
181
182
 
@@ -204,14 +205,16 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
204
205
  }, equal) as React.FunctionComponent<VirtualTableHeaderProps<any>>;
205
206
 
206
207
  function FilterForm<M>({
207
- column,
208
- onFilterUpdate,
209
- filter,
210
- onHover,
211
- createFilterField,
212
- hidden,
213
- setHidden
214
- }: FilterFormProps<M>) {
208
+ column,
209
+ onFilterUpdate,
210
+ filter,
211
+ onHover,
212
+ createFilterField,
213
+ hidden,
214
+ setHidden
215
+ }: FilterFormProps<M>) {
216
+
217
+ const { t } = useTranslation();
215
218
 
216
219
  const id = column.key;
217
220
 
@@ -245,12 +248,12 @@ function FilterForm<M>({
245
248
  if (!filterField) return null;
246
249
  return (
247
250
  <form noValidate={true}
248
- onSubmit={(e) => {
249
- e.stopPropagation();
250
- e.preventDefault();
251
- submit();
252
- }}
253
- className={"text-surface-900 dark:text-white"}>
251
+ onSubmit={(e) => {
252
+ e.stopPropagation();
253
+ e.preventDefault();
254
+ submit();
255
+ }}
256
+ className={"text-surface-900 dark:text-white"}>
254
257
  <div
255
258
  className={cls(defaultBorderMixin, "py-4 px-6 typography-label border-b")}>
256
259
  {column.title ?? id}
@@ -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
  );
@@ -26,7 +26,7 @@ const SortableColumnHeader = ({
26
26
  }: {
27
27
  column: VirtualTableColumn;
28
28
  columnIndex: number;
29
- columnRefs: React.RefObject<HTMLDivElement>[];
29
+ columnRefs: React.RefObject<HTMLDivElement | null>[];
30
30
  isResizing: number;
31
31
  onFilterUpdate: any;
32
32
  filter: [VirtualTableWhereFilterOp, any] | undefined;
@@ -79,7 +79,7 @@ export function VirtualTableSelect(props: {
79
79
  multiple
80
80
  ? <MultiSelect
81
81
  inputRef={ref}
82
- className="w-full h-full p-0 bg-transparent"
82
+ className="w-full h-full p-0 bg-transparent outline-none"
83
83
  position={"item-aligned"}
84
84
  disabled={disabled}
85
85
  includeClear={false}
@@ -104,8 +104,8 @@ export function VirtualTableSelect(props: {
104
104
  inputRef={ref}
105
105
  size={"large"}
106
106
  fullWidth={true}
107
- className="w-full h-full p-0 bg-transparent"
108
- inputClassName="focus:ring-0 focus-visible:ring-0 outline-none focus:outline-none focus-visible:outline-none"
107
+ className="w-full h-full p-0 bg-transparent outline-none [&_button]:ring-0 [&_button]:ring-offset-0"
108
+ inputClassName="ring-0 ring-offset-0 focus:ring-0 focus-visible:ring-0 outline-none focus:outline-none focus-visible:outline-none focus-visible:ring-offset-0"
109
109
  position={"item-aligned"}
110
110
  disabled={disabled}
111
111
  padding={false}
@@ -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";
@@ -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
 
@@ -92,7 +95,7 @@ export const DefaultAppBar = function DefaultAppBar({
92
95
 
93
96
  const user = userProp ?? authController.user;
94
97
 
95
- let avatarComponent: JSX.Element | null;
98
+ let avatarComponent: React.ReactElement | null;
96
99
 
97
100
  if (user) {
98
101
  const initial = user?.displayName
@@ -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}