@firecms/core 3.1.0-canary.24c8270 → 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.
- package/dist/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
- package/dist/components/ErrorBoundary.d.ts +3 -1
- package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
- package/dist/components/LanguageToggle.d.ts +1 -0
- package/dist/components/UnsavedChangesDialog.d.ts +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/core/DrawerNavigationGroup.d.ts +2 -2
- package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
- package/dist/editor/components/editor-bubble-item.d.ts +8 -0
- package/dist/editor/components/editor-bubble.d.ts +8 -0
- package/dist/editor/components/index.d.ts +14 -0
- package/dist/editor/editor.d.ts +30 -0
- package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
- package/dist/editor/extensions/Image/index.d.ts +6 -0
- package/dist/editor/extensions/Image.d.ts +6 -0
- package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
- package/dist/editor/extensions/clipboard.d.ts +7 -0
- package/dist/editor/extensions/custom-keymap.d.ts +1 -0
- package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
- package/dist/editor/hooks/useProseMirror.d.ts +14 -0
- package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/markdown.d.ts +5 -0
- package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
- package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
- package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
- package/dist/editor/nodeViews/index.d.ts +6 -0
- package/dist/editor/plugins/index.d.ts +2 -0
- package/dist/editor/plugins/inputrules.d.ts +6 -0
- package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
- package/dist/editor/plugins/slashCommandPlugin.d.ts +11 -0
- package/dist/editor/schema.d.ts +2 -0
- package/dist/editor/selectors/ai-selector.d.ts +0 -0
- package/dist/editor/selectors/color-selector.d.ts +10 -0
- package/dist/editor/selectors/link-selector.d.ts +8 -0
- package/dist/editor/selectors/node-selector.d.ts +15 -0
- package/dist/editor/selectors/text-buttons.d.ts +1 -0
- package/dist/editor/types.d.ts +5 -0
- package/dist/editor/useProseMirror.d.ts +16 -0
- package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
- package/dist/editor/utils/remove_classes.d.ts +1 -0
- package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
- package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useBuildNavigationController.d.ts +0 -1
- package/dist/hooks/useCollapsedGroups.d.ts +3 -3
- package/dist/hooks/useTranslation.d.ts +17 -0
- package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.es.js +11293 -2142
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +11274 -2142
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/de.d.ts +2 -0
- package/dist/locales/en.d.ts +10 -0
- package/dist/locales/es.d.ts +10 -0
- package/dist/locales/fr.d.ts +2 -0
- package/dist/locales/hi.d.ts +2 -0
- package/dist/locales/it.d.ts +2 -0
- package/dist/locales/pt.d.ts +7 -0
- package/dist/types/customization_controller.d.ts +2 -1
- package/dist/types/firecms.d.ts +2 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/navigation.d.ts +2 -2
- package/dist/types/plugins.d.ts +7 -0
- package/dist/types/translations.d.ts +646 -0
- package/package.json +43 -9
- package/src/app/Scaffold.tsx +7 -5
- package/src/components/AIIcon.tsx +3 -1
- package/src/components/ArrayContainer.tsx +6 -4
- package/src/components/ClearFilterSortButton.tsx +6 -3
- package/src/components/ConfirmationDialog.tsx +4 -2
- package/src/components/DeleteEntityDialog.tsx +10 -7
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
- package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
- package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +16 -43
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -18
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -3
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -2
- package/src/components/EntityCollectionView/FiltersDialog.tsx +8 -5
- package/src/components/EntityCollectionView/ViewModeToggle.tsx +11 -8
- package/src/components/EntityView.tsx +3 -2
- package/src/components/ErrorBoundary.tsx +27 -15
- package/src/components/HomePage/DefaultHomePage.tsx +19 -13
- package/src/components/HomePage/HomePageDnD.tsx +3 -1
- package/src/components/HomePage/NavigationGroup.tsx +3 -1
- package/src/components/HomePage/RenameGroupDialog.tsx +15 -13
- package/src/components/LanguageToggle.tsx +66 -0
- package/src/components/NotFoundPage.tsx +5 -3
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -7
- package/src/components/ReferenceWidget.tsx +3 -2
- package/src/components/SearchIconsView.tsx +3 -1
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +11 -0
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +15 -2
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +11 -0
- package/src/components/UnsavedChangesDialog.tsx +6 -4
- package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
- package/src/components/VirtualTable/VirtualTableHeader.tsx +12 -10
- package/src/components/common/default_entity_actions.tsx +4 -0
- package/src/components/common/useDataSourceTableController.tsx +5 -14
- package/src/components/index.tsx +1 -0
- package/src/core/DefaultAppBar.tsx +14 -10
- package/src/core/DefaultDrawer.tsx +8 -2
- package/src/core/DrawerNavigationGroup.tsx +5 -3
- package/src/core/EntityEditView.tsx +3 -2
- package/src/core/EntityEditViewFormActions.tsx +24 -17
- package/src/core/EntitySidePanel.tsx +4 -3
- package/src/core/FireCMS.tsx +33 -6
- package/src/editor/components/SlashCommandMenu.tsx +348 -0
- package/src/editor/components/editor-bubble-item.tsx +32 -0
- package/src/editor/components/editor-bubble.tsx +118 -0
- package/src/editor/components/index.ts +12 -0
- package/src/editor/editor.tsx +307 -0
- package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
- package/src/editor/extensions/Image/index.ts +133 -0
- package/src/editor/extensions/Image.ts +144 -0
- package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
- package/src/editor/extensions/clipboard.ts +72 -0
- package/src/editor/extensions/custom-keymap.ts +24 -0
- package/src/editor/extensions/drag-and-drop.tsx +472 -0
- package/src/editor/hooks/useProseMirror.ts +115 -0
- package/src/editor/hooks/useProseMirrorContext.ts +15 -0
- package/src/editor/index.ts +2 -0
- package/src/editor/markdown.ts +110 -0
- package/src/editor/nodeViews/ImageComponent.tsx +20 -0
- package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
- package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
- package/src/editor/nodeViews/index.ts +35 -0
- package/src/editor/plugins/index.ts +55 -0
- package/src/editor/plugins/inputrules.ts +82 -0
- package/src/editor/plugins/placeholderPlugin.ts +55 -0
- package/src/editor/plugins/slashCommandPlugin.ts +49 -0
- package/src/editor/schema.ts +228 -0
- package/src/editor/selectors/ai-selector.tsx +111 -0
- package/src/editor/selectors/color-selector.tsx +200 -0
- package/src/editor/selectors/link-selector.tsx +118 -0
- package/src/editor/selectors/node-selector.tsx +157 -0
- package/src/editor/selectors/text-buttons.tsx +86 -0
- package/src/editor/types.ts +6 -0
- package/src/editor/useProseMirror.ts +126 -0
- package/src/editor/utils/prosemirror-utils.ts +78 -0
- package/src/editor/utils/remove_classes.ts +17 -0
- package/src/editor/utils/useDebouncedCallback.ts +25 -0
- package/src/form/EntityForm.tsx +7 -3
- package/src/form/EntityFormActions.tsx +19 -12
- package/src/form/PropertyFieldBinding.tsx +3 -2
- package/src/form/components/LocalChangesMenu.tsx +13 -13
- package/src/form/components/StorageItemPreview.tsx +3 -2
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -4
- package/src/form/field_bindings/BlockFieldBinding.tsx +5 -2
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +23 -18
- package/src/form/field_bindings/MapFieldBinding.tsx +4 -3
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +3 -3
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +4 -2
- package/src/hooks/index.tsx +1 -0
- package/src/hooks/useBuildNavigationController.tsx +20 -13
- package/src/hooks/useCollapsedGroups.ts +7 -6
- package/src/hooks/useTranslation.ts +31 -0
- package/src/i18n/FireCMSi18nProvider.tsx +160 -0
- package/src/index.ts +4 -0
- package/src/locales/de.ts +691 -0
- package/src/locales/en.ts +703 -0
- package/src/locales/es.ts +703 -0
- package/src/locales/fr.ts +691 -0
- package/src/locales/hi.ts +691 -0
- package/src/locales/it.ts +691 -0
- package/src/locales/pt.ts +700 -0
- package/src/preview/components/UrlComponentPreview.tsx +4 -2
- package/src/preview/components/UserPreview.tsx +3 -1
- package/src/types/customization_controller.tsx +2 -1
- package/src/types/firecms.tsx +2 -1
- package/src/types/index.ts +1 -0
- package/src/types/navigation.ts +2 -2
- package/src/types/plugins.tsx +8 -0
- package/src/types/translations.ts +725 -0
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
WarningIcon
|
|
17
17
|
} from "@firecms/ui";
|
|
18
18
|
import { FormexController } from "@firecms/formex";
|
|
19
|
-
import { useSnackbarController } from "../../hooks";
|
|
19
|
+
import { useSnackbarController, useTranslation } from "../../hooks";
|
|
20
20
|
import { mergeDeep } from "../../util";
|
|
21
21
|
import { flattenKeys, removeEntityFromCache } from "../../util/entity_cache";
|
|
22
22
|
import { ResolvedProperties } from "../../types";
|
|
@@ -39,6 +39,7 @@ export function LocalChangesMenu<M extends object>({
|
|
|
39
39
|
}: LocalChangesMenuProps<M>) {
|
|
40
40
|
|
|
41
41
|
const snackbarController = useSnackbarController();
|
|
42
|
+
const { t } = useTranslation();
|
|
42
43
|
const [previewDialogOpen, setPreviewDialogOpen] = useState(false);
|
|
43
44
|
const [open, setOpen] = useState(false);
|
|
44
45
|
|
|
@@ -62,7 +63,7 @@ export function LocalChangesMenu<M extends object>({
|
|
|
62
63
|
formex.setValues(mergedValues);
|
|
63
64
|
snackbarController.open({
|
|
64
65
|
type: "info",
|
|
65
|
-
message: "
|
|
66
|
+
message: t("local_changes_applied")
|
|
66
67
|
});
|
|
67
68
|
handleCloseMenu();
|
|
68
69
|
onClearLocalChanges?.();
|
|
@@ -72,7 +73,7 @@ export function LocalChangesMenu<M extends object>({
|
|
|
72
73
|
removeEntityFromCache(cacheKey);
|
|
73
74
|
snackbarController.open({
|
|
74
75
|
type: "info",
|
|
75
|
-
message: "
|
|
76
|
+
message: t("local_changes_discarded")
|
|
76
77
|
});
|
|
77
78
|
handleCloseMenu();
|
|
78
79
|
onClearLocalChanges?.();
|
|
@@ -90,7 +91,7 @@ export function LocalChangesMenu<M extends object>({
|
|
|
90
91
|
onClick={handleOpenMenu}
|
|
91
92
|
>
|
|
92
93
|
<WarningIcon size={"smallest"} className={"mr-1 text-yellow-600 dark:text-yellow-400"}/>
|
|
93
|
-
|
|
94
|
+
{t("unsaved_local_changes")}
|
|
94
95
|
<KeyboardArrowDownIcon size={"smallest"}/>
|
|
95
96
|
</Button>
|
|
96
97
|
}
|
|
@@ -98,12 +99,11 @@ export function LocalChangesMenu<M extends object>({
|
|
|
98
99
|
onOpenChange={setOpen}
|
|
99
100
|
>
|
|
100
101
|
<div className={"max-w-xs px-4 py-4 text-sm text-gray-700 dark:text-gray-300"}>
|
|
101
|
-
|
|
102
|
-
don't apply them.
|
|
102
|
+
{t("unsaved_local_changes_description")}
|
|
103
103
|
</div>
|
|
104
|
-
<MenuItem dense onClick={handlePreview}><VisibilityIcon size={"small"}/>
|
|
105
|
-
<MenuItem dense onClick={handleApply}><CheckIcon size={"small"}/>
|
|
106
|
-
<MenuItem dense onClick={handleDiscard}><CancelIcon size={"small"}/>
|
|
104
|
+
<MenuItem dense onClick={handlePreview}><VisibilityIcon size={"small"}/>{t("preview_changes")}</MenuItem>
|
|
105
|
+
<MenuItem dense onClick={handleApply}><CheckIcon size={"small"}/>{t("apply_changes")}</MenuItem>
|
|
106
|
+
<MenuItem dense onClick={handleDiscard}><CancelIcon size={"small"}/>{t("discard_local_changes")}</MenuItem>
|
|
107
107
|
</Menu>
|
|
108
108
|
|
|
109
109
|
<Dialog
|
|
@@ -111,10 +111,10 @@ export function LocalChangesMenu<M extends object>({
|
|
|
111
111
|
onOpenChange={setPreviewDialogOpen}
|
|
112
112
|
maxWidth={"4xl"}
|
|
113
113
|
>
|
|
114
|
-
<DialogTitle variant={"h6"}>
|
|
114
|
+
<DialogTitle variant={"h6"}>{t("preview_local_changes")}</DialogTitle>
|
|
115
115
|
<DialogContent className={"my-4"}>
|
|
116
116
|
<Typography variant={"body2"} className={"mb-4"}>
|
|
117
|
-
|
|
117
|
+
{t("preview_local_changes_description")}
|
|
118
118
|
</Typography>
|
|
119
119
|
<div className={`border rounded-lg ${defaultBorderMixin}`} style={{
|
|
120
120
|
maxHeight: 520,
|
|
@@ -127,7 +127,7 @@ export function LocalChangesMenu<M extends object>({
|
|
|
127
127
|
</div>
|
|
128
128
|
</DialogContent>
|
|
129
129
|
<DialogActions>
|
|
130
|
-
<Button onClick={() => setPreviewDialogOpen(false)}>
|
|
130
|
+
<Button onClick={() => setPreviewDialogOpen(false)}>{t("close")}</Button>
|
|
131
131
|
<Button
|
|
132
132
|
variant={"filled"}
|
|
133
133
|
onClick={() => {
|
|
@@ -135,7 +135,7 @@ export function LocalChangesMenu<M extends object>({
|
|
|
135
135
|
setPreviewDialogOpen(false);
|
|
136
136
|
}}
|
|
137
137
|
>
|
|
138
|
-
|
|
138
|
+
{t("apply_changes")}
|
|
139
139
|
</Button>
|
|
140
140
|
</DialogActions>
|
|
141
141
|
</Dialog>
|
|
@@ -5,6 +5,7 @@ import { PreviewSize, PropertyPreview } from "../../preview";
|
|
|
5
5
|
|
|
6
6
|
import { cls, DescriptionIcon, IconButton, paperMixin, RemoveIcon, Tooltip } from "@firecms/ui";
|
|
7
7
|
import { ErrorBoundary } from "../../components";
|
|
8
|
+
import { useTranslation } from "../../hooks/useTranslation";
|
|
8
9
|
|
|
9
10
|
interface StorageItemPreviewProps {
|
|
10
11
|
name: string;
|
|
@@ -27,7 +28,7 @@ export function StorageItemPreview({
|
|
|
27
28
|
placeholder,
|
|
28
29
|
className
|
|
29
30
|
}: StorageItemPreviewProps) {
|
|
30
|
-
|
|
31
|
+
const { t } = useTranslation();
|
|
31
32
|
return (
|
|
32
33
|
<div className={cls(
|
|
33
34
|
"relative border-box flex items-center justify-center",
|
|
@@ -41,7 +42,7 @@ export function StorageItemPreview({
|
|
|
41
42
|
|
|
42
43
|
<Tooltip
|
|
43
44
|
asChild={true}
|
|
44
|
-
title="
|
|
45
|
+
title={t("remove")}>
|
|
45
46
|
<IconButton
|
|
46
47
|
size={"small"}
|
|
47
48
|
onClick={(event) => {
|
|
@@ -4,8 +4,7 @@ import { ReferencePreview } from "../../preview";
|
|
|
4
4
|
import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
|
|
5
5
|
import { ArrayContainer, ArrayEntryParams, ErrorView } from "../../components";
|
|
6
6
|
import { getIconForProperty, getReferenceFrom } from "../../util";
|
|
7
|
-
|
|
8
|
-
import { useNavigationController, useReferenceDialog } from "../../hooks";
|
|
7
|
+
import { useNavigationController, useReferenceDialog, useTranslation } from "../../hooks";
|
|
9
8
|
import { Button, cls, EditIcon, ExpandablePanel, fieldBackgroundMixin, Typography } from "@firecms/ui";
|
|
10
9
|
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
11
10
|
|
|
@@ -48,6 +47,7 @@ export function ArrayOfReferencesFieldBinding({
|
|
|
48
47
|
setValue
|
|
49
48
|
});
|
|
50
49
|
|
|
50
|
+
const { t } = useTranslation();
|
|
51
51
|
const navigationController = useNavigationController();
|
|
52
52
|
const collection: EntityCollection | undefined = useMemo(() => {
|
|
53
53
|
return ofProperty.path ? navigationController.getCollection(ofProperty.path) : undefined;
|
|
@@ -84,7 +84,7 @@ export function ArrayOfReferencesFieldBinding({
|
|
|
84
84
|
}: ArrayEntryParams) => {
|
|
85
85
|
const entryValue = value && value.length > index ? value[index] : undefined;
|
|
86
86
|
if (!entryValue)
|
|
87
|
-
return <div>
|
|
87
|
+
return <div>{t("internal_error")}</div>;
|
|
88
88
|
return (
|
|
89
89
|
<ReferencePreview
|
|
90
90
|
key={internalId}
|
|
@@ -132,7 +132,7 @@ export function ArrayOfReferencesFieldBinding({
|
|
|
132
132
|
disabled={isSubmitting}
|
|
133
133
|
onClick={onEntryClick}>
|
|
134
134
|
<EditIcon size={"small"}/>
|
|
135
|
-
|
|
135
|
+
{t("edit_name", { name: property.name ?? "" })}
|
|
136
136
|
</Button>
|
|
137
137
|
</div>}
|
|
138
138
|
</>;
|
|
@@ -11,6 +11,7 @@ import { DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE } from "../../util/common";
|
|
|
11
11
|
import { cls, ExpandablePanel, paperMixin, Select, SelectItem, Typography } from "@firecms/ui";
|
|
12
12
|
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
13
13
|
import { ArrayContainer, ArrayEntryParams } from "../../components";
|
|
14
|
+
import { useTranslation } from "../../hooks/useTranslation";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* If the `oneOf` property is specified, this fields render each array entry as
|
|
@@ -37,6 +38,7 @@ export function BlockFieldBinding<T extends Array<any>>({
|
|
|
37
38
|
}: FieldProps<T>) {
|
|
38
39
|
|
|
39
40
|
const minimalistView = minimalistViewProp || property.minimalistView;
|
|
41
|
+
const { t } = useTranslation();
|
|
40
42
|
|
|
41
43
|
if (!property.oneOf)
|
|
42
44
|
throw Error("ArrayOneOfField misconfiguration. Property `oneOf` not set");
|
|
@@ -84,7 +86,7 @@ export function BlockFieldBinding<T extends Array<any>>({
|
|
|
84
86
|
const body = <ArrayContainer value={value}
|
|
85
87
|
className={"flex flex-col gap-3"}
|
|
86
88
|
droppableId={propertyKey}
|
|
87
|
-
addLabel={property.name ? "
|
|
89
|
+
addLabel={property.name ? t("add_to_field", { fieldName: property.name }) : t("add_entry")}
|
|
88
90
|
buildEntry={buildEntry}
|
|
89
91
|
onInternalIdAdded={setLastAddedId}
|
|
90
92
|
disabled={isSubmitting || Boolean(property.disabled)}
|
|
@@ -167,6 +169,7 @@ function BlockEntry({
|
|
|
167
169
|
const [typeInternal, setTypeInternal] = useState<string | undefined>(type ?? undefined);
|
|
168
170
|
|
|
169
171
|
const formex = useFormex();
|
|
172
|
+
const { t } = useTranslation();
|
|
170
173
|
|
|
171
174
|
useEffect(() => {
|
|
172
175
|
if (!type) {
|
|
@@ -227,7 +230,7 @@ function BlockEntry({
|
|
|
227
230
|
<Select
|
|
228
231
|
className="mb-2"
|
|
229
232
|
placeholder={<Typography variant={"caption"}
|
|
230
|
-
className={"px-4 py-2 font-medium"}>
|
|
233
|
+
className={"px-4 py-2 font-medium"}>{t("type")}</Typography>}
|
|
231
234
|
size={"medium"}
|
|
232
235
|
fullWidth={true}
|
|
233
236
|
position={"item-aligned"}
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { getDefaultValueForDataType, getIconForProperty } from "../../util";
|
|
23
23
|
import { useCustomizationController } from "../../hooks";
|
|
24
24
|
import { getIn } from "@firecms/formex";
|
|
25
|
+
import { useTranslation } from "../../hooks/useTranslation";
|
|
25
26
|
|
|
26
27
|
type MapEditViewRowState = [number, {
|
|
27
28
|
key: string,
|
|
@@ -54,6 +55,7 @@ export function KeyValueFieldBinding({
|
|
|
54
55
|
throw Error(`Your property ${propertyKey} needs to have the 'keyValue' prop in order to use this field binding`);
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
const { t } = useTranslation();
|
|
57
59
|
const initialValues = getIn(context.formex.initialValues, propertyKey);
|
|
58
60
|
|
|
59
61
|
const mapFormView = <MapEditView value={value}
|
|
@@ -103,6 +105,7 @@ function MapEditView<T extends Record<string, any>>({
|
|
|
103
105
|
fieldName,
|
|
104
106
|
disabled
|
|
105
107
|
}: MapEditViewParams<T>) {
|
|
108
|
+
const { t } = useTranslation();
|
|
106
109
|
const [internalState, setInternalState] = React.useState<MapEditViewRowState[]>(
|
|
107
110
|
Object.keys(initialValue ?? {}).map((key) => [getRandomId(), {
|
|
108
111
|
key,
|
|
@@ -230,7 +233,7 @@ function MapEditView<T extends Record<string, any>>({
|
|
|
230
233
|
}]]);
|
|
231
234
|
}
|
|
232
235
|
}>
|
|
233
|
-
{fieldName ?
|
|
236
|
+
{fieldName ? t("add_to_field", { fieldName }) : t("add_entry")}
|
|
234
237
|
</Button>
|
|
235
238
|
|
|
236
239
|
</div>;
|
|
@@ -261,12 +264,13 @@ function MapKeyValueRow<T extends Record<string, any>>({
|
|
|
261
264
|
}) {
|
|
262
265
|
|
|
263
266
|
const { locale } = useCustomizationController();
|
|
267
|
+
const { t } = useTranslation();
|
|
264
268
|
|
|
265
269
|
function buildInput(entryValue: any, fieldKey: string, dataType: DataType) {
|
|
266
270
|
if (dataType === "string" || dataType === "number") {
|
|
267
271
|
return <TextField
|
|
268
272
|
key={dataType}
|
|
269
|
-
placeholder={"value"}
|
|
273
|
+
placeholder={t("value")}
|
|
270
274
|
value={entryValue}
|
|
271
275
|
type={dataType === "number" ? "number" : "text"}
|
|
272
276
|
size={"medium"}
|
|
@@ -325,7 +329,7 @@ function MapKeyValueRow<T extends Record<string, any>>({
|
|
|
325
329
|
<ArrayContainer value={entryValue}
|
|
326
330
|
newDefaultEntry={""}
|
|
327
331
|
droppableId={rowId.toString()}
|
|
328
|
-
addLabel={fieldKey ?
|
|
332
|
+
addLabel={fieldKey ? t("add_to_field", { fieldName: fieldKey }) : t("add_entry")}
|
|
329
333
|
size={"small"}
|
|
330
334
|
disabled={disabled || !fieldKey}
|
|
331
335
|
canAddElements={true}
|
|
@@ -370,7 +374,7 @@ function MapKeyValueRow<T extends Record<string, any>>({
|
|
|
370
374
|
} else {
|
|
371
375
|
return <Typography
|
|
372
376
|
variant={"caption"}>
|
|
373
|
-
{
|
|
377
|
+
{t("data_type_not_supported", { dataType })}
|
|
374
378
|
</Typography>;
|
|
375
379
|
}
|
|
376
380
|
}
|
|
@@ -386,7 +390,7 @@ function MapKeyValueRow<T extends Record<string, any>>({
|
|
|
386
390
|
<div className="w-[300px] max-w-[30%]">
|
|
387
391
|
<TextField
|
|
388
392
|
value={fieldKey}
|
|
389
|
-
placeholder={"key"}
|
|
393
|
+
placeholder={t("key")}
|
|
390
394
|
disabled={disabled || (entryValue !== undefined && entryValue !== null && entryValue !== "")}
|
|
391
395
|
size={"medium"}
|
|
392
396
|
onChange={(event) => {
|
|
@@ -404,17 +408,17 @@ function MapKeyValueRow<T extends Record<string, any>>({
|
|
|
404
408
|
</IconButton>}
|
|
405
409
|
>
|
|
406
410
|
<MenuItem dense
|
|
407
|
-
onClick={() => doUpdateDataType("string")}>string</MenuItem>
|
|
411
|
+
onClick={() => doUpdateDataType("string")}>{t("string")}</MenuItem>
|
|
408
412
|
<MenuItem dense
|
|
409
|
-
onClick={() => doUpdateDataType("number")}>number</MenuItem>
|
|
413
|
+
onClick={() => doUpdateDataType("number")}>{t("number")}</MenuItem>
|
|
410
414
|
<MenuItem dense
|
|
411
|
-
onClick={() => doUpdateDataType("boolean")}>boolean</MenuItem>
|
|
415
|
+
onClick={() => doUpdateDataType("boolean")}>{t("boolean")}</MenuItem>
|
|
412
416
|
<MenuItem dense
|
|
413
|
-
onClick={() => doUpdateDataType("date")}>date</MenuItem>
|
|
417
|
+
onClick={() => doUpdateDataType("date")}>{t("date")}</MenuItem>
|
|
414
418
|
<MenuItem dense
|
|
415
|
-
onClick={() => doUpdateDataType("map")}>map</MenuItem>
|
|
419
|
+
onClick={() => doUpdateDataType("map")}>{t("map")}</MenuItem>
|
|
416
420
|
<MenuItem dense
|
|
417
|
-
onClick={() => doUpdateDataType("array")}>array</MenuItem>
|
|
421
|
+
onClick={() => doUpdateDataType("array")}>{t("array")}</MenuItem>
|
|
418
422
|
</Menu>
|
|
419
423
|
|
|
420
424
|
<IconButton aria-label="delete"
|
|
@@ -446,6 +450,7 @@ function ArrayKeyValueRow<T>({
|
|
|
446
450
|
}) {
|
|
447
451
|
|
|
448
452
|
const { locale } = useCustomizationController();
|
|
453
|
+
const { t } = useTranslation();
|
|
449
454
|
const [selectedDataType, setSelectedDataType] = useState<DataType>(getDataType(value) ?? "string");
|
|
450
455
|
|
|
451
456
|
function doUpdateDataType(dataType: DataType) {
|
|
@@ -487,7 +492,7 @@ function ArrayKeyValueRow<T>({
|
|
|
487
492
|
}}/>;
|
|
488
493
|
} else if (dataType === "array") {
|
|
489
494
|
return <Typography variant={"caption"}>
|
|
490
|
-
|
|
495
|
+
{t("arrays_of_arrays_not_supported")}
|
|
491
496
|
</Typography>;
|
|
492
497
|
} else if (dataType === "map") {
|
|
493
498
|
return <div className={cls(defaultBorderMixin, "ml-2 pl-2 border-l border-solid")}>
|
|
@@ -499,7 +504,7 @@ function ArrayKeyValueRow<T>({
|
|
|
499
504
|
} else {
|
|
500
505
|
return <Typography
|
|
501
506
|
variant={"caption"}>
|
|
502
|
-
{
|
|
507
|
+
{t("data_type_not_supported", { dataType })}
|
|
503
508
|
</Typography>;
|
|
504
509
|
}
|
|
505
510
|
}
|
|
@@ -519,15 +524,15 @@ function ArrayKeyValueRow<T>({
|
|
|
519
524
|
<ArrowDropDownIcon/>
|
|
520
525
|
</IconButton>}>
|
|
521
526
|
<MenuItem dense
|
|
522
|
-
onClick={() => doUpdateDataType("string")}>string</MenuItem>
|
|
527
|
+
onClick={() => doUpdateDataType("string")}>{t("string")}</MenuItem>
|
|
523
528
|
<MenuItem dense
|
|
524
|
-
onClick={() => doUpdateDataType("number")}>number</MenuItem>
|
|
529
|
+
onClick={() => doUpdateDataType("number")}>{t("number")}</MenuItem>
|
|
525
530
|
<MenuItem dense
|
|
526
|
-
onClick={() => doUpdateDataType("boolean")}>boolean</MenuItem>
|
|
531
|
+
onClick={() => doUpdateDataType("boolean")}>{t("boolean")}</MenuItem>
|
|
527
532
|
<MenuItem dense
|
|
528
|
-
onClick={() => doUpdateDataType("map")}>map</MenuItem>
|
|
533
|
+
onClick={() => doUpdateDataType("map")}>{t("map")}</MenuItem>
|
|
529
534
|
<MenuItem dense
|
|
530
|
-
onClick={() => doUpdateDataType("date")}>date</MenuItem>
|
|
535
|
+
onClick={() => doUpdateDataType("date")}>{t("date")}</MenuItem>
|
|
531
536
|
</Menu>
|
|
532
537
|
|
|
533
538
|
</Typography>
|
|
@@ -7,6 +7,7 @@ import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
|
|
|
7
7
|
import { FormEntry } from "../components/FormEntry";
|
|
8
8
|
import { PropertyFieldBinding } from "../PropertyFieldBinding";
|
|
9
9
|
import { cls, ExpandablePanel, InputLabel, Select, SelectItem } from "@firecms/ui";
|
|
10
|
+
import { useTranslation } from "../../hooks";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Field that renders the children property fields
|
|
@@ -91,7 +92,7 @@ export function MapFieldBinding({
|
|
|
91
92
|
}
|
|
92
93
|
</div>
|
|
93
94
|
|
|
94
|
-
{/*{pickOnlySomeKeys && buildPickKeysSelect(disabled, property.properties, setValue, value)}*/}
|
|
95
|
+
{/*{pickOnlySomeKeys && buildPickKeysSelect(disabled, property.properties, setValue, value, t)}*/}
|
|
95
96
|
|
|
96
97
|
</>
|
|
97
98
|
;
|
|
@@ -128,7 +129,7 @@ export function MapFieldBinding({
|
|
|
128
129
|
);
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
const buildPickKeysSelect = (disabled: boolean, properties: Properties, setValue: (value: any) => void, value: any) => {
|
|
132
|
+
const buildPickKeysSelect = (disabled: boolean, properties: Properties, setValue: (value: any) => void, value: any, t: any) => {
|
|
132
133
|
|
|
133
134
|
const keys = Object.keys(properties)
|
|
134
135
|
.filter((key) => !value || !(key in value));
|
|
@@ -143,7 +144,7 @@ const buildPickKeysSelect = (disabled: boolean, properties: Properties, setValue
|
|
|
143
144
|
if (!keys.length) return <></>;
|
|
144
145
|
|
|
145
146
|
return <div className={"m-4"}>
|
|
146
|
-
<InputLabel>
|
|
147
|
+
<InputLabel>{t("add_property")}</InputLabel>
|
|
147
148
|
<Select
|
|
148
149
|
value={""}
|
|
149
150
|
size={"large"}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
useStorageSource
|
|
13
13
|
} from "../../index";
|
|
14
14
|
import { cls, fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin, fieldBackgroundMixin } from "@firecms/ui";
|
|
15
|
-
import { FireCMSEditor, FireCMSEditorProps } from "
|
|
15
|
+
import { FireCMSEditor, FireCMSEditorProps } from "../../editor";
|
|
16
16
|
import { resolveProperty, resolveStorageFilenameString, resolveStoragePathString } from "../../util";
|
|
17
17
|
|
|
18
18
|
interface MarkdownEditorFieldProps {
|
|
@@ -55,12 +55,12 @@ export function MarkdownEditorFieldBinding({
|
|
|
55
55
|
}
|
|
56
56
|
internalValue.current = content;
|
|
57
57
|
setValue(content);
|
|
58
|
-
}, [setValue]);
|
|
58
|
+
}, [setValue, value]);
|
|
59
59
|
|
|
60
60
|
useEffect(() => {
|
|
61
61
|
if (internalValue.current !== value) {
|
|
62
62
|
internalValue.current = value;
|
|
63
|
-
setFieldVersion(
|
|
63
|
+
setFieldVersion(v => v + 1);
|
|
64
64
|
}
|
|
65
65
|
}, [value]);
|
|
66
66
|
|
|
@@ -7,6 +7,7 @@ import { PropertyFieldBinding } from "../PropertyFieldBinding";
|
|
|
7
7
|
import { ExpandablePanel, Typography } from "@firecms/ui";
|
|
8
8
|
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
9
9
|
import { useAuthController } from "../../hooks";
|
|
10
|
+
import { useTranslation } from "../../hooks/useTranslation";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Generic array field that allows reordering and renders the child property
|
|
@@ -34,6 +35,7 @@ export function RepeatFieldBinding<T extends Array<any>>({
|
|
|
34
35
|
|
|
35
36
|
const authController = useAuthController();
|
|
36
37
|
const minimalistView = minimalistViewProp || property.minimalistView;
|
|
38
|
+
const { t } = useTranslation();
|
|
37
39
|
|
|
38
40
|
if (!property.of)
|
|
39
41
|
throw Error("RepeatFieldBinding misconfiguration. Property `of` not set");
|
|
@@ -87,7 +89,7 @@ export function RepeatFieldBinding<T extends Array<any>>({
|
|
|
87
89
|
const canAddElements = !property.disabled && !isSubmitting && !disabled && (property.canAddElements || property.canAddElements === undefined);
|
|
88
90
|
const sortable = property.sortable === undefined ? true : property.sortable;
|
|
89
91
|
const arrayContainer = <ArrayContainer droppableId={propertyKey}
|
|
90
|
-
addLabel={property.name ? "
|
|
92
|
+
addLabel={property.name ? t("add_to_field", { fieldName: property.name }) : t("add_entry")}
|
|
91
93
|
value={value}
|
|
92
94
|
buildEntry={buildEntry}
|
|
93
95
|
onInternalIdAdded={setLastAddedId}
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
Typography
|
|
42
42
|
} from "@firecms/ui";
|
|
43
43
|
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
44
|
+
import { useTranslation } from "../../hooks/useTranslation";
|
|
44
45
|
|
|
45
46
|
const dropZoneClasses = "box-border relative pt-[2px] items-center border border-transparent min-h-[254px] outline-none rounded-md duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] focus:border-primary-solid";
|
|
46
47
|
const disabledClasses = fieldBackgroundDisabledMixin;
|
|
@@ -387,6 +388,7 @@ export function StorageUpload({
|
|
|
387
388
|
storage,
|
|
388
389
|
storagePathBuilder,
|
|
389
390
|
}: StorageUploadProps) {
|
|
391
|
+
const { t } = useTranslation();
|
|
390
392
|
|
|
391
393
|
if (multipleFilesSupported) {
|
|
392
394
|
const arrayProperty = property as ResolvedArrayProperty<string[]>;
|
|
@@ -461,8 +463,8 @@ export function StorageUpload({
|
|
|
461
463
|
}, [value, multipleFilesSupported, onChange, setInternalValue]);
|
|
462
464
|
|
|
463
465
|
const helpText = multipleFilesSupported
|
|
464
|
-
? "
|
|
465
|
-
: "
|
|
466
|
+
? t("drag_drop_multiple")
|
|
467
|
+
: t("drag_drop_single");
|
|
466
468
|
|
|
467
469
|
const renderProperty: ResolvedStringProperty = multipleFilesSupported
|
|
468
470
|
? (property as ResolvedArrayProperty<string[]>).of as ResolvedStringProperty
|
package/src/hooks/index.tsx
CHANGED
|
@@ -34,7 +34,6 @@ import { getParentReferencesFromPath } from "../util/parent_references_from_path
|
|
|
34
34
|
const DEFAULT_BASE_PATH = "/";
|
|
35
35
|
const DEFAULT_COLLECTION_PATH = "/c";
|
|
36
36
|
|
|
37
|
-
export const NAVIGATION_DEFAULT_GROUP_NAME = "Views";
|
|
38
37
|
export const NAVIGATION_ADMIN_GROUP_NAME = "Admin";
|
|
39
38
|
|
|
40
39
|
export type BuildNavigationContextProps<EC extends EntityCollection, USER extends User> = {
|
|
@@ -189,7 +188,7 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
|
|
|
189
188
|
path: pathKey,
|
|
190
189
|
collection,
|
|
191
190
|
description: collection.description?.trim(),
|
|
192
|
-
group: groupName
|
|
191
|
+
group: groupName
|
|
193
192
|
});
|
|
194
193
|
return acc;
|
|
195
194
|
}, [] as NavigationEntry[]),
|
|
@@ -217,7 +216,7 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
|
|
|
217
216
|
path: view.path,
|
|
218
217
|
view,
|
|
219
218
|
description: view.description?.trim(),
|
|
220
|
-
group: groupName
|
|
219
|
+
group: groupName
|
|
221
220
|
});
|
|
222
221
|
return acc;
|
|
223
222
|
}, [] as NavigationEntry[]),
|
|
@@ -242,7 +241,7 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
|
|
|
242
241
|
}, [] as NavigationEntry[])
|
|
243
242
|
];
|
|
244
243
|
|
|
245
|
-
const groupOrderValue = (groupName?: string): number => {
|
|
244
|
+
const groupOrderValue = (groupName?: string | null): number => {
|
|
246
245
|
if (groupName === NAVIGATION_ADMIN_GROUP_NAME) return 1;
|
|
247
246
|
return 0; // Other groups
|
|
248
247
|
};
|
|
@@ -266,7 +265,10 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
|
|
|
266
265
|
|
|
267
266
|
const collectedGroupsFromEntries = navigationEntries
|
|
268
267
|
.map(e => e.group)
|
|
269
|
-
.filter(
|
|
268
|
+
.filter((g): g is string => g !== null && Boolean(g));
|
|
269
|
+
|
|
270
|
+
// Check if there are any ungrouped entries
|
|
271
|
+
const hasUngroupedEntries = navigationEntries.some(e => e.group === null && e.type !== "admin");
|
|
270
272
|
|
|
271
273
|
// Preserve order from finalNavigationGroupMappings (persisted order)
|
|
272
274
|
const groupsFromMappings = finalNavigationGroupMappings.map(g => g.name);
|
|
@@ -284,7 +286,12 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
|
|
|
284
286
|
const uniqueGroupsArray = [...new Set(allDefinedGroups)];
|
|
285
287
|
const adminGroups = uniqueGroupsArray.filter(g => g === NAVIGATION_ADMIN_GROUP_NAME);
|
|
286
288
|
const nonAdminGroups = uniqueGroupsArray.filter(g => g !== NAVIGATION_ADMIN_GROUP_NAME);
|
|
287
|
-
|
|
289
|
+
// Place null (ungrouped) first if there are ungrouped entries
|
|
290
|
+
const uniqueGroups: (string | null)[] = [
|
|
291
|
+
...(hasUngroupedEntries ? [null] : []),
|
|
292
|
+
...nonAdminGroups,
|
|
293
|
+
...adminGroups
|
|
294
|
+
];
|
|
288
295
|
|
|
289
296
|
return {
|
|
290
297
|
allowDragAndDrop: plugins?.some(plugin => plugin.homePage?.allowDragAndDrop) ?? false,
|
|
@@ -682,12 +689,12 @@ async function resolveCMSViews(
|
|
|
682
689
|
return resolvedViews;
|
|
683
690
|
}
|
|
684
691
|
|
|
685
|
-
function getGroup(collectionOrView: EntityCollection<any, any> | CMSView) {
|
|
692
|
+
function getGroup(collectionOrView: EntityCollection<any, any> | CMSView): string | null {
|
|
686
693
|
const trimmed = collectionOrView.group?.trim();
|
|
687
694
|
if (!trimmed || trimmed === "") {
|
|
688
|
-
return
|
|
695
|
+
return null;
|
|
689
696
|
}
|
|
690
|
-
return trimmed
|
|
697
|
+
return trimmed;
|
|
691
698
|
}
|
|
692
699
|
|
|
693
700
|
function areCollectionListsEqual(a: EntityCollection[], b: EntityCollection[]) {
|
|
@@ -823,7 +830,7 @@ function computeNavigationGroups({
|
|
|
823
830
|
(collections ?? []).forEach(collection => {
|
|
824
831
|
const entry = collection.id ?? collection.path;
|
|
825
832
|
if (!assignedEntries.has(entry)) {
|
|
826
|
-
const groupName = getGroup(collection);
|
|
833
|
+
const groupName = getGroup(collection) ?? "__default__";
|
|
827
834
|
if (!unassignedGroupMap[groupName]) unassignedGroupMap[groupName] = [];
|
|
828
835
|
unassignedGroupMap[groupName].push(entry);
|
|
829
836
|
}
|
|
@@ -833,7 +840,7 @@ function computeNavigationGroups({
|
|
|
833
840
|
(views ?? []).forEach(view => {
|
|
834
841
|
const entry = view.path;
|
|
835
842
|
if (!assignedEntries.has(entry)) {
|
|
836
|
-
const groupName = getGroup(view);
|
|
843
|
+
const groupName = getGroup(view) ?? "__default__";
|
|
837
844
|
if (!unassignedGroupMap[groupName]) unassignedGroupMap[groupName] = [];
|
|
838
845
|
unassignedGroupMap[groupName].push(entry);
|
|
839
846
|
}
|
|
@@ -861,7 +868,7 @@ function computeNavigationGroups({
|
|
|
861
868
|
|
|
862
869
|
// Add collections
|
|
863
870
|
(collections ?? []).forEach(collection => {
|
|
864
|
-
const name = getGroup(collection);
|
|
871
|
+
const name = getGroup(collection) ?? "__default__";
|
|
865
872
|
const entry = collection.id ?? collection.path;
|
|
866
873
|
if (!groupMap[name]) groupMap[name] = [];
|
|
867
874
|
groupMap[name].push(entry);
|
|
@@ -869,7 +876,7 @@ function computeNavigationGroups({
|
|
|
869
876
|
|
|
870
877
|
// Add views
|
|
871
878
|
(views ?? []).forEach(view => {
|
|
872
|
-
const name = getGroup(view);
|
|
879
|
+
const name = getGroup(view) ?? "__default__";
|
|
873
880
|
const entry = view.path;
|
|
874
881
|
if (!groupMap[name]) groupMap[name] = [];
|
|
875
882
|
groupMap[name].push(entry);
|
|
@@ -10,7 +10,7 @@ const STORAGE_KEY_PREFIX = "firecms-collapsed-groups";
|
|
|
10
10
|
* @param groupNames - Array of group names to track
|
|
11
11
|
* @param namespace - Namespace for localStorage key (e.g., "home", "drawer") to allow independent state
|
|
12
12
|
*/
|
|
13
|
-
export function useCollapsedGroups(groupNames: string[], namespace: string = "default") {
|
|
13
|
+
export function useCollapsedGroups(groupNames: (string | null)[], namespace: string = "default") {
|
|
14
14
|
const storageKey = `${STORAGE_KEY_PREFIX}-${namespace}`;
|
|
15
15
|
|
|
16
16
|
// Load collapsed groups from localStorage on mount
|
|
@@ -37,7 +37,7 @@ export function useCollapsedGroups(groupNames: string[], namespace: string = "de
|
|
|
37
37
|
// Only clean up if we have actual groups loaded (avoid cleaning up during initial load)
|
|
38
38
|
if (groupNames.length === 0) return;
|
|
39
39
|
|
|
40
|
-
const currentGroupNames = new Set(groupNames);
|
|
40
|
+
const currentGroupNames = new Set(groupNames.map(g => g ?? "__default__"));
|
|
41
41
|
|
|
42
42
|
setCollapsedGroups(prev => {
|
|
43
43
|
const cleaned = Object.fromEntries(
|
|
@@ -56,12 +56,13 @@ export function useCollapsedGroups(groupNames: string[], namespace: string = "de
|
|
|
56
56
|
});
|
|
57
57
|
}, [groupNames]);
|
|
58
58
|
|
|
59
|
-
const isGroupCollapsed = useCallback((name
|
|
60
|
-
return !!collapsedGroups[name];
|
|
59
|
+
const isGroupCollapsed = useCallback((name?: string | null) => {
|
|
60
|
+
return !!collapsedGroups[name ?? "__default__"];
|
|
61
61
|
}, [collapsedGroups]);
|
|
62
62
|
|
|
63
|
-
const toggleGroupCollapsed = useCallback((name
|
|
64
|
-
|
|
63
|
+
const toggleGroupCollapsed = useCallback((name?: string | null) => {
|
|
64
|
+
const key = name ?? "__default__";
|
|
65
|
+
setCollapsedGroups(prev => ({ ...prev, [key]: !prev[key] }));
|
|
65
66
|
}, []);
|
|
66
67
|
|
|
67
68
|
return {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useTranslation as useI18nTranslation } from "react-i18next";
|
|
2
|
+
|
|
3
|
+
const FIRECMS_NS = "firecms_core";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Internal hook for translating FireCMS UI strings.
|
|
7
|
+
*
|
|
8
|
+
* Uses the `firecms_core` i18next namespace that is initialised by
|
|
9
|
+
* `FireCMSi18nProvider`. Do NOT use `react-i18next` directly in internal
|
|
10
|
+
* components — always go through this hook so the namespace is consistent.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const { t } = useTranslation();
|
|
14
|
+
* <Button>{t("save")}</Button>
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export function useTranslation() {
|
|
19
|
+
const { t, i18n } = useI18nTranslation(FIRECMS_NS);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Typed translation function scoped to FirecmsTranslations keys.
|
|
23
|
+
* Also supports i18next interpolation variables, e.g.
|
|
24
|
+
* t("add_to_field", { fieldName: "Tags" })
|
|
25
|
+
* t("error_deleting", { message: err.message })
|
|
26
|
+
*/
|
|
27
|
+
const typedT = (key: string, vars?: Record<string, string>): string =>
|
|
28
|
+
t(key, vars) as string;
|
|
29
|
+
|
|
30
|
+
return { t: typedT, i18n };
|
|
31
|
+
}
|