@firecms/core 3.3.0 → 3.4.0-canary.2575a08
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/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/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/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/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/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/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 +3658 -1820
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +3655 -1817
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/pl.d.ts +2 -0
- package/dist/preview/index.d.ts +1 -0
- package/dist/preview/property_previews/GeopointPropertyPreview.d.ts +4 -0
- 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/navigation.d.ts +34 -5
- package/dist/types/plugins.d.ts +13 -0
- package/dist/types/properties.d.ts +17 -1
- package/dist/types/side_entity_controller.d.ts +7 -0
- package/dist/types/translations.d.ts +6 -0
- 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/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 +2 -0
- 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 -10
- package/src/app/Scaffold.tsx +34 -44
- 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 +66 -15
- 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/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/contexts/SnackbarProvider.tsx +14 -1
- package/src/core/DefaultDrawer.tsx +150 -64
- 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/field_configs.tsx +15 -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/useBuildNavigationController.tsx +23 -8
- package/src/hooks/useResolvedNavigationFrom.tsx +3 -1
- package/src/hooks/useValidateAuthenticator.tsx +4 -0
- package/src/i18n/FireCMSi18nProvider.tsx +50 -4
- 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 +7 -2
- package/src/locales/en.ts +7 -2
- package/src/locales/es.ts +7 -2
- package/src/locales/fr.ts +7 -2
- package/src/locales/hi.ts +7 -2
- package/src/locales/it.ts +7 -2
- package/src/locales/pl.ts +735 -0
- package/src/locales/pt.ts +7 -2
- package/src/preview/PropertyPreview.tsx +12 -0
- 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/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/navigation.ts +35 -5
- package/src/types/plugins.tsx +13 -0
- package/src/types/properties.ts +17 -0
- package/src/types/side_entity_controller.tsx +7 -0
- package/src/types/translations.ts +6 -0
- 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/entities.ts +16 -1
- package/src/util/entity_cache.ts +18 -0
- package/src/util/geopoint.ts +81 -0
- package/src/util/index.ts +2 -0
- package/src/util/navigation_blocking.ts +45 -0
- package/src/util/navigation_from_path.ts +47 -7
- package/src/util/navigation_utils.ts +244 -4
- 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 +8 -0
- package/src/util/urls.ts +52 -0
package/src/locales/pt.ts
CHANGED
|
@@ -151,8 +151,8 @@ export const pt: FireCMSTranslations = {
|
|
|
151
151
|
// ─── Error states ─────────────────────────────────────────────
|
|
152
152
|
error: "Erro",
|
|
153
153
|
error_uploading_file: "Erro ao carregar ficheiro",
|
|
154
|
-
error_deleting: "Erro ao eliminar",
|
|
155
|
-
error_before_delete: "Erro antes de eliminar",
|
|
154
|
+
error_deleting: "Erro ao eliminar: {{message}}",
|
|
155
|
+
error_before_delete: "Erro antes de eliminar: {{message}}",
|
|
156
156
|
error_updating_asset: "Erro ao atualizar recurso",
|
|
157
157
|
error_deleting_asset: "Erro ao eliminar recurso",
|
|
158
158
|
error_firestore_index: "É necessário um índice Firestore para esta consulta.",
|
|
@@ -394,6 +394,7 @@ export const pt: FireCMSTranslations = {
|
|
|
394
394
|
download: "Descarregar",
|
|
395
395
|
large_number_of_documents: "Esta coleção tem um grande número de documentos ({{count}}).",
|
|
396
396
|
include_undefined_values: "Incluir valores indefinidos",
|
|
397
|
+
export_apply_filter_sort: "Exportar apenas os resultados que correspondem ao filtro/ordenação atual",
|
|
397
398
|
submit: "Submeter",
|
|
398
399
|
|
|
399
400
|
no_filterable_properties: "Não há propriedades filtráveis disponíveis",
|
|
@@ -595,6 +596,10 @@ export const pt: FireCMSTranslations = {
|
|
|
595
596
|
order_property_not_found: "A propriedade \"{{property}}\" não existe ou não é uma propriedade numérica. Selecione uma propriedade válida ou limpe a seleção.",
|
|
596
597
|
no_number_properties: "Nenhuma propriedade numérica encontrada. Adicione uma propriedade numérica para habilitar a ordenação.",
|
|
597
598
|
order_property_description: "Selecione uma propriedade numérica para manter a ordem dos itens",
|
|
599
|
+
image_property: "Propriedade de imagem",
|
|
600
|
+
image_property_not_found: "A propriedade \"{{property}}\" não existe ou não é uma propriedade de imagem. Selecione uma propriedade válida ou limpe a seleção.",
|
|
601
|
+
no_image_properties: "Nenhuma propriedade de imagem encontrada. Adicione uma propriedade de armazenamento ou URL de imagem para usar como miniatura.",
|
|
602
|
+
image_property_description: "Selecione a propriedade usada como miniatura nas visualizações de Cartões e Kanban. Se não for definida, a primeira propriedade de imagem correspondente será usada automaticamente.",
|
|
598
603
|
display_settings: "Configurações de exibição",
|
|
599
604
|
default_row_size: "Tamanho de linha padrão",
|
|
600
605
|
side_dialog_width: "Largura do diálogo lateral",
|
|
@@ -4,6 +4,7 @@ import equal from "react-fast-compare"
|
|
|
4
4
|
import {
|
|
5
5
|
CMSType,
|
|
6
6
|
EntityReference,
|
|
7
|
+
GeoPoint,
|
|
7
8
|
ResolvedArrayProperty,
|
|
8
9
|
ResolvedMapProperty,
|
|
9
10
|
ResolvedNumberProperty,
|
|
@@ -27,12 +28,14 @@ import { ArrayPropertyEnumPreview } from "./property_previews/ArrayPropertyEnumP
|
|
|
27
28
|
import { ArrayOfStringsPreview } from "./property_previews/ArrayOfStringsPreview";
|
|
28
29
|
import { ArrayOneOfPreview } from "./property_previews/ArrayOneOfPreview";
|
|
29
30
|
import { MapPropertyPreview } from "./property_previews/MapPropertyPreview";
|
|
31
|
+
import { GeopointPropertyPreview } from "./property_previews/GeopointPropertyPreview";
|
|
30
32
|
import { ReferencePreview } from "./components/ReferencePreview";
|
|
31
33
|
import { DatePreview } from "./components/DatePreview";
|
|
32
34
|
import { BooleanPreview } from "./components/BooleanPreview";
|
|
33
35
|
import { NumberPropertyPreview } from "./property_previews/NumberPropertyPreview";
|
|
34
36
|
import { ErrorView } from "../components";
|
|
35
37
|
import { UserPreview } from "./components/UserPreview";
|
|
38
|
+
import { getGeoPointCoordinates } from "../util";
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
41
|
* @group Preview components
|
|
@@ -202,6 +205,15 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
202
205
|
} else {
|
|
203
206
|
content = buildWrongValueType(propertyKey, property.dataType, value);
|
|
204
207
|
}
|
|
208
|
+
} else if (property.dataType === "geopoint") {
|
|
209
|
+
const coordinates = getGeoPointCoordinates(value as GeoPoint);
|
|
210
|
+
if (coordinates) {
|
|
211
|
+
content = <GeopointPropertyPreview {...props}
|
|
212
|
+
property={property}
|
|
213
|
+
value={value as GeoPoint}/>;
|
|
214
|
+
} else {
|
|
215
|
+
content = buildWrongValueType(propertyKey, property.dataType, value);
|
|
216
|
+
}
|
|
205
217
|
} else if (property.dataType === "reference") {
|
|
206
218
|
if (typeof property.path === "string") {
|
|
207
219
|
if (typeof value === "object" && "isEntityReference" in value && value.isEntityReference()) {
|
|
@@ -7,6 +7,7 @@ import { Skeleton } from "@firecms/ui";
|
|
|
7
7
|
import { ErrorBoundary, ErrorView } from "../../components";
|
|
8
8
|
import { EntityPreview, EntityPreviewContainer } from "../../components/EntityPreview";
|
|
9
9
|
import { jsonStringifyReplacer } from "../../util/objects";
|
|
10
|
+
import { entityCacheKey } from "../../util/entity_cache";
|
|
10
11
|
|
|
11
12
|
export type ReferencePreviewProps = {
|
|
12
13
|
disabled?: boolean;
|
|
@@ -53,7 +54,7 @@ function ReferencePreviewInternal({
|
|
|
53
54
|
|
|
54
55
|
const navigationController = useNavigationController();
|
|
55
56
|
|
|
56
|
-
const collection = navigationController.getCollection(reference.path);
|
|
57
|
+
const collection = navigationController.getCollection(reference.path, false, reference.pathSegments);
|
|
57
58
|
if (!collection) {
|
|
58
59
|
if (customizationController.components?.missingReference) {
|
|
59
60
|
return <customizationController.components.missingReference path={reference.path}/>;
|
|
@@ -99,16 +100,19 @@ function ReferencePreviewExisting<M extends Record<string, any> = any>({
|
|
|
99
100
|
dataLoadingError
|
|
100
101
|
} = useEntityFetch({
|
|
101
102
|
path: reference.path,
|
|
103
|
+
// References carry the segments of the collection they point at, when the code that
|
|
104
|
+
// built them knew them (see getReferenceFrom).
|
|
105
|
+
pathSegments: reference.pathSegments,
|
|
102
106
|
entityId: reference.id,
|
|
103
107
|
collection,
|
|
104
108
|
useCache: true
|
|
105
109
|
});
|
|
106
110
|
|
|
107
111
|
if (entity) {
|
|
108
|
-
referencesCache.set(reference.
|
|
112
|
+
referencesCache.set(entityCacheKey(reference.path, reference.id), entity);
|
|
109
113
|
}
|
|
110
114
|
|
|
111
|
-
const usedEntity = entity ?? referencesCache.get(reference.
|
|
115
|
+
const usedEntity = entity ?? referencesCache.get(entityCacheKey(reference.path, reference.id));
|
|
112
116
|
|
|
113
117
|
let body: React.ReactNode;
|
|
114
118
|
|
|
@@ -6,6 +6,7 @@ import { useStorageSource } from "../../hooks";
|
|
|
6
6
|
import { DownloadConfig, FileType } from "../../types";
|
|
7
7
|
import { PreviewSize } from "../PropertyPreviewProps";
|
|
8
8
|
import { ErrorView } from "../../components";
|
|
9
|
+
import { getPreviewTypeFromUrl, isAbsoluteHttpUrl } from "../../util/urls";
|
|
9
10
|
|
|
10
11
|
type StorageThumbnailProps = {
|
|
11
12
|
storagePathOrDownloadUrl: string;
|
|
@@ -41,10 +42,20 @@ export function StorageThumbnailInternal({
|
|
|
41
42
|
const [error, setError] = React.useState<Error | undefined>(undefined);
|
|
42
43
|
const storage = useStorageSource();
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* The saved value may already be a resolved download URL instead of a
|
|
47
|
+
* storage path: either because the property is configured with
|
|
48
|
+
* `storeUrl: true`, or because the value was written directly in the
|
|
49
|
+
* datasource pointing to an externally hosted file. Such values can be
|
|
50
|
+
* previewed as they are, and must not be handed over to the storage source,
|
|
51
|
+
* which can only resolve paths of its own bucket.
|
|
52
|
+
*/
|
|
53
|
+
const valueIsUrl = storeUrl || isAbsoluteHttpUrl(storagePathOrDownloadUrl);
|
|
54
|
+
|
|
44
55
|
const [downloadConfig, setDownloadConfig] = React.useState<DownloadConfig>(URL_CACHE[storagePathOrDownloadUrl]);
|
|
45
56
|
|
|
46
57
|
useEffect(() => {
|
|
47
|
-
if (!storagePathOrDownloadUrl)
|
|
58
|
+
if (!storagePathOrDownloadUrl || valueIsUrl)
|
|
48
59
|
return;
|
|
49
60
|
let unmounted = false;
|
|
50
61
|
storage.getDownloadURL(storagePathOrDownloadUrl)
|
|
@@ -57,10 +68,21 @@ export function StorageThumbnailInternal({
|
|
|
57
68
|
return () => {
|
|
58
69
|
unmounted = true;
|
|
59
70
|
};
|
|
60
|
-
}, [storagePathOrDownloadUrl]);
|
|
71
|
+
}, [storagePathOrDownloadUrl, valueIsUrl]);
|
|
61
72
|
|
|
62
73
|
if (!storagePathOrDownloadUrl) return null;
|
|
63
74
|
|
|
75
|
+
if (valueIsUrl) {
|
|
76
|
+
// There is no storage metadata for a value that is already a URL, so the
|
|
77
|
+
// preview type is inferred from its file extension when possible.
|
|
78
|
+
return <UrlComponentPreview previewType={getPreviewTypeFromUrl(storagePathOrDownloadUrl) ?? "file"}
|
|
79
|
+
url={storagePathOrDownloadUrl}
|
|
80
|
+
interactive={interactive}
|
|
81
|
+
size={size}
|
|
82
|
+
fill={fill}
|
|
83
|
+
hint={storagePathOrDownloadUrl} />;
|
|
84
|
+
}
|
|
85
|
+
|
|
64
86
|
const filetype = downloadConfig?.metadata ? getFiletype(downloadConfig?.metadata.contentType) : undefined;
|
|
65
87
|
const previewType = filetype?.startsWith("image")
|
|
66
88
|
? "image"
|
package/src/preview/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./property_previews/ArrayOfStringsPreview";
|
|
|
7
7
|
export * from "./property_previews/ArrayPropertyEnumPreview";
|
|
8
8
|
export * from "./property_previews/ArrayOfMapsPreview";
|
|
9
9
|
export * from "./property_previews/NumberPropertyPreview";
|
|
10
|
+
export * from "./property_previews/GeopointPropertyPreview";
|
|
10
11
|
export * from "./property_previews/StringPropertyPreview";
|
|
11
12
|
export * from "./property_previews/ArrayOfStorageComponentsPreview";
|
|
12
13
|
export * from "./property_previews/ArrayOfReferencesPreview";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { GeoPoint } from "../../types";
|
|
4
|
+
import { PropertyPreviewProps } from "../PropertyPreviewProps";
|
|
5
|
+
import { formatGeoPoint, getGeoPointCoordinates } from "../../util";
|
|
6
|
+
|
|
7
|
+
export function GeopointPropertyPreview({
|
|
8
|
+
value,
|
|
9
|
+
size
|
|
10
|
+
}: PropertyPreviewProps<GeoPoint>): React.ReactElement {
|
|
11
|
+
|
|
12
|
+
const coordinates = getGeoPointCoordinates(value);
|
|
13
|
+
|
|
14
|
+
if (!coordinates) {
|
|
15
|
+
return <span className={size === "small" ? "text-sm text-text-secondary dark:text-text-secondary-dark" : "text-text-secondary dark:text-text-secondary-dark"}>—</span>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<span className={size === "small" ? "text-sm font-mono" : "font-mono"}>
|
|
20
|
+
{formatGeoPoint(coordinates)}
|
|
21
|
+
</span>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
} from "../util/navigation_from_path";
|
|
11
11
|
import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
|
|
12
12
|
import { toArray } from "../util/arrays";
|
|
13
|
+
import { addInitialSlash, encodeEntityId } from "../util/navigation_utils";
|
|
14
|
+
import { shouldBlockEntityNavigation } from "../util/navigation_blocking";
|
|
13
15
|
import { NotFoundPage } from "../components";
|
|
14
16
|
import { lazyEager } from "../util/lazy_eager";
|
|
15
17
|
|
|
@@ -88,7 +90,7 @@ export function FireCMSRoute() {
|
|
|
88
90
|
let collection: EntityCollection<any> | undefined;
|
|
89
91
|
collection = navigation.getCollectionById(navigationEntries[0].id);
|
|
90
92
|
if (!collection)
|
|
91
|
-
collection = navigation.getCollection(navigationEntries[0].path);
|
|
93
|
+
collection = navigation.getCollection(navigationEntries[0].path, false, navigationEntries[0].pathSegments);
|
|
92
94
|
if (!collection)
|
|
93
95
|
return null;
|
|
94
96
|
return <React.Suspense fallback={null}>
|
|
@@ -97,6 +99,7 @@ export function FireCMSRoute() {
|
|
|
97
99
|
isSubCollection={false}
|
|
98
100
|
parentCollectionIds={[]}
|
|
99
101
|
fullPath={collection.path}
|
|
102
|
+
pathSegments={navigationEntries[0].pathSegments}
|
|
100
103
|
fullIdPath={collection.id}
|
|
101
104
|
updateUrl={true}
|
|
102
105
|
{...collection}
|
|
@@ -111,7 +114,7 @@ export function FireCMSRoute() {
|
|
|
111
114
|
const firstEntry = navigationEntries[0] as NavigationViewCollectionInternal<any>;
|
|
112
115
|
collection = navigation.getCollectionById(firstEntry.id);
|
|
113
116
|
if (!collection)
|
|
114
|
-
collection = navigation.getCollection(firstEntry.path);
|
|
117
|
+
collection = navigation.getCollection(firstEntry.path, false, firstEntry.pathSegments);
|
|
115
118
|
if (!collection)
|
|
116
119
|
return null;
|
|
117
120
|
return <React.Suspense fallback={null}>
|
|
@@ -121,6 +124,7 @@ export function FireCMSRoute() {
|
|
|
121
124
|
isSubCollection={false}
|
|
122
125
|
parentCollectionIds={[]}
|
|
123
126
|
fullPath={collection.path}
|
|
127
|
+
pathSegments={firstEntry.pathSegments}
|
|
124
128
|
updateUrl={true}
|
|
125
129
|
{...collection}
|
|
126
130
|
Actions={toArray(collection.Actions)} />
|
|
@@ -182,6 +186,8 @@ function EntityFullScreenRoute({
|
|
|
182
186
|
const urlTab = getSelectedTabFromUrl(isNew, lastCustomView);
|
|
183
187
|
const [selectedTab, setSelectedTab] = useState<string | undefined>(urlTab);
|
|
184
188
|
|
|
189
|
+
// `navigationPath` comes from the URL, where entity ids are still escaped (see
|
|
190
|
+
// encodeEntityId), so splitting it on "/" inside is correct and no segments are needed.
|
|
185
191
|
const parentCollectionIds = navigation.getParentCollectionIds(navigationPath);
|
|
186
192
|
useEffect(() => {
|
|
187
193
|
if (urlTab !== selectedTab) {
|
|
@@ -189,27 +195,51 @@ function EntityFullScreenRoute({
|
|
|
189
195
|
}
|
|
190
196
|
}, [urlTab]);
|
|
191
197
|
|
|
192
|
-
const
|
|
198
|
+
const lastCollectionEntry = navigationEntries.findLast((entry) => entry.type === "collection");
|
|
199
|
+
|
|
200
|
+
// The entity id reaches the URL escaped AND percent-encoded, so it cannot be located by
|
|
201
|
+
// searching `pathname` for the raw id — that misses for any id needing encoding (a "/",
|
|
202
|
+
// but equally a space or a non-ASCII character). Rebuild the URLs from the navigation
|
|
203
|
+
// entries instead, which already carry the escaped chain in `fullPath`.
|
|
204
|
+
const entityEntryIndex = lastEntityEntry ? navigationEntries.indexOf(lastEntityEntry) : -1;
|
|
205
|
+
const parentCollectionEntry = entityEntryIndex > 0
|
|
206
|
+
? navigationEntries[entityEntryIndex - 1] as NavigationViewCollectionInternal<any>
|
|
207
|
+
: undefined;
|
|
208
|
+
|
|
209
|
+
const buildUrl = (escapedPath: string) => addInitialSlash(navigation.buildUrlCollectionPath(escapedPath));
|
|
210
|
+
|
|
211
|
+
const basePath = !entityId || isNew || !parentCollectionEntry
|
|
193
212
|
? pathname
|
|
194
|
-
:
|
|
213
|
+
: buildUrl(parentCollectionEntry.fullPath);
|
|
214
|
+
|
|
215
|
+
const entityPath = lastEntityEntry ? buildUrl(lastEntityEntry.fullPath) : basePath;
|
|
195
216
|
|
|
196
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Build the URL of an entity in the current collection. `id` is raw, so it is escaped
|
|
219
|
+
* before being joined; `buildUrlCollectionPath` then percent-encodes the whole path.
|
|
220
|
+
*/
|
|
221
|
+
const buildEntityUrl = (id: string, tab?: string) => {
|
|
222
|
+
const parentPath = parentCollectionEntry?.fullPath ?? lastCollectionEntry?.fullPath;
|
|
223
|
+
if (!parentPath) return pathname;
|
|
224
|
+
return buildUrl(`${parentPath}/${encodeEntityId(id)}${tab ? "/" + tab : ""}`);
|
|
225
|
+
};
|
|
197
226
|
|
|
198
227
|
let blocker: Blocker | undefined = undefined;
|
|
199
228
|
try {
|
|
200
229
|
blocker = useBlocker(({
|
|
230
|
+
currentLocation,
|
|
201
231
|
nextLocation
|
|
202
|
-
}) => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
232
|
+
}) => shouldBlockEntityNavigation({
|
|
233
|
+
currentLocation,
|
|
234
|
+
nextLocation,
|
|
235
|
+
entityPath,
|
|
236
|
+
basePath,
|
|
237
|
+
blocked: blocked.current
|
|
238
|
+
}));
|
|
207
239
|
} catch (e) {
|
|
208
240
|
// console.warn("Blocker not available, navigation will not be blocked");
|
|
209
241
|
}
|
|
210
242
|
|
|
211
|
-
const lastCollectionEntry = navigationEntries.findLast((entry) => entry.type === "collection");
|
|
212
|
-
|
|
213
243
|
if (isNew && !lastCollectionEntry) {
|
|
214
244
|
throw new Error("INTERNAL: No collection found in the navigation");
|
|
215
245
|
}
|
|
@@ -219,14 +249,20 @@ function EntityFullScreenRoute({
|
|
|
219
249
|
}
|
|
220
250
|
|
|
221
251
|
const collection = isNew ? lastCollectionEntry!.collection : lastEntityEntry!.parentCollection;
|
|
222
|
-
|
|
223
|
-
|
|
252
|
+
// `fullIdPath` is used downstream to build URLs, so it carries the escaped chain;
|
|
253
|
+
// `collectionPath` addresses the datasource, so it is resolved from the raw one.
|
|
254
|
+
const fullIdPath = isNew ? lastCollectionEntry!.fullPath : (parentCollectionEntry?.fullPath ?? lastEntityEntry!.fullPath);
|
|
255
|
+
// The navigation entries know the real segment boundaries, so the path is resolved
|
|
256
|
+
// from them rather than re-parsed out of the flattened string.
|
|
257
|
+
const pathSegments = isNew ? lastCollectionEntry!.pathSegments : lastEntityEntry!.pathSegments;
|
|
258
|
+
const collectionPath = navigation.resolveIdsFrom(isNew ? lastCollectionEntry!.path : lastEntityEntry!.path, pathSegments);
|
|
224
259
|
return <>
|
|
225
260
|
<React.Suspense fallback={null}>
|
|
226
261
|
<EntityEditView
|
|
227
262
|
key={collection.id + "_" + (isNew ? "new" : (isCopy ? entityId + "_copy" : entityId))}
|
|
228
263
|
entityId={isNew ? undefined : entityId}
|
|
229
264
|
fullIdPath={fullIdPath}
|
|
265
|
+
pathSegments={pathSegments}
|
|
230
266
|
collection={collection}
|
|
231
267
|
layout={"full_screen"}
|
|
232
268
|
path={collectionPath}
|
|
@@ -234,25 +270,16 @@ function EntityFullScreenRoute({
|
|
|
234
270
|
selectedTab={selectedTab ?? undefined}
|
|
235
271
|
onValuesModified={(modified) => blocked.current = modified}
|
|
236
272
|
onSaved={(params) => {
|
|
237
|
-
const newSelectedTab = params.selectedTab;
|
|
238
273
|
const newEntityId = params.entityId;
|
|
239
|
-
if (
|
|
240
|
-
|
|
241
|
-
} else {
|
|
242
|
-
navigate(`${basePath}/${newEntityId}`, { replace: true });
|
|
243
|
-
}
|
|
274
|
+
if (!newEntityId) return;
|
|
275
|
+
navigate(buildEntityUrl(newEntityId, params.selectedTab), { replace: true });
|
|
244
276
|
}}
|
|
245
277
|
onTabChange={(params) => {
|
|
246
278
|
setSelectedTab(params.selectedTab);
|
|
247
|
-
if (isNew) {
|
|
279
|
+
if (isNew || !entityId) {
|
|
248
280
|
return;
|
|
249
281
|
}
|
|
250
|
-
|
|
251
|
-
if (newSelectedTab) {
|
|
252
|
-
navigate(`${basePath}/${entityId}/${newSelectedTab}`, { replace: true });
|
|
253
|
-
} else {
|
|
254
|
-
navigate(`${basePath}/${entityId}`, { replace: true });
|
|
255
|
-
}
|
|
282
|
+
navigate(buildEntityUrl(entityId, params.selectedTab), { replace: true });
|
|
256
283
|
}}
|
|
257
284
|
parentCollectionIds={parentCollectionIds}
|
|
258
285
|
/>
|
package/src/types/collections.ts
CHANGED
|
@@ -101,6 +101,14 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
101
101
|
*/
|
|
102
102
|
titleProperty?: keyof M;
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Property used as the image thumbnail in Cards and Kanban view modes.
|
|
106
|
+
* Must reference a storage/image property (or an array of them), or a
|
|
107
|
+
* string property with `url: "image"`.
|
|
108
|
+
* If not specified, the first matching image property will be used.
|
|
109
|
+
*/
|
|
110
|
+
imageProperty?: keyof M;
|
|
111
|
+
|
|
104
112
|
/**
|
|
105
113
|
* When editing an entity, you can choose to open the entity in a side dialog
|
|
106
114
|
* or in a full screen dialog. Defaults to `full_screen`.
|
|
@@ -261,6 +269,22 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
261
269
|
*/
|
|
262
270
|
initialFilter?: FilterValues<Extract<keyof M, string>>; // setting FilterValues<M> can break defining collections by code
|
|
263
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Array of property keys that are allowed for filtering. Allowed filters will be displayed in the collection view, table row headers
|
|
274
|
+
* and will be used to filter data.
|
|
275
|
+
*
|
|
276
|
+
* If not specified, all properties that are filterable will be allowed.
|
|
277
|
+
*
|
|
278
|
+
* If you specify this prop, the filters will be displayed in the collection view if they are filterable.
|
|
279
|
+
*
|
|
280
|
+
* If you set it as an empty array, no filters will be displayed.
|
|
281
|
+
*
|
|
282
|
+
* e.g. `allowedFilters: ["name", "category"]`
|
|
283
|
+
*
|
|
284
|
+
* e.g. `allowedFilters: []`
|
|
285
|
+
*/
|
|
286
|
+
allowedFilters?: (keyof M)[];
|
|
287
|
+
|
|
264
288
|
/**
|
|
265
289
|
* Default sort applied to this collection.
|
|
266
290
|
* When setting this prop, entities will have a default order
|
|
@@ -473,6 +497,14 @@ export interface CollectionActionsProps<M extends Record<string, any> = any, USE
|
|
|
473
497
|
*/
|
|
474
498
|
path: string;
|
|
475
499
|
|
|
500
|
+
/**
|
|
501
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
502
|
+
*
|
|
503
|
+
* Optional, and never derived by splitting `path` — a parent entity id may itself
|
|
504
|
+
* contain "/". Absent means "not known here", not "no slashes".
|
|
505
|
+
*/
|
|
506
|
+
pathSegments?: string[];
|
|
507
|
+
|
|
476
508
|
/**
|
|
477
509
|
* Path of the last collection, like `addresses`
|
|
478
510
|
*/
|
|
@@ -725,6 +757,8 @@ export type EntityTableController<M extends Record<string, any> = any> = {
|
|
|
725
757
|
dataLoadingError?: Error;
|
|
726
758
|
filterValues?: FilterValues<Extract<keyof M, string>>;
|
|
727
759
|
setFilterValues?: (filterValues: FilterValues<Extract<keyof M, string>>) => void;
|
|
760
|
+
allowedFilters?: (keyof M)[];
|
|
761
|
+
forcedFilters?: (keyof M)[];
|
|
728
762
|
sortBy?: [Extract<keyof M, string>, "asc" | "desc"];
|
|
729
763
|
setSortBy?: (sortBy?: [Extract<keyof M, string>, "asc" | "desc"]) => void;
|
|
730
764
|
searchString?: string;
|
package/src/types/datasource.ts
CHANGED
|
@@ -8,6 +8,20 @@ import { FireCMSContext } from "./firecms_context";
|
|
|
8
8
|
*/
|
|
9
9
|
export interface FetchEntityProps<M extends Record<string, any> = any> {
|
|
10
10
|
path: string;
|
|
11
|
+
/**
|
|
12
|
+
* The path split at its real segment boundaries, e.g.
|
|
13
|
+
* `["nodes", "node/42", "edges"]`.
|
|
14
|
+
*
|
|
15
|
+
* `path` is a single flattened string, so a parent entity id containing "/" cannot be
|
|
16
|
+
* recovered from it — `"nodes/node/42/edges"` is indistinguishable from a three-level
|
|
17
|
+
* nesting. This is the unambiguous form: one array element per segment, entity ids
|
|
18
|
+
* kept whole however many slashes they contain.
|
|
19
|
+
*
|
|
20
|
+
* For any backend whose ids cannot contain "/" this is exactly `path.split("/")`, so
|
|
21
|
+
* delegates that do not care can keep using `path`. Delegates that do should prefer
|
|
22
|
+
* `pathSegments ?? path.split("/")`.
|
|
23
|
+
*/
|
|
24
|
+
pathSegments?: string[];
|
|
11
25
|
entityId: string;
|
|
12
26
|
databaseId?: string;
|
|
13
27
|
collection?: EntityCollection<M, any>
|
|
@@ -28,6 +42,20 @@ export type ListenEntityProps<M extends Record<string, any> = any> =
|
|
|
28
42
|
*/
|
|
29
43
|
export interface FetchCollectionProps<M extends Record<string, any> = any> {
|
|
30
44
|
path: string;
|
|
45
|
+
/**
|
|
46
|
+
* The path split at its real segment boundaries, e.g.
|
|
47
|
+
* `["nodes", "node/42", "edges"]`.
|
|
48
|
+
*
|
|
49
|
+
* `path` is a single flattened string, so a parent entity id containing "/" cannot be
|
|
50
|
+
* recovered from it — `"nodes/node/42/edges"` is indistinguishable from a three-level
|
|
51
|
+
* nesting. This is the unambiguous form: one array element per segment, entity ids
|
|
52
|
+
* kept whole however many slashes they contain.
|
|
53
|
+
*
|
|
54
|
+
* For any backend whose ids cannot contain "/" this is exactly `path.split("/")`, so
|
|
55
|
+
* delegates that do not care can keep using `path`. Delegates that do should prefer
|
|
56
|
+
* `pathSegments ?? path.split("/")`.
|
|
57
|
+
*/
|
|
58
|
+
pathSegments?: string[];
|
|
31
59
|
collection?: EntityCollection<M> | ResolvedEntityCollection<M>;
|
|
32
60
|
filter?: FilterValues<Extract<keyof M, string>>,
|
|
33
61
|
limit?: number;
|
|
@@ -52,6 +80,20 @@ export type ListenCollectionProps<M extends Record<string, any> = any> =
|
|
|
52
80
|
*/
|
|
53
81
|
export interface SaveEntityProps<M extends Record<string, any> = any> {
|
|
54
82
|
path: string;
|
|
83
|
+
/**
|
|
84
|
+
* The path split at its real segment boundaries, e.g.
|
|
85
|
+
* `["nodes", "node/42", "edges"]`.
|
|
86
|
+
*
|
|
87
|
+
* `path` is a single flattened string, so a parent entity id containing "/" cannot be
|
|
88
|
+
* recovered from it — `"nodes/node/42/edges"` is indistinguishable from a three-level
|
|
89
|
+
* nesting. This is the unambiguous form: one array element per segment, entity ids
|
|
90
|
+
* kept whole however many slashes they contain.
|
|
91
|
+
*
|
|
92
|
+
* For any backend whose ids cannot contain "/" this is exactly `path.split("/")`, so
|
|
93
|
+
* delegates that do not care can keep using `path`. Delegates that do should prefer
|
|
94
|
+
* `pathSegments ?? path.split("/")`.
|
|
95
|
+
*/
|
|
96
|
+
pathSegments?: string[];
|
|
55
97
|
values: Partial<EntityValues<M>>;
|
|
56
98
|
entityId?: string; // can be empty for new entities
|
|
57
99
|
previousValues?: Partial<EntityValues<M>>;
|
|
@@ -64,6 +106,11 @@ export interface SaveEntityProps<M extends Record<string, any> = any> {
|
|
|
64
106
|
*/
|
|
65
107
|
export interface DeleteEntityProps<M extends Record<string, any> = any> {
|
|
66
108
|
entity: Entity<M>;
|
|
109
|
+
/**
|
|
110
|
+
* `entity.path` split at its real segment boundaries, when the caller knows them.
|
|
111
|
+
* Never derived from `entity.path` — see `pathSegments` on FetchCollectionProps.
|
|
112
|
+
*/
|
|
113
|
+
pathSegments?: string[];
|
|
67
114
|
collection?: EntityCollection<M> | ResolvedEntityCollection<M>;
|
|
68
115
|
}
|
|
69
116
|
|
|
@@ -203,13 +250,19 @@ export interface DataSource {
|
|
|
203
250
|
name: string,
|
|
204
251
|
value: any,
|
|
205
252
|
entityId?: string,
|
|
206
|
-
collection?: EntityCollection
|
|
253
|
+
collection?: EntityCollection,
|
|
254
|
+
/**
|
|
255
|
+
* Appended last, and optional, so existing implementations and callers keep
|
|
256
|
+
* working unchanged — a function declaring fewer parameters stays assignable.
|
|
257
|
+
* See `pathSegments` on FetchCollectionProps.
|
|
258
|
+
*/
|
|
259
|
+
pathSegments?: string[]
|
|
207
260
|
): Promise<boolean>;
|
|
208
261
|
|
|
209
262
|
/**
|
|
210
263
|
* Generate an id for a new entity
|
|
211
264
|
*/
|
|
212
|
-
generateEntityId(path: string, collection: EntityCollection): string;
|
|
265
|
+
generateEntityId(path: string, collection: EntityCollection, pathSegments?: string[]): string;
|
|
213
266
|
|
|
214
267
|
/**
|
|
215
268
|
* Count the number of entities in a collection
|
|
@@ -230,6 +283,10 @@ export interface DataSource {
|
|
|
230
283
|
initTextSearch?: (props: {
|
|
231
284
|
context: FireCMSContext,
|
|
232
285
|
path: string,
|
|
286
|
+
/**
|
|
287
|
+
* `path` split at its real segment boundaries, when known. Never derived from `path`.
|
|
288
|
+
*/
|
|
289
|
+
pathSegments?: string[],
|
|
233
290
|
collection: EntityCollection,
|
|
234
291
|
parentCollectionIds?: string[]
|
|
235
292
|
}) => Promise<boolean>;
|
|
@@ -237,6 +294,8 @@ export interface DataSource {
|
|
|
237
294
|
}
|
|
238
295
|
|
|
239
296
|
export type FilterCombinationValidProps = {
|
|
297
|
+
/** `path` split at its real segment boundaries — see `pathSegments` on FetchCollectionProps. */
|
|
298
|
+
pathSegments?: string[];
|
|
240
299
|
path: string;
|
|
241
300
|
collection: EntityCollection<any>;
|
|
242
301
|
filterValues: FilterValues<any>;
|
|
@@ -366,12 +425,12 @@ export interface DataSourceDelegate {
|
|
|
366
425
|
* @param collection
|
|
367
426
|
* @return `true` if there are no other fields besides the given entity
|
|
368
427
|
*/
|
|
369
|
-
checkUniqueField(path: string, name: string, value: any, entityId?: string, collection?: EntityCollection): Promise<boolean>;
|
|
428
|
+
checkUniqueField(path: string, name: string, value: any, entityId?: string, collection?: EntityCollection, pathSegments?: string[]): Promise<boolean>;
|
|
370
429
|
|
|
371
430
|
/**
|
|
372
431
|
* Generate an id for a new entity
|
|
373
432
|
*/
|
|
374
|
-
generateEntityId(path: string, collection?: EntityCollection): string;
|
|
433
|
+
generateEntityId(path: string, collection?: EntityCollection, pathSegments?: string[]): string;
|
|
375
434
|
|
|
376
435
|
/**
|
|
377
436
|
* Count the number of entities in a collection
|
|
@@ -398,6 +457,10 @@ export interface DataSourceDelegate {
|
|
|
398
457
|
initTextSearch?: (props: {
|
|
399
458
|
context: FireCMSContext,
|
|
400
459
|
path: string,
|
|
460
|
+
/**
|
|
461
|
+
* `path` split at its real segment boundaries, when known. Never derived from `path`.
|
|
462
|
+
*/
|
|
463
|
+
pathSegments?: string[],
|
|
401
464
|
databaseId?: string,
|
|
402
465
|
collection: EntityCollection,
|
|
403
466
|
parentCollectionIds?: string[]
|
package/src/types/entities.ts
CHANGED
|
@@ -21,6 +21,17 @@ export interface Entity<M extends object = any> {
|
|
|
21
21
|
*/
|
|
22
22
|
path: string;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
26
|
+
*
|
|
27
|
+
* `path` is a single flattened string, so a parent entity id containing "/" cannot be
|
|
28
|
+
* recovered from it. This keeps each segment whole.
|
|
29
|
+
*
|
|
30
|
+
* Optional, and never derived by splitting `path` — a guess would be wrong in exactly
|
|
31
|
+
* the case the field exists for. Absent means "not known here", not "no slashes".
|
|
32
|
+
*/
|
|
33
|
+
pathSegments?: string[];
|
|
34
|
+
|
|
24
35
|
/**
|
|
25
36
|
* Current values
|
|
26
37
|
*/
|
|
@@ -55,10 +66,22 @@ export class EntityReference {
|
|
|
55
66
|
*/
|
|
56
67
|
readonly databaseId?: string;
|
|
57
68
|
|
|
58
|
-
|
|
69
|
+
/**
|
|
70
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
71
|
+
*
|
|
72
|
+
* `path` is a single flattened string, so a parent entity id containing "/" cannot be
|
|
73
|
+
* recovered from it. This keeps each segment whole.
|
|
74
|
+
*
|
|
75
|
+
* Optional, and never derived by splitting `path` — a guess would be wrong in exactly
|
|
76
|
+
* the case the field exists for. Absent means "not known here", not "no slashes".
|
|
77
|
+
*/
|
|
78
|
+
readonly pathSegments?: string[];
|
|
79
|
+
|
|
80
|
+
constructor(id: string, path: string, databaseId?: string, pathSegments?: string[]) {
|
|
59
81
|
this.id = id;
|
|
60
82
|
this.path = path;
|
|
61
83
|
this.databaseId = databaseId;
|
|
84
|
+
this.pathSegments = pathSegments;
|
|
62
85
|
}
|
|
63
86
|
|
|
64
87
|
get pathWithId() {
|
|
@@ -65,6 +65,12 @@ export type EntityActionClickProps<M extends object, USER extends User = User> =
|
|
|
65
65
|
context: FireCMSContext<USER>;
|
|
66
66
|
|
|
67
67
|
fullPath?: string;
|
|
68
|
+
/**
|
|
69
|
+
* `fullPath` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`,
|
|
70
|
+
* when the caller knows them. Absent means "not known here" — never derived from
|
|
71
|
+
* `fullPath`, since a parent id may itself contain "/".
|
|
72
|
+
*/
|
|
73
|
+
pathSegments?: string[];
|
|
68
74
|
fullIdPath?: string;
|
|
69
75
|
collection?: EntityCollection<M>;
|
|
70
76
|
|
|
@@ -86,6 +86,14 @@ export interface EntityOnFetchProps<M extends Record<string, any> = any, USER ex
|
|
|
86
86
|
*/
|
|
87
87
|
path: string;
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
91
|
+
*
|
|
92
|
+
* Optional, and never derived by splitting `path` — a parent entity id may itself
|
|
93
|
+
* contain "/". Absent means "not known here", not "no slashes".
|
|
94
|
+
*/
|
|
95
|
+
pathSegments?: string[];
|
|
96
|
+
|
|
89
97
|
/**
|
|
90
98
|
* Fetched entity
|
|
91
99
|
*/
|
|
@@ -133,6 +141,14 @@ export interface EntityOnSaveProps<M extends Record<string, any> = any, USER ext
|
|
|
133
141
|
*/
|
|
134
142
|
path: string;
|
|
135
143
|
|
|
144
|
+
/**
|
|
145
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
146
|
+
*
|
|
147
|
+
* Optional, and never derived by splitting `path` — a parent entity id may itself
|
|
148
|
+
* contain "/". Absent means "not known here", not "no slashes".
|
|
149
|
+
*/
|
|
150
|
+
pathSegments?: string[];
|
|
151
|
+
|
|
136
152
|
/**
|
|
137
153
|
* Full path where this entity is being saved, with alias resolved
|
|
138
154
|
*/
|
|
@@ -180,6 +196,14 @@ export interface EntityOnDeleteProps<M extends Record<string, any> = any, USER e
|
|
|
180
196
|
*/
|
|
181
197
|
path: string;
|
|
182
198
|
|
|
199
|
+
/**
|
|
200
|
+
* `path` split at its real segment boundaries, e.g. `["nodes", "node/42", "edges"]`.
|
|
201
|
+
*
|
|
202
|
+
* Optional, and never derived by splitting `path` — a parent entity id may itself
|
|
203
|
+
* contain "/". Absent means "not known here", not "no slashes".
|
|
204
|
+
*/
|
|
205
|
+
pathSegments?: string[];
|
|
206
|
+
|
|
183
207
|
/**
|
|
184
208
|
* Deleted entity id
|
|
185
209
|
*/
|