@firecms/core 3.3.0-canary.d3242eb → 3.4.0-canary.00f9be5
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/README.md +4 -0
- package/dist/app/useApp.d.ts +1 -0
- package/dist/components/EntityCollectionTable/column_utils.d.ts +4 -3
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +6 -1
- package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +3 -1
- package/dist/components/EntityCollectionView/EntityCollectionBoardView.d.ts +3 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +8 -0
- package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +3 -1
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +3 -1
- package/dist/components/EntityCollectionView/FiltersDialog.d.ts +2 -1
- package/dist/components/EntityCollectionView/useBoardDataController.d.ts +3 -1
- package/dist/components/EntityPreview.d.ts +3 -1
- package/dist/components/ErrorBoundary.d.ts +1 -3
- package/dist/components/LicenseBanner.d.ts +23 -0
- package/dist/components/ReferenceTable/ReferenceSelectionTable.d.ts +10 -1
- package/dist/components/SelectableTable/filters/filter_text_input.d.ts +30 -0
- package/dist/components/SelectableTable/filters/filter_text_input.test.d.ts +1 -0
- package/dist/components/common/table_url_params.d.ts +24 -0
- package/dist/components/common/useDataSourceTableController.d.ts +9 -1
- package/dist/components/common/useTableSearchHelper.d.ts +9 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/contexts/LicenseStatusContext.d.ts +16 -0
- package/dist/core/DefaultDrawer.d.ts +10 -3
- package/dist/core/EntityEditView.d.ts +9 -1
- package/dist/core/EntityEditViewFormActions.d.ts +1 -1
- package/dist/core/field_configs.d.ts +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/plugin_translations.d.ts +38 -0
- package/dist/core/pro_plugins.d.ts +40 -0
- package/dist/de-BF5vcQPY.js +703 -0
- package/dist/de-BF5vcQPY.js.map +1 -0
- package/dist/editor/extensions/Image.d.ts +11 -0
- package/dist/editor/index.d.ts +2 -1
- package/dist/editor/lazy_editor.d.ts +6 -0
- package/dist/editor-BSo_aS1I.js +4563 -0
- package/dist/editor-BSo_aS1I.js.map +1 -0
- package/dist/form/EntityForm.d.ts +8 -1
- package/dist/form/EntityFormActions.d.ts +2 -0
- package/dist/form/field_bindings/GeopointFieldBinding.d.ts +5 -0
- package/dist/form/index.d.ts +1 -0
- package/dist/fr-DH9_wyFI.js +703 -0
- package/dist/fr-DH9_wyFI.js.map +1 -0
- package/dist/hi-BM8dm1Lu.js +703 -0
- package/dist/hi-BM8dm1Lu.js.map +1 -0
- package/dist/hooks/data/delete.d.ts +1 -1
- package/dist/hooks/data/save.d.ts +1 -1
- package/dist/hooks/data/useCollectionFetch.d.ts +7 -1
- package/dist/hooks/data/useEntityFetch.d.ts +9 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useLicenseStatus.d.ts +16 -0
- package/dist/hooks/useProjectLog.d.ts +11 -6
- package/dist/hooks/useTranslation.d.ts +1 -1
- package/dist/i18n/__tests__/FireCMSi18nProvider.test.d.ts +1 -0
- package/dist/i18n/__tests__/nested_provider_lifecycle.test.d.ts +1 -0
- package/dist/index.es.js +4429 -10479
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +9313 -6582
- package/dist/index.umd.js.map +1 -1
- package/dist/it-D3K1gfe4.js +703 -0
- package/dist/it-D3K1gfe4.js.map +1 -0
- package/dist/locales/pl.d.ts +2 -0
- package/dist/pl-CP1zeLzn.js +705 -0
- package/dist/pl-CP1zeLzn.js.map +1 -0
- package/dist/preview/index.d.ts +1 -0
- package/dist/preview/property_previews/GeopointPropertyPreview.d.ts +4 -0
- package/dist/pt-Cus7eKQR.js +705 -0
- package/dist/pt-Cus7eKQR.js.map +1 -0
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/collections.d.ts +31 -0
- package/dist/types/datasource.d.ts +67 -4
- package/dist/types/entities.d.ts +21 -1
- package/dist/types/entity_actions.d.ts +6 -0
- package/dist/types/entity_callbacks.d.ts +21 -0
- package/dist/types/firecms.d.ts +18 -1
- package/dist/types/firecms_context.d.ts +8 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/license.d.ts +55 -0
- package/dist/types/navigation.d.ts +34 -5
- package/dist/types/plugins.d.ts +22 -1
- package/dist/types/properties.d.ts +17 -1
- package/dist/types/side_entity_controller.d.ts +7 -0
- package/dist/types/translations.d.ts +54 -5
- package/dist/util/__tests__/collections.test.d.ts +1 -0
- package/dist/util/__tests__/urls.test.d.ts +1 -0
- package/dist/util/collections.d.ts +1 -1
- package/dist/util/date_fns_locales.d.ts +18 -0
- package/dist/util/entities.d.ts +1 -0
- package/dist/util/entity_cache.d.ts +11 -0
- package/dist/util/geopoint.d.ts +22 -0
- package/dist/util/index.d.ts +3 -0
- package/dist/util/lazy_eager.d.ts +3 -3
- package/dist/util/navigation_blocking.d.ts +22 -0
- package/dist/util/navigation_from_path.d.ts +17 -0
- package/dist/util/navigation_utils.d.ts +107 -3
- package/dist/util/parent_references_from_path.d.ts +16 -0
- package/dist/util/paths.d.ts +28 -0
- package/dist/util/permissions.d.ts +10 -4
- package/dist/util/urls.d.ts +22 -0
- package/package.json +17 -11
- package/src/app/Scaffold.tsx +54 -45
- package/src/app/useApp.tsx +1 -0
- package/src/components/DeleteEntityDialog.tsx +2 -0
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +3 -1
- package/src/components/EntityCollectionTable/column_utils.tsx +11 -19
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +7 -0
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +5 -1
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +12 -0
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +71 -17
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +6 -2
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +8 -1
- package/src/components/EntityCollectionView/FiltersDialog.tsx +39 -28
- package/src/components/EntityCollectionView/useBoardDataController.tsx +17 -1
- package/src/components/EntityPreview.tsx +45 -41
- package/src/components/ErrorBoundary.tsx +12 -18
- package/src/components/HomePage/NavigationCardBinding.tsx +6 -3
- package/src/components/LicenseBanner.tsx +207 -0
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +19 -2
- package/src/components/ReferenceWidget.tsx +1 -1
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +2 -2
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +33 -7
- package/src/components/SelectableTable/filters/filter_text_input.test.ts +184 -0
- package/src/components/SelectableTable/filters/filter_text_input.ts +71 -0
- package/src/components/VirtualTable/VirtualTable.tsx +19 -8
- package/src/components/common/default_entity_actions.tsx +22 -2
- package/src/components/common/table_url_params.ts +172 -0
- package/src/components/common/useDataSourceTableController.tsx +126 -163
- package/src/components/common/useTableSearchHelper.ts +26 -2
- package/src/components/index.tsx +1 -0
- package/src/contexts/LicenseStatusContext.tsx +50 -0
- package/src/contexts/SnackbarProvider.tsx +14 -1
- package/src/core/DefaultAppBar.tsx +28 -16
- package/src/core/DefaultDrawer.tsx +151 -65
- package/src/core/DrawerNavigationGroup.tsx +27 -29
- package/src/core/DrawerNavigationItem.tsx +10 -12
- package/src/core/EntityEditView.tsx +77 -35
- package/src/core/EntityEditViewFormActions.tsx +3 -2
- package/src/core/EntitySidePanel.tsx +8 -4
- package/src/core/FireCMS.tsx +73 -47
- package/src/core/SideDialogs.tsx +44 -1
- package/src/core/field_configs.tsx +15 -0
- package/src/core/index.tsx +1 -0
- package/src/core/plugin_translations.ts +87 -0
- package/src/core/pro_plugins.ts +79 -0
- package/src/editor/components/SlashCommandMenu.tsx +4 -1
- package/src/editor/extensions/Image.ts +17 -1
- package/src/editor/hooks/useProseMirror.ts +5 -0
- package/src/editor/index.ts +11 -1
- package/src/editor/lazy_editor.tsx +33 -0
- package/src/form/EntityForm.tsx +31 -10
- package/src/form/EntityFormActions.tsx +2 -0
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +1 -1
- package/src/form/field_bindings/GeopointFieldBinding.tsx +139 -0
- package/src/form/index.tsx +1 -0
- package/src/hooks/data/delete.ts +8 -0
- package/src/hooks/data/save.ts +6 -1
- package/src/hooks/data/useCollectionFetch.tsx +21 -1
- package/src/hooks/data/useEntityFetch.tsx +35 -4
- package/src/hooks/index.tsx +2 -0
- package/src/hooks/useBuildNavigationController.tsx +75 -16
- package/src/hooks/useFireCMSContext.tsx +7 -3
- package/src/hooks/useLicenseStatus.tsx +32 -0
- package/src/hooks/useProjectLog.tsx +27 -9
- package/src/hooks/useResolvedNavigationFrom.tsx +3 -1
- package/src/hooks/useTranslation.ts +3 -1
- package/src/hooks/useValidateAuthenticator.tsx +4 -0
- package/src/i18n/FireCMSi18nProvider.tsx +199 -23
- package/src/i18n/__tests__/FireCMSi18nProvider.test.tsx +91 -0
- package/src/i18n/__tests__/nested_provider_lifecycle.test.tsx +67 -0
- package/src/internal/useBuildDataSource.ts +37 -15
- package/src/internal/useBuildSideEntityController.tsx +20 -5
- package/src/locales/de.ts +37 -7
- package/src/locales/en.ts +41 -9
- package/src/locales/es.ts +37 -7
- package/src/locales/fr.ts +37 -7
- package/src/locales/hi.ts +37 -7
- package/src/locales/it.ts +37 -7
- package/src/locales/pl.ts +752 -0
- package/src/locales/pt.ts +37 -7
- package/src/preview/PropertyPreview.tsx +12 -0
- package/src/preview/components/DatePreview.tsx +2 -4
- package/src/preview/components/ReferencePreview.tsx +7 -3
- package/src/preview/components/StorageThumbnail.tsx +24 -2
- package/src/preview/index.ts +1 -0
- package/src/preview/property_previews/GeopointPropertyPreview.tsx +23 -0
- package/src/routes/FireCMSRoute.tsx +54 -27
- package/src/types/analytics.ts +3 -0
- package/src/types/collections.ts +34 -0
- package/src/types/datasource.ts +67 -4
- package/src/types/entities.ts +24 -1
- package/src/types/entity_actions.tsx +6 -0
- package/src/types/entity_callbacks.ts +24 -0
- package/src/types/firecms.tsx +19 -1
- package/src/types/firecms_context.tsx +9 -0
- package/src/types/index.ts +1 -0
- package/src/types/license.ts +63 -0
- package/src/types/navigation.ts +35 -5
- package/src/types/plugins.tsx +23 -1
- package/src/types/properties.ts +17 -0
- package/src/types/side_entity_controller.tsx +7 -0
- package/src/types/translations.ts +54 -5
- package/src/util/__tests__/collections.test.ts +32 -0
- package/src/util/__tests__/urls.test.ts +131 -0
- package/src/util/collections.ts +1 -1
- package/src/util/date_fns_locales.ts +168 -0
- package/src/util/entities.ts +16 -1
- package/src/util/entity_cache.ts +18 -0
- package/src/util/geopoint.ts +81 -0
- package/src/util/index.ts +3 -0
- package/src/util/lazy_eager.tsx +30 -8
- package/src/util/navigation_blocking.ts +45 -0
- package/src/util/navigation_from_path.ts +47 -7
- package/src/util/navigation_utils.ts +250 -5
- package/src/util/parent_references_from_path.ts +45 -4
- package/src/util/paths.ts +40 -3
- package/src/util/permissions.ts +22 -10
- package/src/util/previews.ts +8 -1
- package/src/util/resolutions.ts +11 -0
- package/src/util/urls.ts +52 -0
- package/src/util/useStorageUploadController.tsx +6 -1
|
@@ -1,17 +1,76 @@
|
|
|
1
|
-
import React, { PropsWithChildren, useEffect, useRef } from "react";
|
|
1
|
+
import React, { PropsWithChildren, useContext, useEffect, useMemo, useRef } from "react";
|
|
2
2
|
import i18next, { i18n } from "i18next";
|
|
3
|
-
import { I18nextProvider, initReactI18next } from "react-i18next";
|
|
3
|
+
import { I18nContext, I18nextProvider, initReactI18next } from "react-i18next";
|
|
4
4
|
import { en } from "../locales/en";
|
|
5
5
|
import { es } from "../locales/es";
|
|
6
|
-
import { de } from "../locales/de";
|
|
7
|
-
import { fr } from "../locales/fr";
|
|
8
|
-
import { it } from "../locales/it";
|
|
9
|
-
import { hi } from "../locales/hi";
|
|
10
|
-
import { pt } from "../locales/pt";
|
|
11
6
|
import { FireCMSTranslations } from "../types/translations";
|
|
12
7
|
|
|
13
8
|
const FIRECMS_NS = "firecms_core";
|
|
14
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Translations that are not part of the bundle every app downloads.
|
|
12
|
+
*
|
|
13
|
+
* All eight locales used to be imported statically, which put ~305KB of strings
|
|
14
|
+
* — every language, for every user — into `@firecms/core` and therefore onto the
|
|
15
|
+
* startup path of every FireCMS app. Only the active language is ever read.
|
|
16
|
+
*
|
|
17
|
+
* `en` and `es` stay static: `en` is the fallback language and the base every
|
|
18
|
+
* unknown locale is seeded from, and both are re-exported from the package root,
|
|
19
|
+
* so a consumer may be importing them by name.
|
|
20
|
+
*
|
|
21
|
+
* The specifiers are written out rather than built from a template so bundlers
|
|
22
|
+
* can see them and emit one chunk per locale.
|
|
23
|
+
*/
|
|
24
|
+
const localeLoaders: Record<string, () => Promise<FireCMSTranslations>> = {
|
|
25
|
+
de: () => import("../locales/de").then((m) => m.de),
|
|
26
|
+
fr: () => import("../locales/fr").then((m) => m.fr),
|
|
27
|
+
it: () => import("../locales/it").then((m) => m.it),
|
|
28
|
+
hi: () => import("../locales/hi").then((m) => m.hi),
|
|
29
|
+
pt: () => import("../locales/pt").then((m) => m.pt),
|
|
30
|
+
pl: () => import("../locales/pl").then((m) => m.pl)
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Locale bundles available synchronously: the two static ones plus anything a
|
|
35
|
+
* previous provider already fetched. `buildResources` reads this, so a rebuild
|
|
36
|
+
* triggered by a changing `translations` prop cannot overwrite a lazily loaded
|
|
37
|
+
* language with the English base it would otherwise fall back to.
|
|
38
|
+
*/
|
|
39
|
+
const loadedLocales: Record<string, FireCMSTranslations> = {
|
|
40
|
+
en,
|
|
41
|
+
es
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const inFlightLocales: Record<string, Promise<void>> = {};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Fetch a locale bundle into `loadedLocales`.
|
|
48
|
+
*
|
|
49
|
+
* Returns undefined when the bundle is already in memory or there is nothing to
|
|
50
|
+
* fetch, so callers can stay synchronous in the common case.
|
|
51
|
+
*/
|
|
52
|
+
function loadLocale(locale?: string): Promise<void> | undefined {
|
|
53
|
+
if (!locale) return undefined;
|
|
54
|
+
const language = locale.split("-")[0];
|
|
55
|
+
if (loadedLocales[language]) return undefined;
|
|
56
|
+
const loader = localeLoaders[language];
|
|
57
|
+
if (!loader) return undefined;
|
|
58
|
+
if (!inFlightLocales[language]) {
|
|
59
|
+
inFlightLocales[language] = loader()
|
|
60
|
+
.then((bundle) => {
|
|
61
|
+
loadedLocales[language] = bundle;
|
|
62
|
+
})
|
|
63
|
+
.catch(() => {
|
|
64
|
+
// A locale that fails to load falls back to English, which is
|
|
65
|
+
// what an unknown locale has always done.
|
|
66
|
+
})
|
|
67
|
+
.finally(() => {
|
|
68
|
+
delete inFlightLocales[language];
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return inFlightLocales[language];
|
|
72
|
+
}
|
|
73
|
+
|
|
15
74
|
export const FIRECMS_LOCALE_STORAGE_KEY = "firecms_locale";
|
|
16
75
|
|
|
17
76
|
/** DeepPartial helper — allows partial overrides at any nesting level */
|
|
@@ -49,21 +108,44 @@ export function FireCMSi18nProvider({
|
|
|
49
108
|
translations,
|
|
50
109
|
children
|
|
51
110
|
}: PropsWithChildren<FireCMSi18nProviderProps>) {
|
|
111
|
+
|
|
112
|
+
// A FireCMS i18next instance already in context means this provider is nested —
|
|
113
|
+
// e.g. FireCMSCloudApp mounts one inside the one the host app already mounted.
|
|
114
|
+
// A second instance built only from the defaults would shadow the parent's and
|
|
115
|
+
// silently drop every translation the host registered, leaving the host's own
|
|
116
|
+
// strings to render as raw keys.
|
|
117
|
+
//
|
|
118
|
+
// So the nested instance is *seeded* from the parent's resources rather than
|
|
119
|
+
// sharing them. Sharing one mutable instance was the obvious alternative and is
|
|
120
|
+
// wrong: bundles added by one provider outlive it, so opening one project's app
|
|
121
|
+
// and then switching to another left the first project's translations answering
|
|
122
|
+
// for keys the second never defined. Copying keeps each provider's strings to
|
|
123
|
+
// itself, which is also how this behaved before nesting was handled at all.
|
|
124
|
+
const parentI18n = useContext(I18nContext)?.i18n;
|
|
125
|
+
const parentInstance = parentI18n?.hasResourceBundle?.("en", FIRECMS_NS)
|
|
126
|
+
? parentI18n
|
|
127
|
+
: undefined;
|
|
128
|
+
|
|
52
129
|
const i18nRef = useRef<i18n | null>(null);
|
|
53
130
|
const [ready, setReady] = React.useState(false);
|
|
54
131
|
|
|
55
132
|
if (!i18nRef.current) {
|
|
56
133
|
const instance = i18next.createInstance();
|
|
57
134
|
|
|
58
|
-
//
|
|
59
|
-
|
|
135
|
+
// English baseline + this provider's overrides, with anything the parent
|
|
136
|
+
// already resolved layered in underneath the overrides.
|
|
137
|
+
const resources = buildResourcesWithParent(translations, parentInstance);
|
|
60
138
|
|
|
61
|
-
let initialLocale = locale;
|
|
139
|
+
let initialLocale = parentInstance?.language ?? locale;
|
|
62
140
|
if (typeof window !== "undefined") {
|
|
63
141
|
const stored = localStorage.getItem(FIRECMS_LOCALE_STORAGE_KEY);
|
|
64
142
|
if (stored) initialLocale = stored;
|
|
65
143
|
}
|
|
66
144
|
|
|
145
|
+
// Kicked off before init so the request is in flight while i18next sets
|
|
146
|
+
// itself up, rather than after it.
|
|
147
|
+
const pendingInitialLocale = loadLocale(initialLocale);
|
|
148
|
+
|
|
67
149
|
instance
|
|
68
150
|
.use(initReactI18next)
|
|
69
151
|
.init({
|
|
@@ -76,19 +158,54 @@ export function FireCMSi18nProvider({
|
|
|
76
158
|
// React already escapes — don't double-escape
|
|
77
159
|
escapeValue: false,
|
|
78
160
|
},
|
|
161
|
+
react: {
|
|
162
|
+
// Locale bundles now arrive after init, and a bundle landing
|
|
163
|
+
// in the store raises an `added` event rather than
|
|
164
|
+
// `languageChanged`. react-i18next binds only the latter by
|
|
165
|
+
// default, so without this a language switched from the
|
|
166
|
+
// toggle would fetch its strings, put them in the store, and
|
|
167
|
+
// leave the UI sitting on the English fallback.
|
|
168
|
+
bindI18nStore: "added"
|
|
169
|
+
},
|
|
79
170
|
}, () => {
|
|
80
|
-
|
|
171
|
+
if (!pendingInitialLocale) {
|
|
172
|
+
setReady(true);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// Holding the first render until the active language has arrived
|
|
176
|
+
// keeps a non-English user from seeing English strings repaint.
|
|
177
|
+
// Only locales outside the static pair wait, and only on the
|
|
178
|
+
// first mount.
|
|
179
|
+
pendingInitialLocale.then(() => {
|
|
180
|
+
applyLoadedLocale(instance, initialLocale, translations, parentInstance);
|
|
181
|
+
setReady(true);
|
|
182
|
+
});
|
|
81
183
|
});
|
|
82
184
|
|
|
83
185
|
instance.on("languageChanged", (lng) => {
|
|
84
186
|
if (typeof window !== "undefined") {
|
|
85
187
|
localStorage.setItem(FIRECMS_LOCALE_STORAGE_KEY, lng);
|
|
86
188
|
}
|
|
189
|
+
// Switching to a language whose bundle has not been fetched yet
|
|
190
|
+
// renders the English fallback until it lands.
|
|
191
|
+
loadLocale(lng)?.then(() => applyLoadedLocale(instance, lng, translations, parentInstance));
|
|
87
192
|
});
|
|
88
193
|
|
|
89
194
|
i18nRef.current = instance;
|
|
90
195
|
}
|
|
91
196
|
|
|
197
|
+
// Follow the parent's language, so a switch made outside this provider is not
|
|
198
|
+
// stranded on the other side of the boundary.
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (!parentInstance || !i18nRef.current) return;
|
|
201
|
+
const instance = i18nRef.current;
|
|
202
|
+
const follow = (lng: string) => {
|
|
203
|
+
if (instance.language !== lng) instance.changeLanguage(lng);
|
|
204
|
+
};
|
|
205
|
+
parentInstance.on("languageChanged", follow);
|
|
206
|
+
return () => { parentInstance.off("languageChanged", follow); };
|
|
207
|
+
}, [parentInstance]);
|
|
208
|
+
|
|
92
209
|
// When `locale` prop changes, switch language on the existing instance
|
|
93
210
|
// ONLY if the user hasn't explicitly set a preference
|
|
94
211
|
useEffect(() => {
|
|
@@ -100,10 +217,16 @@ export function FireCMSi18nProvider({
|
|
|
100
217
|
}
|
|
101
218
|
}, [locale]);
|
|
102
219
|
|
|
103
|
-
// When consumer translations prop changes, update the resource bundles
|
|
220
|
+
// When consumer translations prop changes, update the resource bundles.
|
|
221
|
+
//
|
|
222
|
+
// Built through the same layering as the initial instance, parent included.
|
|
223
|
+
// Rebuilding from the bare English baseline here would overwrite whatever the
|
|
224
|
+
// host registered — a nested provider without translations of its own would
|
|
225
|
+
// reset the host's overrides to the built-in strings. That was already true;
|
|
226
|
+
// it was simply invisible until `bindI18nStore` made a store write repaint.
|
|
104
227
|
useEffect(() => {
|
|
105
228
|
if (!i18nRef.current) return;
|
|
106
|
-
const resources =
|
|
229
|
+
const resources = buildResourcesWithParent(translations, parentInstance);
|
|
107
230
|
for (const [lang, bundle] of Object.entries(resources)) {
|
|
108
231
|
i18nRef.current.addResourceBundle(
|
|
109
232
|
lang,
|
|
@@ -113,7 +236,7 @@ export function FireCMSi18nProvider({
|
|
|
113
236
|
true // overwrite existing keys
|
|
114
237
|
);
|
|
115
238
|
}
|
|
116
|
-
}, [translations]);
|
|
239
|
+
}, [translations, parentInstance]);
|
|
117
240
|
|
|
118
241
|
if (!ready || !i18nRef.current) return null;
|
|
119
242
|
|
|
@@ -124,6 +247,64 @@ export function FireCMSi18nProvider({
|
|
|
124
247
|
);
|
|
125
248
|
}
|
|
126
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Push a lazily loaded locale bundle into a live i18next instance.
|
|
252
|
+
*
|
|
253
|
+
* Consumer overrides are re-applied on top, because the instance may already be
|
|
254
|
+
* holding a bundle that `buildResources` seeded from English for this language,
|
|
255
|
+
* and the real translations have to win over that seed while the overrides win
|
|
256
|
+
* over both.
|
|
257
|
+
*/
|
|
258
|
+
function applyLoadedLocale(
|
|
259
|
+
instance: i18n,
|
|
260
|
+
locale: string,
|
|
261
|
+
translations?: { [locale: string]: DeepPartial<FireCMSTranslations> },
|
|
262
|
+
parentInstance?: i18n
|
|
263
|
+
) {
|
|
264
|
+
const language = locale.split("-")[0];
|
|
265
|
+
if (!loadedLocales[language]) return;
|
|
266
|
+
const bundle = buildResourcesWithParent(translations, parentInstance)[language]?.[FIRECMS_NS];
|
|
267
|
+
if (!bundle) return;
|
|
268
|
+
instance.addResourceBundle(
|
|
269
|
+
language,
|
|
270
|
+
FIRECMS_NS,
|
|
271
|
+
bundle,
|
|
272
|
+
true, // deep merge
|
|
273
|
+
true // overwrite the English seed
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The full resource map for this provider: the locale bundles currently in
|
|
279
|
+
* memory, then anything the parent provider already resolved, then this
|
|
280
|
+
* provider's own overrides on top.
|
|
281
|
+
*
|
|
282
|
+
* Shared by every path that writes into the instance — initial construction, a
|
|
283
|
+
* changed `translations` prop, and a locale arriving late — so all three layer
|
|
284
|
+
* in the same order and none of them can undo another.
|
|
285
|
+
*/
|
|
286
|
+
function buildResourcesWithParent(
|
|
287
|
+
translations?: { [locale: string]: DeepPartial<FireCMSTranslations> },
|
|
288
|
+
parentInstance?: i18n
|
|
289
|
+
): Record<string, Record<string, object>> {
|
|
290
|
+
const resources = buildResources(translations);
|
|
291
|
+
if (!parentInstance) return resources;
|
|
292
|
+
|
|
293
|
+
const inherited: Record<string, any> = parentInstance.services?.resourceStore?.data ?? {};
|
|
294
|
+
for (const [lang, namespaces] of Object.entries(inherited)) {
|
|
295
|
+
const bundle = (namespaces as any)?.[FIRECMS_NS];
|
|
296
|
+
if (!bundle) continue;
|
|
297
|
+
resources[lang] = {
|
|
298
|
+
[FIRECMS_NS]: {
|
|
299
|
+
...(resources[lang]?.[FIRECMS_NS] ?? {}),
|
|
300
|
+
...bundle,
|
|
301
|
+
...(translations?.[lang] ?? {})
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return resources;
|
|
306
|
+
}
|
|
307
|
+
|
|
127
308
|
/**
|
|
128
309
|
* Build an i18next resources object from the English baseline plus any
|
|
129
310
|
* consumer-provided overrides.
|
|
@@ -131,15 +312,10 @@ export function FireCMSi18nProvider({
|
|
|
131
312
|
function buildResources(
|
|
132
313
|
translations?: { [locale: string]: DeepPartial<FireCMSTranslations> }
|
|
133
314
|
): Record<string, Record<string, object>> {
|
|
134
|
-
const resources: Record<string, Record<string, object>> = {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
fr: { [FIRECMS_NS]: { ...fr } },
|
|
139
|
-
it: { [FIRECMS_NS]: { ...it } },
|
|
140
|
-
hi: { [FIRECMS_NS]: { ...hi } },
|
|
141
|
-
pt: { [FIRECMS_NS]: { ...pt } },
|
|
142
|
-
};
|
|
315
|
+
const resources: Record<string, Record<string, object>> = {};
|
|
316
|
+
for (const [lang, bundle] of Object.entries(loadedLocales)) {
|
|
317
|
+
resources[lang] = { [FIRECMS_NS]: { ...bundle } };
|
|
318
|
+
}
|
|
143
319
|
|
|
144
320
|
if (!translations) return resources;
|
|
145
321
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { render, screen } from "@testing-library/react";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { FireCMSi18nProvider } from "../FireCMSi18nProvider";
|
|
8
|
+
|
|
9
|
+
/** Reads a key through the same channel application code uses. */
|
|
10
|
+
function Str({ k }: { k: string }) {
|
|
11
|
+
const { t } = useTranslation("firecms_core");
|
|
12
|
+
return <span data-testid={k}>{t(k)}</span>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const HOST_TRANSLATIONS = {
|
|
16
|
+
en: {
|
|
17
|
+
host_only_key: "Explore your Firestore data",
|
|
18
|
+
save: "Publish" // an override of a real core key
|
|
19
|
+
}
|
|
20
|
+
} as any;
|
|
21
|
+
|
|
22
|
+
describe("FireCMSi18nProvider", () => {
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
window.localStorage.clear();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("resolves its own translations", () => {
|
|
29
|
+
render(
|
|
30
|
+
<FireCMSi18nProvider translations={HOST_TRANSLATIONS}>
|
|
31
|
+
<Str k={"host_only_key"}/>
|
|
32
|
+
</FireCMSi18nProvider>
|
|
33
|
+
);
|
|
34
|
+
expect(screen.getByTestId("host_only_key").textContent).toBe("Explore your Firestore data");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("keeps the host's translations when a nested provider is mounted without any", () => {
|
|
38
|
+
// This is the FireCMSCloudApp case: it mounts its own provider with
|
|
39
|
+
// `translations={appConfig?.translations}`, which is undefined for every
|
|
40
|
+
// project that has not deployed custom code. A nested provider that built its
|
|
41
|
+
// own isolated instance would shadow the host's and render the raw key.
|
|
42
|
+
render(
|
|
43
|
+
<FireCMSi18nProvider translations={HOST_TRANSLATIONS}>
|
|
44
|
+
<FireCMSi18nProvider translations={undefined}>
|
|
45
|
+
<Str k={"host_only_key"}/>
|
|
46
|
+
</FireCMSi18nProvider>
|
|
47
|
+
</FireCMSi18nProvider>
|
|
48
|
+
);
|
|
49
|
+
expect(screen.getByTestId("host_only_key").textContent).toBe("Explore your Firestore data");
|
|
50
|
+
expect(screen.getByTestId("host_only_key").textContent).not.toBe("host_only_key");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("does not let a nested provider clobber a host override", () => {
|
|
54
|
+
render(
|
|
55
|
+
<FireCMSi18nProvider translations={HOST_TRANSLATIONS}>
|
|
56
|
+
<FireCMSi18nProvider translations={undefined}>
|
|
57
|
+
<Str k={"save"}/>
|
|
58
|
+
</FireCMSi18nProvider>
|
|
59
|
+
</FireCMSi18nProvider>
|
|
60
|
+
);
|
|
61
|
+
expect(screen.getByTestId("save").textContent).toBe("Publish");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("lets a nested provider add its own translations on top of the host's", () => {
|
|
65
|
+
render(
|
|
66
|
+
<FireCMSi18nProvider translations={HOST_TRANSLATIONS}>
|
|
67
|
+
<FireCMSi18nProvider translations={{ en: { nested_key: "From the nested provider" } } as any}>
|
|
68
|
+
<>
|
|
69
|
+
<Str k={"host_only_key"}/>
|
|
70
|
+
<Str k={"nested_key"}/>
|
|
71
|
+
</>
|
|
72
|
+
</FireCMSi18nProvider>
|
|
73
|
+
</FireCMSi18nProvider>
|
|
74
|
+
);
|
|
75
|
+
expect(screen.getByTestId("host_only_key").textContent).toBe("Explore your Firestore data");
|
|
76
|
+
expect(screen.getByTestId("nested_key").textContent).toBe("From the nested provider");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("still serves the built-in core strings through a nested provider", () => {
|
|
80
|
+
render(
|
|
81
|
+
<FireCMSi18nProvider>
|
|
82
|
+
<FireCMSi18nProvider translations={undefined}>
|
|
83
|
+
<Str k={"discard"}/>
|
|
84
|
+
</FireCMSi18nProvider>
|
|
85
|
+
</FireCMSi18nProvider>
|
|
86
|
+
);
|
|
87
|
+
const el = screen.getByTestId("discard");
|
|
88
|
+
expect(el.textContent).not.toBe("discard");
|
|
89
|
+
expect(el.textContent?.length).toBeGreaterThan(0);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { render, screen } from "@testing-library/react";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { FireCMSi18nProvider } from "../FireCMSi18nProvider";
|
|
8
|
+
|
|
9
|
+
function Str({ k }: { k: string }) {
|
|
10
|
+
const { t } = useTranslation("firecms_core");
|
|
11
|
+
return <span data-testid={k}>{t(k)}</span>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const host = { en: { host_key: "Host" } } as any;
|
|
15
|
+
const projectA = { en: { only_in_a: "From project A" } } as any;
|
|
16
|
+
const projectB = { en: { only_in_b: "From project B" } } as any;
|
|
17
|
+
|
|
18
|
+
describe("nested provider lifecycle", () => {
|
|
19
|
+
beforeEach(() => window.localStorage.clear());
|
|
20
|
+
|
|
21
|
+
it("does not carry one project's translations into the next", () => {
|
|
22
|
+
// The real shape of a project switch: App.tsx's provider stays mounted while
|
|
23
|
+
// FireCMSCloudApp's inner one is torn down and rebuilt with another project's
|
|
24
|
+
// translations. Both share the outer i18next instance now, so anything the
|
|
25
|
+
// first project added could outlive it.
|
|
26
|
+
const { rerender } = render(
|
|
27
|
+
<FireCMSi18nProvider translations={host}>
|
|
28
|
+
<FireCMSi18nProvider key={"a"} translations={projectA}>
|
|
29
|
+
<Str k={"only_in_a"}/>
|
|
30
|
+
</FireCMSi18nProvider>
|
|
31
|
+
</FireCMSi18nProvider>
|
|
32
|
+
);
|
|
33
|
+
expect(screen.getByTestId("only_in_a").textContent).toBe("From project A");
|
|
34
|
+
|
|
35
|
+
rerender(
|
|
36
|
+
<FireCMSi18nProvider translations={host}>
|
|
37
|
+
<FireCMSi18nProvider key={"b"} translations={projectB}>
|
|
38
|
+
<>
|
|
39
|
+
<Str k={"only_in_b"}/>
|
|
40
|
+
<Str k={"only_in_a"}/>
|
|
41
|
+
</>
|
|
42
|
+
</FireCMSi18nProvider>
|
|
43
|
+
</FireCMSi18nProvider>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
expect(screen.getByTestId("only_in_b").textContent).toBe("From project B");
|
|
47
|
+
expect(screen.getByTestId("only_in_a").textContent).toBe("only_in_a");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("keeps the host's own translations across the switch", () => {
|
|
51
|
+
const { rerender } = render(
|
|
52
|
+
<FireCMSi18nProvider translations={host}>
|
|
53
|
+
<FireCMSi18nProvider key={"a"} translations={projectA}>
|
|
54
|
+
<Str k={"host_key"}/>
|
|
55
|
+
</FireCMSi18nProvider>
|
|
56
|
+
</FireCMSi18nProvider>
|
|
57
|
+
);
|
|
58
|
+
rerender(
|
|
59
|
+
<FireCMSi18nProvider translations={host}>
|
|
60
|
+
<FireCMSi18nProvider key={"b"} translations={projectB}>
|
|
61
|
+
<Str k={"host_key"}/>
|
|
62
|
+
</FireCMSi18nProvider>
|
|
63
|
+
</FireCMSi18nProvider>
|
|
64
|
+
);
|
|
65
|
+
expect(screen.getByTestId("host_key").textContent).toBe("Host");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -55,6 +55,7 @@ export function useBuildDataSource({
|
|
|
55
55
|
*/
|
|
56
56
|
fetchCollection: useCallback(<M extends Record<string, any>>({
|
|
57
57
|
path,
|
|
58
|
+
pathSegments,
|
|
58
59
|
collection,
|
|
59
60
|
filter,
|
|
60
61
|
limit,
|
|
@@ -67,6 +68,7 @@ export function useBuildDataSource({
|
|
|
67
68
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
68
69
|
return usedDelegate.fetchCollection<M>({
|
|
69
70
|
path,
|
|
71
|
+
pathSegments,
|
|
70
72
|
filter,
|
|
71
73
|
limit,
|
|
72
74
|
startAfter,
|
|
@@ -97,6 +99,7 @@ export function useBuildDataSource({
|
|
|
97
99
|
? useCallback(<M extends Record<string, any>>(
|
|
98
100
|
{
|
|
99
101
|
path,
|
|
102
|
+
pathSegments,
|
|
100
103
|
collection: collectionProp,
|
|
101
104
|
filter,
|
|
102
105
|
limit,
|
|
@@ -109,7 +112,7 @@ export function useBuildDataSource({
|
|
|
109
112
|
}: ListenCollectionProps<M>
|
|
110
113
|
): () => void => {
|
|
111
114
|
|
|
112
|
-
const collection = collectionProp ?? navigationController.getCollection(path);
|
|
115
|
+
const collection = collectionProp ?? navigationController.getCollection(path, false, pathSegments);
|
|
113
116
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
114
117
|
|
|
115
118
|
if (!usedDelegate.listenCollection)
|
|
@@ -117,6 +120,7 @@ export function useBuildDataSource({
|
|
|
117
120
|
|
|
118
121
|
return usedDelegate.listenCollection<M>({
|
|
119
122
|
path,
|
|
123
|
+
pathSegments,
|
|
120
124
|
filter,
|
|
121
125
|
limit,
|
|
122
126
|
startAfter,
|
|
@@ -139,6 +143,7 @@ export function useBuildDataSource({
|
|
|
139
143
|
*/
|
|
140
144
|
fetchEntity: useCallback(<M extends Record<string, any>>({
|
|
141
145
|
path,
|
|
146
|
+
pathSegments,
|
|
142
147
|
entityId,
|
|
143
148
|
collection
|
|
144
149
|
}: FetchEntityProps<M>
|
|
@@ -146,6 +151,7 @@ export function useBuildDataSource({
|
|
|
146
151
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
147
152
|
return usedDelegate.fetchEntity({
|
|
148
153
|
path,
|
|
154
|
+
pathSegments,
|
|
149
155
|
entityId,
|
|
150
156
|
collection
|
|
151
157
|
});
|
|
@@ -165,6 +171,7 @@ export function useBuildDataSource({
|
|
|
165
171
|
? useCallback(<M extends Record<string, any>>(
|
|
166
172
|
{
|
|
167
173
|
path,
|
|
174
|
+
pathSegments,
|
|
168
175
|
entityId,
|
|
169
176
|
collection,
|
|
170
177
|
onUpdate,
|
|
@@ -177,6 +184,7 @@ export function useBuildDataSource({
|
|
|
177
184
|
|
|
178
185
|
return usedDelegate.listenEntity<M>({
|
|
179
186
|
path,
|
|
187
|
+
pathSegments,
|
|
180
188
|
entityId,
|
|
181
189
|
onUpdate,
|
|
182
190
|
onError,
|
|
@@ -198,13 +206,14 @@ export function useBuildDataSource({
|
|
|
198
206
|
saveEntity: useCallback(async <M extends Record<string, any>>(
|
|
199
207
|
{
|
|
200
208
|
path,
|
|
209
|
+
pathSegments,
|
|
201
210
|
entityId,
|
|
202
211
|
values,
|
|
203
212
|
collection: collectionProp,
|
|
204
213
|
status
|
|
205
214
|
}: SaveEntityProps<M>): Promise<Entity<M>> => {
|
|
206
215
|
|
|
207
|
-
const collection = collectionProp ?? navigationController.getCollection(path);
|
|
216
|
+
const collection = collectionProp ?? navigationController.getCollection(path, false, pathSegments);
|
|
208
217
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
209
218
|
|
|
210
219
|
const resolvedCollection = collection
|
|
@@ -244,6 +253,7 @@ export function useBuildDataSource({
|
|
|
244
253
|
try {
|
|
245
254
|
const entities = await usedDelegate.fetchCollection({
|
|
246
255
|
path,
|
|
256
|
+
pathSegments,
|
|
247
257
|
orderBy: orderProperty,
|
|
248
258
|
order: "asc",
|
|
249
259
|
limit: 1,
|
|
@@ -270,14 +280,21 @@ export function useBuildDataSource({
|
|
|
270
280
|
|
|
271
281
|
return usedDelegate.saveEntity({
|
|
272
282
|
path,
|
|
283
|
+
pathSegments,
|
|
273
284
|
collection,
|
|
274
285
|
entityId,
|
|
275
286
|
values: finalValues,
|
|
276
287
|
status
|
|
277
288
|
}).then((res) => {
|
|
289
|
+
// Keep everything the delegate resolved — `databaseId`, and `pathSegments` in
|
|
290
|
+
// particular — so a just-saved entity is as resolvable as a fetched one.
|
|
291
|
+
// Only the values are re-mapped back to the CMS model.
|
|
278
292
|
return {
|
|
279
|
-
|
|
280
|
-
|
|
293
|
+
...res,
|
|
294
|
+
// A delegate that predates `pathSegments` returns none; the segments we
|
|
295
|
+
// just saved with describe exactly this entity's collection, so they are
|
|
296
|
+
// the authoritative answer rather than a guess.
|
|
297
|
+
pathSegments: res.pathSegments ?? pathSegments,
|
|
281
298
|
values: usedDelegate.delegateToCMSModel(finalValues)
|
|
282
299
|
} as Entity<M>;
|
|
283
300
|
});
|
|
@@ -292,12 +309,15 @@ export function useBuildDataSource({
|
|
|
292
309
|
deleteEntity: useCallback(<M extends Record<string, any>>(
|
|
293
310
|
{
|
|
294
311
|
entity,
|
|
312
|
+
pathSegments,
|
|
295
313
|
collection
|
|
296
314
|
}: DeleteEntityProps<M>
|
|
297
315
|
): Promise<void> => {
|
|
298
316
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
299
317
|
return usedDelegate.deleteEntity({
|
|
300
318
|
entity,
|
|
319
|
+
// Falls back to what the entity carries from the fetch that produced it.
|
|
320
|
+
pathSegments: pathSegments ?? entity.pathSegments,
|
|
301
321
|
collection
|
|
302
322
|
});
|
|
303
323
|
}, [delegate.deleteEntity]),
|
|
@@ -317,33 +337,32 @@ export function useBuildDataSource({
|
|
|
317
337
|
name: string,
|
|
318
338
|
value: any,
|
|
319
339
|
entityId?: string,
|
|
320
|
-
collection?: EntityCollection
|
|
340
|
+
collection?: EntityCollection,
|
|
341
|
+
pathSegments?: string[]
|
|
321
342
|
): Promise<boolean> => {
|
|
322
343
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
323
|
-
return usedDelegate.checkUniqueField(path, name, value, entityId, collection);
|
|
344
|
+
return usedDelegate.checkUniqueField(path, name, value, entityId, collection, pathSegments);
|
|
324
345
|
}, [delegate.checkUniqueField]),
|
|
325
346
|
|
|
326
|
-
generateEntityId: useCallback((path: string, collection: EntityCollection): string => {
|
|
347
|
+
generateEntityId: useCallback((path: string, collection: EntityCollection, pathSegments?: string[]): string => {
|
|
327
348
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
328
|
-
return usedDelegate.generateEntityId(path, collection);
|
|
349
|
+
return usedDelegate.generateEntityId(path, collection, pathSegments);
|
|
329
350
|
}, [delegate.generateEntityId]),
|
|
330
351
|
|
|
352
|
+
// Typed with the declared props rather than an inline duplicate: the duplicate is
|
|
353
|
+
// free to drift from the interface, and a field it forgets is dropped in silence.
|
|
331
354
|
countEntities: delegate.countEntities ? async ({
|
|
332
355
|
path,
|
|
356
|
+
pathSegments,
|
|
333
357
|
collection,
|
|
334
358
|
filter,
|
|
335
359
|
order,
|
|
336
360
|
orderBy
|
|
337
|
-
}: {
|
|
338
|
-
path: string,
|
|
339
|
-
collection: EntityCollection<any>,
|
|
340
|
-
filter?: FilterValues<Extract<keyof any, string>>,
|
|
341
|
-
orderBy?: string,
|
|
342
|
-
order?: "desc" | "asc",
|
|
343
|
-
}): Promise<number> => {
|
|
361
|
+
}: FetchCollectionProps<any>): Promise<number> => {
|
|
344
362
|
const usedDelegate = collection?.overrides?.dataSourceDelegate ?? delegate;
|
|
345
363
|
return usedDelegate.countEntities!({
|
|
346
364
|
path,
|
|
365
|
+
pathSegments,
|
|
347
366
|
filter,
|
|
348
367
|
orderBy,
|
|
349
368
|
order,
|
|
@@ -353,11 +372,13 @@ export function useBuildDataSource({
|
|
|
353
372
|
|
|
354
373
|
isFilterCombinationValid: useCallback(({
|
|
355
374
|
path,
|
|
375
|
+
pathSegments,
|
|
356
376
|
databaseId,
|
|
357
377
|
filterValues,
|
|
358
378
|
sortBy
|
|
359
379
|
}: {
|
|
360
380
|
path: string,
|
|
381
|
+
pathSegments?: string[],
|
|
361
382
|
databaseId?: string,
|
|
362
383
|
filterValues: FilterValues<any>,
|
|
363
384
|
sortBy?: [string, "asc" | "desc"]
|
|
@@ -367,6 +388,7 @@ export function useBuildDataSource({
|
|
|
367
388
|
return delegate.isFilterCombinationValid(
|
|
368
389
|
{
|
|
369
390
|
path,
|
|
391
|
+
pathSegments,
|
|
370
392
|
databaseId,
|
|
371
393
|
filterValues,
|
|
372
394
|
sortBy
|