@firecms/core 3.1.0-canary.1df3b2c → 3.1.0-canary.501d471
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/EntityCollectionTable/internal/popup_field/useDraggable.d.ts +2 -2
- package/dist/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
- package/dist/components/EntityCollectionView/ViewModeToggle.d.ts +5 -10
- package/dist/components/ErrorBoundary.d.ts +4 -2
- 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/VirtualTable/VirtualTableHeader.d.ts +2 -1
- package/dist/components/VirtualTable/VirtualTableHeaderRow.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +6 -1
- package/dist/components/VirtualTable/types.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/image-bubble.d.ts +5 -0
- package/dist/editor/components/index.d.ts +16 -0
- package/dist/editor/components/table-bubble.d.ts +5 -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 +13 -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 +12 -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/components/ErrorFocus.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- 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 +5 -0
- package/dist/index.es.js +29682 -18363
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +29681 -18382
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useRestoreScroll.d.ts +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/analytics.d.ts +1 -1
- package/dist/types/collections.d.ts +46 -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 +23 -0
- package/dist/types/properties.d.ts +9 -8
- package/dist/types/storage.d.ts +1 -0
- package/dist/types/translations.d.ts +669 -0
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/index.d.ts +1 -0
- package/dist/util/lazy_eager.d.ts +7 -0
- package/dist/util/objects.d.ts +1 -0
- package/dist/util/resolutions.d.ts +2 -2
- package/dist/util/useStorageUploadController.d.ts +10 -1
- package/package.json +49 -13
- 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/EntityCollectionRowActions.tsx +9 -3
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
- package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +11 -11
- package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
- package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
- package/src/components/EntityCollectionView/EntityBoardCard.tsx +1 -1
- package/src/components/EntityCollectionView/EntityCard.tsx +4 -0
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +39 -46
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +71 -31
- 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 +37 -37
- package/src/components/EntityJsonPreview.tsx +2 -1
- 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/VirtualTable.tsx +121 -116
- package/src/components/VirtualTable/VirtualTableHeader.tsx +59 -56
- package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +9 -4
- package/src/components/VirtualTable/VirtualTableProps.tsx +7 -1
- package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +3 -3
- package/src/components/VirtualTable/types.tsx +1 -0
- package/src/components/common/default_entity_actions.tsx +4 -0
- package/src/components/common/useDataSourceTableController.tsx +12 -4
- package/src/components/index.tsx +1 -0
- package/src/core/DefaultAppBar.tsx +15 -11
- package/src/core/DefaultDrawer.tsx +8 -2
- package/src/core/DrawerNavigationGroup.tsx +5 -3
- package/src/core/EntityEditView.tsx +54 -8
- package/src/core/EntityEditViewFormActions.tsx +24 -17
- package/src/core/EntitySidePanel.tsx +34 -30
- package/src/core/FireCMS.tsx +33 -6
- package/src/core/field_configs.tsx +18 -11
- package/src/editor/components/SlashCommandMenu.tsx +516 -0
- package/src/editor/components/editor-bubble-item.tsx +32 -0
- package/src/editor/components/editor-bubble.tsx +118 -0
- package/src/editor/components/image-bubble.tsx +156 -0
- package/src/editor/components/index.ts +14 -0
- package/src/editor/components/table-bubble.tsx +165 -0
- package/src/editor/editor.tsx +455 -0
- package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
- package/src/editor/extensions/Image/index.ts +133 -0
- package/src/editor/extensions/Image.ts +159 -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 +480 -0
- package/src/editor/hooks/useProseMirror.ts +124 -0
- package/src/editor/hooks/useProseMirrorContext.ts +15 -0
- package/src/editor/index.ts +2 -0
- package/src/editor/markdown.ts +172 -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 +58 -0
- package/src/editor/plugins/inputrules.ts +82 -0
- package/src/editor/plugins/placeholderPlugin.ts +55 -0
- package/src/editor/plugins/slashCommandPlugin.ts +61 -0
- package/src/editor/schema.ts +240 -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 +108 -0
- package/src/editor/utils/remove_classes.ts +17 -0
- package/src/editor/utils/useDebouncedCallback.ts +25 -0
- package/src/form/EntityForm.tsx +149 -67
- package/src/form/EntityFormActions.tsx +19 -12
- package/src/form/PropertyFieldBinding.tsx +10 -8
- package/src/form/components/ErrorFocus.tsx +3 -3
- package/src/form/components/LocalChangesMenu.tsx +13 -13
- package/src/form/components/StorageItemPreview.tsx +3 -2
- package/src/form/components/StorageUploadProgress.tsx +18 -3
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +18 -5
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +22 -9
- package/src/form/field_bindings/BlockFieldBinding.tsx +26 -9
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +46 -24
- package/src/form/field_bindings/MapFieldBinding.tsx +27 -11
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +74 -37
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +15 -1
- package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +25 -11
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +25 -11
- package/src/form/field_bindings/RepeatFieldBinding.tsx +21 -6
- package/src/form/field_bindings/SelectFieldBinding.tsx +7 -5
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +110 -92
- package/src/form/field_bindings/SwitchFieldBinding.tsx +31 -14
- package/src/form/field_bindings/TextFieldBinding.tsx +10 -7
- package/src/form/field_bindings/UserSelectFieldBinding.tsx +7 -5
- package/src/hooks/index.tsx +1 -0
- package/src/hooks/useBuildNavigationController.tsx +49 -22
- package/src/hooks/useCollapsedGroups.ts +7 -6
- package/src/hooks/useTranslation.ts +31 -0
- package/src/hooks/useValidateAuthenticator.tsx +1 -1
- package/src/i18n/FireCMSi18nProvider.tsx +160 -0
- package/src/index.ts +5 -0
- package/src/internal/useBuildDataSource.ts +1 -2
- package/src/internal/useBuildSideEntityController.tsx +22 -20
- package/src/locales/de.ts +718 -0
- package/src/locales/en.ts +730 -0
- package/src/locales/es.ts +730 -0
- package/src/locales/fr.ts +718 -0
- package/src/locales/hi.ts +718 -0
- package/src/locales/it.ts +718 -0
- package/src/locales/pt.ts +727 -0
- package/src/preview/PropertyPreview.tsx +4 -2
- package/src/preview/components/ReferencePreview.tsx +2 -1
- package/src/preview/components/UrlComponentPreview.tsx +4 -2
- package/src/preview/components/UserPreview.tsx +3 -1
- package/src/preview/property_previews/MapPropertyPreview.tsx +49 -27
- package/src/routes/FireCMSRoute.tsx +63 -54
- package/src/types/analytics.ts +10 -0
- package/src/types/collections.ts +49 -0
- 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 +26 -0
- package/src/types/properties.ts +12 -10
- package/src/types/storage.ts +2 -1
- package/src/types/translations.ts +752 -0
- package/src/util/entities.ts +1 -1
- package/src/util/index.ts +1 -0
- package/src/util/join_collections.ts +10 -8
- package/src/util/lazy_eager.tsx +33 -0
- package/src/util/objects.ts +15 -0
- package/src/util/previews.ts +2 -2
- package/src/util/property_utils.tsx +1 -1
- package/src/util/resolutions.ts +5 -3
- package/src/util/useStorageUploadController.tsx +23 -29
package/src/form/EntityForm.tsx
CHANGED
|
@@ -40,7 +40,10 @@ import {
|
|
|
40
40
|
useSnackbarController
|
|
41
41
|
} from "../hooks";
|
|
42
42
|
import { Alert, CheckIcon, Chip, cls, EditIcon, NotesIcon, paperMixin, Tooltip, Typography } from "@firecms/ui";
|
|
43
|
-
import { Formex, FormexController, getIn, setIn, useCreateFormex
|
|
43
|
+
import { Formex, FormexController, getIn, setIn, useCreateFormex,
|
|
44
|
+
useFormex
|
|
45
|
+
} from "@firecms/formex";
|
|
46
|
+
import { useTranslation } from "../hooks";
|
|
44
47
|
import { useAnalyticsController } from "../hooks/useAnalyticsController";
|
|
45
48
|
import { FormEntry, FormLayout, LabelWithIconAndTooltip, PropertyFieldBinding } from "../form";
|
|
46
49
|
import { ValidationError } from "yup";
|
|
@@ -122,6 +125,35 @@ export function extractTouchedValues(values: any, touched: Record<string, boolea
|
|
|
122
125
|
return acc;
|
|
123
126
|
}
|
|
124
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Recursively removes empty plain objects `{}` and empty arrays `[]` from a value tree.
|
|
130
|
+
* This prevents ghost containers created by `setIn` intermediate path construction
|
|
131
|
+
* (e.g. `{ address: {} }` when only `address.city` was touched but value is undefined)
|
|
132
|
+
* from falsely triggering the unsaved local changes indicator.
|
|
133
|
+
*/
|
|
134
|
+
function removeEmptyContainers(obj: any): any {
|
|
135
|
+
if (Array.isArray(obj)) {
|
|
136
|
+
const cleaned = obj.map(removeEmptyContainers);
|
|
137
|
+
// Keep arrays even if they contain only nulls/undefined — that's intentional data
|
|
138
|
+
return cleaned;
|
|
139
|
+
}
|
|
140
|
+
if (obj && typeof obj === "object" && Object.getPrototypeOf(obj) === Object.prototype) {
|
|
141
|
+
const result: Record<string, any> = {};
|
|
142
|
+
for (const key of Object.keys(obj)) {
|
|
143
|
+
const cleaned = removeEmptyContainers(obj[key]);
|
|
144
|
+
// Skip empty plain objects
|
|
145
|
+
if (cleaned && typeof cleaned === "object" && !Array.isArray(cleaned)
|
|
146
|
+
&& Object.getPrototypeOf(cleaned) === Object.prototype
|
|
147
|
+
&& Object.keys(cleaned).length === 0) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
result[key] = cleaned;
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
return obj;
|
|
155
|
+
}
|
|
156
|
+
|
|
125
157
|
export function getChanges<T extends object>(source: Partial<T>, comparison: Partial<T>): Partial<T> {
|
|
126
158
|
const changes: Partial<T> = {};
|
|
127
159
|
|
|
@@ -181,30 +213,30 @@ export function getChanges<T extends object>(source: Partial<T>, comparison: Par
|
|
|
181
213
|
}
|
|
182
214
|
|
|
183
215
|
export function EntityForm<M extends Record<string, any>>({
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
216
|
+
path,
|
|
217
|
+
fullIdPath,
|
|
218
|
+
entityId: entityIdProp,
|
|
219
|
+
collection,
|
|
220
|
+
onValuesModified,
|
|
221
|
+
onIdChange,
|
|
222
|
+
onSaved,
|
|
223
|
+
entity,
|
|
224
|
+
initialDirtyValues,
|
|
225
|
+
onFormContextReady,
|
|
226
|
+
forceActionsAtTheBottom,
|
|
227
|
+
initialStatus,
|
|
228
|
+
className,
|
|
229
|
+
onStatusChange,
|
|
230
|
+
onEntityChange,
|
|
231
|
+
openEntityMode = "full_screen",
|
|
232
|
+
formex: formexProp,
|
|
233
|
+
disabled: disabledProp,
|
|
234
|
+
Builder,
|
|
235
|
+
EntityFormActionsComponent = EntityFormActions,
|
|
236
|
+
showDefaultActions = true,
|
|
237
|
+
showEntityPath = true,
|
|
238
|
+
children
|
|
239
|
+
}: EntityFormProps<M>) {
|
|
208
240
|
|
|
209
241
|
if (collection.customId && collection.formAutoSave) {
|
|
210
242
|
console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
|
|
@@ -212,6 +244,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
212
244
|
|
|
213
245
|
const sideEntityController = useSideEntityController();
|
|
214
246
|
const navigationController = useNavigationController();
|
|
247
|
+
const { t } = useTranslation();
|
|
215
248
|
|
|
216
249
|
const navigateBack = useCallback(() => {
|
|
217
250
|
if (openEntityMode === "side_panel") {
|
|
@@ -247,7 +280,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
247
280
|
const context = useFireCMSContext();
|
|
248
281
|
const analyticsController = useAnalyticsController();
|
|
249
282
|
|
|
250
|
-
const [underlyingChanges] = useState<Partial<EntityValues<M>>>({});
|
|
283
|
+
const [underlyingChanges, setUnderlyingChanges] = useState<Partial<EntityValues<M>>>({});
|
|
251
284
|
|
|
252
285
|
const [customIdLoading, setCustomIdLoading] = useState<boolean>(false);
|
|
253
286
|
|
|
@@ -328,7 +361,15 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
328
361
|
return [initialValues, initialDirty];
|
|
329
362
|
}, [autoApplyLocalChanges, localChangesDataRaw, baseInitialValues, initialDirtyValues]);
|
|
330
363
|
|
|
331
|
-
const hasLocalChanges =
|
|
364
|
+
const hasLocalChanges = useMemo(() => {
|
|
365
|
+
if (localChangesCleared || !localChangesDataRaw || Object.keys(localChangesDataRaw).length === 0) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
// Compare cached values against entity values to check for real differences
|
|
369
|
+
const entityValues = entity?.values ?? {};
|
|
370
|
+
const realChanges = getChanges(localChangesDataRaw as Partial<M>, entityValues as Partial<M>);
|
|
371
|
+
return Object.keys(realChanges).length > 0;
|
|
372
|
+
}, [localChangesCleared, localChangesDataRaw, entity?.values]);
|
|
332
373
|
|
|
333
374
|
const formex: FormexController<M> = formexProp ?? useCreateFormex<M>({
|
|
334
375
|
initialValues: initialValues as M,
|
|
@@ -348,8 +389,10 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
348
389
|
onValuesChangeDeferred: (values: M, controller: FormexController<M>) => {
|
|
349
390
|
const key = (status === "new" || status === "copy") ? path + "#new" : path + "/" + entityId;
|
|
350
391
|
if (controller.dirty) {
|
|
351
|
-
const touchedValues = extractTouchedValues(values, controller.touched);
|
|
352
|
-
|
|
392
|
+
const touchedValues = removeEmptyContainers(extractTouchedValues(values, controller.touched));
|
|
393
|
+
if (touchedValues && Object.keys(touchedValues).length > 0) {
|
|
394
|
+
saveEntityToCache(key, touchedValues);
|
|
395
|
+
}
|
|
353
396
|
}
|
|
354
397
|
},
|
|
355
398
|
validation: (values) => {
|
|
@@ -366,6 +409,15 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
366
409
|
useEffect(() => {
|
|
367
410
|
|
|
368
411
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
412
|
+
if (e.defaultPrevented) return;
|
|
413
|
+
const activeElement = document.activeElement as HTMLElement;
|
|
414
|
+
const isInput = activeElement && (
|
|
415
|
+
activeElement.tagName === "INPUT" ||
|
|
416
|
+
activeElement.tagName === "TEXTAREA" ||
|
|
417
|
+
activeElement.isContentEditable
|
|
418
|
+
);
|
|
419
|
+
if (isInput) return;
|
|
420
|
+
|
|
369
421
|
const isUndo = (e.metaKey || e.ctrlKey) && !e.shiftKey && e.key.toLowerCase() === "z";
|
|
370
422
|
const isRedo =
|
|
371
423
|
((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "z") ||
|
|
@@ -455,12 +507,12 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
455
507
|
}, [entityId, path, snackbarController]);
|
|
456
508
|
|
|
457
509
|
const saveEntity = ({
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
510
|
+
values,
|
|
511
|
+
previousValues,
|
|
512
|
+
entityId,
|
|
513
|
+
collection,
|
|
514
|
+
path
|
|
515
|
+
}: {
|
|
464
516
|
collection: EntityCollection<M>,
|
|
465
517
|
path: string,
|
|
466
518
|
entityId: string | undefined,
|
|
@@ -493,13 +545,13 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
493
545
|
};
|
|
494
546
|
|
|
495
547
|
const onSaveEntityRequest = async ({
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
548
|
+
collection,
|
|
549
|
+
path,
|
|
550
|
+
entityId,
|
|
551
|
+
values,
|
|
552
|
+
previousValues,
|
|
553
|
+
autoSave
|
|
554
|
+
}: EntityFormSaveParams<M>): Promise<void> => {
|
|
503
555
|
if (!status)
|
|
504
556
|
return;
|
|
505
557
|
if (autoSave) {
|
|
@@ -567,6 +619,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
567
619
|
}, [snackbarController]);
|
|
568
620
|
|
|
569
621
|
const pluginActions: React.ReactNode[] = [];
|
|
622
|
+
const pluginBeforeTitle: React.ReactNode[] = [];
|
|
570
623
|
const plugins = customizationController.plugins;
|
|
571
624
|
|
|
572
625
|
const actionsDisabled = disabled || formex.isSubmitting || (status === "existing" && !formex.dirty) || Boolean(disabledProp);
|
|
@@ -590,6 +643,12 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
590
643
|
key={`actions_${plugin.key}`} {...actionProps} />
|
|
591
644
|
: null
|
|
592
645
|
)).filter(Boolean));
|
|
646
|
+
pluginBeforeTitle.push(...plugins.map((plugin) => (
|
|
647
|
+
plugin.form?.BeforeTitle
|
|
648
|
+
? <plugin.form.BeforeTitle
|
|
649
|
+
key={`before_title_${plugin.key}`} {...actionProps} />
|
|
650
|
+
: null
|
|
651
|
+
)).filter(Boolean));
|
|
593
652
|
}
|
|
594
653
|
|
|
595
654
|
const titlePropertyKey = getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs);
|
|
@@ -631,21 +690,42 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
631
690
|
const modified = formex.dirty;
|
|
632
691
|
|
|
633
692
|
const uniqueFieldValidator: CustomFieldValidator = useCallback(({
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
693
|
+
name,
|
|
694
|
+
value
|
|
695
|
+
}) => dataSource.checkUniqueField(path, name, value, entityId, collection),
|
|
637
696
|
[dataSource, path, entityId]);
|
|
638
697
|
|
|
639
698
|
const validationSchema = useMemo(() => entityId
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
699
|
+
? getYupEntitySchema(
|
|
700
|
+
entityId,
|
|
701
|
+
resolvedCollection.properties,
|
|
702
|
+
uniqueFieldValidator)
|
|
703
|
+
: undefined,
|
|
645
704
|
[entityId, resolvedCollection.properties, uniqueFieldValidator]);
|
|
646
705
|
|
|
647
706
|
useOnAutoSave(autoSave, formex, lastSavedValues, save);
|
|
648
707
|
|
|
708
|
+
// Detect external changes to the entity (e.g. from onSnapshot after Admin SDK writes)
|
|
709
|
+
const prevEntityValuesRef = useRef<EntityValues<M> | undefined>(entity?.values);
|
|
710
|
+
useEffect(() => {
|
|
711
|
+
if (!entity?.values || status !== "existing") return;
|
|
712
|
+
const prev = prevEntityValuesRef.current;
|
|
713
|
+
prevEntityValuesRef.current = entity.values;
|
|
714
|
+
if (prev && !equal(prev, entity.values)) {
|
|
715
|
+
// Compute the diff between the old and new entity values
|
|
716
|
+
const changes: Partial<EntityValues<M>> = {};
|
|
717
|
+
const allKeys = new Set([...Object.keys(prev), ...Object.keys(entity.values)]);
|
|
718
|
+
for (const key of allKeys) {
|
|
719
|
+
if (!equal((prev as any)[key], (entity.values as any)[key])) {
|
|
720
|
+
(changes as any)[key] = (entity.values as any)[key];
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
if (Object.keys(changes).length > 0) {
|
|
724
|
+
setUnderlyingChanges(changes);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}, [entity?.values, status]);
|
|
728
|
+
|
|
649
729
|
useEffect(() => {
|
|
650
730
|
if (!autoSave && !formex.isSubmitting && underlyingChanges && entity) {
|
|
651
731
|
// we update the form fields from the Firestore data
|
|
@@ -699,8 +779,8 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
699
779
|
|
|
700
780
|
return (
|
|
701
781
|
<FormEntry propertyKey={key}
|
|
702
|
-
|
|
703
|
-
|
|
782
|
+
widthPercentage={widthPercentage}
|
|
783
|
+
key={`field_${key}`}>
|
|
704
784
|
<PropertyFieldBinding {...cmsFormFieldProps} />
|
|
705
785
|
</FormEntry>
|
|
706
786
|
);
|
|
@@ -713,7 +793,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
713
793
|
throw new Error("When using additional fields you need to provide a Builder or a value");
|
|
714
794
|
}
|
|
715
795
|
const child = Builder
|
|
716
|
-
? <Builder entity={entity} context={context}/>
|
|
796
|
+
? <Builder entity={entity} context={context} />
|
|
717
797
|
: <div className={"w-full"}>
|
|
718
798
|
{additionalField.value?.({
|
|
719
799
|
entity,
|
|
@@ -725,9 +805,9 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
725
805
|
<div key={`additional_${key}`} className={"w-full"}>
|
|
726
806
|
<LabelWithIconAndTooltip
|
|
727
807
|
propertyKey={key}
|
|
728
|
-
icon={<NotesIcon size={"small"}/>}
|
|
808
|
+
icon={<NotesIcon size={"small"} />}
|
|
729
809
|
title={additionalField.name}
|
|
730
|
-
className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>
|
|
810
|
+
className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"} />
|
|
731
811
|
<div
|
|
732
812
|
className={cls(paperMixin, "w-full min-h-14 p-4 md:p-6 overflow-x-scroll no-scrollbar")}>
|
|
733
813
|
<ErrorBoundary>
|
|
@@ -749,6 +829,8 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
749
829
|
|
|
750
830
|
const formView = <ErrorBoundary>
|
|
751
831
|
<>
|
|
832
|
+
{pluginBeforeTitle}
|
|
833
|
+
|
|
752
834
|
{!Builder && <div className={"w-full py-2 flex flex-col items-start my-4 lg:my-6"}>
|
|
753
835
|
<Typography
|
|
754
836
|
className={"my-4 flex-grow line-clamp-1 " + (collection.hideIdFromForm ? "mb-6" : "")}
|
|
@@ -758,7 +840,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
758
840
|
|
|
759
841
|
{!entity?.values && initialStatus === "existing" &&
|
|
760
842
|
<Alert color={"warning"} size={"small"} outerClassName={"w-full mb-4 text-xs"}>
|
|
761
|
-
|
|
843
|
+
{t("this_entity_not_exist")}
|
|
762
844
|
</Alert>}
|
|
763
845
|
|
|
764
846
|
{showEntityPath && <Alert color={"base"} outerClassName={"w-full"} size={"small"}>
|
|
@@ -772,27 +854,27 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
772
854
|
{children}
|
|
773
855
|
|
|
774
856
|
{initialEntityId && !entity && initialStatus !== "new" && <Alert color={"info"} size={"small"}>
|
|
775
|
-
|
|
857
|
+
{t("this_entity_not_exist")}
|
|
776
858
|
</Alert>}
|
|
777
859
|
|
|
778
860
|
{!Builder && !collection.hideIdFromForm &&
|
|
779
861
|
<CustomIdField customId={collection.customId}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
862
|
+
entityId={entityId}
|
|
863
|
+
status={status}
|
|
864
|
+
onChange={setEntityId}
|
|
865
|
+
error={entityIdError}
|
|
866
|
+
loading={customIdLoading}
|
|
867
|
+
entity={entity} />
|
|
786
868
|
}
|
|
787
869
|
|
|
788
870
|
{entityId && formContext && <>
|
|
789
871
|
<div className="mt-12 flex flex-col gap-8" ref={formRef}>
|
|
790
872
|
{formFields()}
|
|
791
|
-
<ErrorFocus containerRef={formRef}/>
|
|
873
|
+
<ErrorFocus containerRef={formRef} />
|
|
792
874
|
</div>
|
|
793
875
|
</>}
|
|
794
876
|
|
|
795
|
-
{forceActionsAtTheBottom && <div className="h-16"/>}
|
|
877
|
+
{forceActionsAtTheBottom && <div className="h-16" />}
|
|
796
878
|
</>
|
|
797
879
|
</ErrorBoundary>;
|
|
798
880
|
|
|
@@ -852,12 +934,12 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
852
934
|
{formex.dirty
|
|
853
935
|
? <Tooltip title={"This form has been modified"}>
|
|
854
936
|
<Chip size={"small"} className={"py-1"} colorScheme={"orangeDarker"}>
|
|
855
|
-
<EditIcon size={"smallest"}/>
|
|
937
|
+
<EditIcon size={"smallest"} />
|
|
856
938
|
</Chip>
|
|
857
939
|
</Tooltip>
|
|
858
940
|
: <Tooltip title={"The current form is in sync with the database"}>
|
|
859
941
|
<Chip size={"small"} className={"py-1"}>
|
|
860
|
-
<CheckIcon size={"smallest"}/>
|
|
942
|
+
<CheckIcon size={"smallest"} />
|
|
861
943
|
</Chip>
|
|
862
944
|
</Tooltip>}
|
|
863
945
|
</div>
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "@firecms/ui";
|
|
20
20
|
import { FormexController } from "@firecms/formex";
|
|
21
21
|
import { useFireCMSContext, useSideEntityController } from "../hooks";
|
|
22
|
+
import { useTranslation } from "../hooks/useTranslation";
|
|
22
23
|
|
|
23
24
|
export interface EntityFormActionsProps {
|
|
24
25
|
fullPath: string;
|
|
@@ -56,6 +57,7 @@ export function EntityFormActions({
|
|
|
56
57
|
|
|
57
58
|
const context = useFireCMSContext();
|
|
58
59
|
const sideEntityController = useSideEntityController();
|
|
60
|
+
const { t } = useTranslation();
|
|
59
61
|
|
|
60
62
|
return layout === "bottom"
|
|
61
63
|
? buildBottomActions({
|
|
@@ -72,7 +74,8 @@ export function EntityFormActions({
|
|
|
72
74
|
openEntityMode,
|
|
73
75
|
navigateBack,
|
|
74
76
|
formContext,
|
|
75
|
-
formex
|
|
77
|
+
formex,
|
|
78
|
+
t
|
|
76
79
|
})
|
|
77
80
|
: buildSideActions({
|
|
78
81
|
fullPath,
|
|
@@ -88,7 +91,8 @@ export function EntityFormActions({
|
|
|
88
91
|
openEntityMode,
|
|
89
92
|
navigateBack,
|
|
90
93
|
formContext,
|
|
91
|
-
formex
|
|
94
|
+
formex,
|
|
95
|
+
t
|
|
92
96
|
});
|
|
93
97
|
}
|
|
94
98
|
|
|
@@ -108,6 +112,7 @@ type ActionsViewProps<M extends object> = {
|
|
|
108
112
|
navigateBack: () => void;
|
|
109
113
|
formContext: FormContext,
|
|
110
114
|
formex: FormexController<any>;
|
|
115
|
+
t: (key: any, vars?: Record<string, string>) => string;
|
|
111
116
|
};
|
|
112
117
|
|
|
113
118
|
function buildBottomActions<M extends object>({
|
|
@@ -125,7 +130,8 @@ function buildBottomActions<M extends object>({
|
|
|
125
130
|
openEntityMode,
|
|
126
131
|
navigateBack,
|
|
127
132
|
formContext,
|
|
128
|
-
formex
|
|
133
|
+
formex,
|
|
134
|
+
t
|
|
129
135
|
}: ActionsViewProps<M>) {
|
|
130
136
|
|
|
131
137
|
const hasErrors = Object.keys(formex.errors).length > 0 && formex.submitCount > 0;
|
|
@@ -165,16 +171,16 @@ function buildBottomActions<M extends object>({
|
|
|
165
171
|
<Button variant="text" disabled={disabled || formex.isSubmitting}
|
|
166
172
|
color={"primary"}
|
|
167
173
|
type="reset">
|
|
168
|
-
{status === "existing" ? "
|
|
174
|
+
{status === "existing" ? t("discard") : t("clear")}
|
|
169
175
|
</Button>
|
|
170
176
|
<Button variant={"filled"}
|
|
171
177
|
color="primary"
|
|
172
178
|
type="submit"
|
|
173
179
|
disabled={disabled || formex.isSubmitting}
|
|
174
180
|
startIcon={hasErrors ? <ErrorIcon/> : undefined}>
|
|
175
|
-
{status === "existing" && "
|
|
176
|
-
{status === "copy" && "
|
|
177
|
-
{status === "new" && "
|
|
181
|
+
{status === "existing" && t("save")}
|
|
182
|
+
{status === "copy" && t("create_copy")}
|
|
183
|
+
{status === "new" && t("create")}
|
|
178
184
|
</Button>
|
|
179
185
|
|
|
180
186
|
</DialogActions>;
|
|
@@ -193,7 +199,8 @@ function buildSideActions<M extends object>({
|
|
|
193
199
|
disabled,
|
|
194
200
|
status,
|
|
195
201
|
pluginActions,
|
|
196
|
-
formex
|
|
202
|
+
formex,
|
|
203
|
+
t
|
|
197
204
|
}: ActionsViewProps<M>) {
|
|
198
205
|
|
|
199
206
|
const hasErrors = Object.keys(formex.errors).length > 0 && formex.submitCount > 0;
|
|
@@ -207,12 +214,12 @@ function buildSideActions<M extends object>({
|
|
|
207
214
|
size={"large"}
|
|
208
215
|
startIcon={hasErrors ? <ErrorIcon/> : undefined}
|
|
209
216
|
disabled={disabled || formex.isSubmitting}>
|
|
210
|
-
{status === "existing" && "
|
|
211
|
-
{status === "copy" && "
|
|
212
|
-
{status === "new" && "
|
|
217
|
+
{status === "existing" && t("save")}
|
|
218
|
+
{status === "copy" && t("create_copy")}
|
|
219
|
+
{status === "new" && t("create")}
|
|
213
220
|
</LoadingButton>
|
|
214
221
|
<Button fullWidth={true} variant="text" disabled={disabled || formex.isSubmitting} type="reset">
|
|
215
|
-
{status === "existing" ? "
|
|
222
|
+
{status === "existing" ? t("discard") : t("clear")}
|
|
216
223
|
</Button>
|
|
217
224
|
|
|
218
225
|
{pluginActions}
|
|
@@ -17,10 +17,10 @@ import {
|
|
|
17
17
|
import { ReadOnlyFieldBinding } from "./field_bindings/ReadOnlyFieldBinding";
|
|
18
18
|
|
|
19
19
|
import { isHidden, isPropertyBuilder, isReadOnly, resolveProperty } from "../util";
|
|
20
|
-
import { useAuthController, useCustomizationController } from "../hooks";
|
|
20
|
+
import { useAuthController, useCustomizationController, useTranslation } from "../hooks";
|
|
21
21
|
import { Typography } from "@firecms/ui";
|
|
22
22
|
import { getFieldConfig, getFieldId } from "../core";
|
|
23
|
-
import { ErrorBoundary } from "../components";
|
|
23
|
+
import { ErrorBoundary, CircularProgressCenter } from "../components";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* This component renders a form field creating the corresponding configuration
|
|
@@ -137,7 +137,7 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Rec
|
|
|
137
137
|
}
|
|
138
138
|
const configProperty = resolveProperty({
|
|
139
139
|
propertyKey,
|
|
140
|
-
propertyOrBuilder: propertyConfig.property,
|
|
140
|
+
propertyOrBuilder: propertyConfig.property as any,
|
|
141
141
|
values: fieldProps.form.values,
|
|
142
142
|
path: context.path,
|
|
143
143
|
entityId: context.entityId,
|
|
@@ -145,7 +145,7 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Rec
|
|
|
145
145
|
index,
|
|
146
146
|
authController
|
|
147
147
|
});
|
|
148
|
-
Component = configProperty
|
|
148
|
+
Component = configProperty?.Field as ComponentType<FieldProps<T>> | undefined;
|
|
149
149
|
}
|
|
150
150
|
if (!Component) {
|
|
151
151
|
console.warn(`No field component found for property ${propertyKey}`);
|
|
@@ -213,6 +213,7 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
|
|
|
213
213
|
formexFieldProps: FormexFieldProps<T, any>
|
|
214
214
|
}) {
|
|
215
215
|
|
|
216
|
+
const { t } = useTranslation();
|
|
216
217
|
const { plugins } = useCustomizationController();
|
|
217
218
|
|
|
218
219
|
const customFieldProps: any = property.customProps;
|
|
@@ -286,12 +287,13 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
|
|
|
286
287
|
|
|
287
288
|
return (
|
|
288
289
|
<ErrorBoundary>
|
|
289
|
-
|
|
290
|
-
|
|
290
|
+
<React.Suspense fallback={<CircularProgressCenter />}>
|
|
291
|
+
<UsedComponent {...cmsFieldProps} />
|
|
292
|
+
</React.Suspense>
|
|
291
293
|
|
|
292
294
|
{underlyingValueHasChanged && !isSubmitting &&
|
|
293
295
|
<Typography variant={"caption"} className={"ml-3.5"}>
|
|
294
|
-
|
|
296
|
+
{t("value_updated_elsewhere")}
|
|
295
297
|
</Typography>}
|
|
296
298
|
|
|
297
299
|
</ErrorBoundary>);
|
|
@@ -302,7 +304,7 @@ const shouldPropertyReRender = (property: PropertyOrBuilder | ResolvedProperty,
|
|
|
302
304
|
if (plugins?.some((plugin) => plugin.form?.fieldBuilder)) {
|
|
303
305
|
return true;
|
|
304
306
|
}
|
|
305
|
-
if (isPropertyBuilder(property)) {
|
|
307
|
+
if (isPropertyBuilder(property as any)) {
|
|
306
308
|
return true;
|
|
307
309
|
}
|
|
308
310
|
const defAProperty = property as Property | ResolvedProperty;
|
|
@@ -2,9 +2,9 @@ import React, { useEffect, useRef } from "react";
|
|
|
2
2
|
import { useFormex } from "@firecms/formex";
|
|
3
3
|
|
|
4
4
|
export const ErrorFocus = ({ containerRef }:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
{
|
|
6
|
+
containerRef?: React.RefObject<HTMLDivElement | null>
|
|
7
|
+
}) => {
|
|
8
8
|
const {
|
|
9
9
|
isValidating,
|
|
10
10
|
errors,
|
|
@@ -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>
|