@webbio/strapi-plugin-page-builder 0.12.2-platform → 0.12.4-platform
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 +166 -166
- package/admin/src/api/collection-type.ts +111 -111
- package/admin/src/api/entity-relation.ts +42 -42
- package/admin/src/api/has-page-relation.ts +37 -37
- package/admin/src/api/has-platform-relation.ts +40 -40
- package/admin/src/api/page-type-relation.ts +41 -41
- package/admin/src/api/platform-page-types.ts +45 -45
- package/admin/src/api/platform-relation.ts +42 -42
- package/admin/src/api/platform.ts +35 -35
- package/admin/src/api/search-filtered-entity.ts +114 -114
- package/admin/src/api/template.ts +39 -39
- package/admin/src/components/Combobox/index.tsx +83 -83
- package/admin/src/components/Combobox/react-select-custom-styles.tsx +121 -121
- package/admin/src/components/Combobox/styles.ts +46 -46
- package/admin/src/components/ConfirmModal/index.tsx +90 -90
- package/admin/src/components/EditView/CollectionTypeSearch/index.tsx +127 -127
- package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/index.tsx +149 -149
- package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/styles.ts +19 -19
- package/admin/src/components/EditView/CollectionTypeSettings/index.tsx +82 -82
- package/admin/src/components/EditView/Details/index.tsx +48 -48
- package/admin/src/components/EditView/Details/styles.ts +51 -51
- package/admin/src/components/EditView/PageSettings/index.tsx +124 -124
- package/admin/src/components/EditView/Platform/platform-select.tsx +30 -30
- package/admin/src/components/EditView/Template/TemplateConfirmModal/index.tsx +36 -36
- package/admin/src/components/EditView/Template/TemplateSelect/index.tsx +70 -70
- package/admin/src/components/EditView/Template/TemplateSelect/use-template-modules.ts +41 -41
- package/admin/src/components/EditView/index.tsx +35 -35
- package/admin/src/components/EditView/page-type-select.tsx +32 -32
- package/admin/src/components/EditView/wrapper.tsx +41 -41
- package/admin/src/components/GlobalPlatformSelect/index.tsx +40 -40
- package/admin/src/components/GlobalPlatformSelect/styles.ts +27 -27
- package/admin/src/components/Initializer/index.tsx +24 -24
- package/admin/src/components/PageFilters/PageTypeFilter/index.tsx +39 -39
- package/admin/src/components/PageFilters/PlatformFilter/index.tsx +32 -32
- package/admin/src/components/PageFilters/filters.tsx +189 -189
- package/admin/src/components/PageFilters/index.tsx +35 -35
- package/admin/src/components/PageTypeEditView/TemplatePlatformSelect/index.tsx +76 -76
- package/admin/src/components/PageTypeEditView/index.tsx +53 -53
- package/admin/src/components/PlatformFilteredSelectField/InputIcon/index.tsx +23 -23
- package/admin/src/components/PlatformFilteredSelectField/Multi/index.tsx +210 -210
- package/admin/src/components/PlatformFilteredSelectField/Single/index.tsx +197 -197
- package/admin/src/components/PlatformFilteredSelectField/hooks/useRelationLoad.tsx +128 -128
- package/admin/src/components/PlatformFilteredSelectField/index.tsx +85 -85
- package/admin/src/components/PlatformFilteredSelectField/styles.tsx +77 -77
- package/admin/src/components/PlatformFilteredSelectField/utils/get-translations.ts +3 -3
- package/admin/src/components/PlatformFilteredSelectField/utils/relation-helper.ts +147 -147
- package/admin/src/components/PluginIcon/index.tsx +94 -94
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/components/Relations/RelationInput.tsx +689 -689
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/components/Relations/RelationInputDataManager.tsx +6 -6
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/components/Relations/useRelation.ts +170 -170
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/components/Relations/utils/getRelationLink.ts +5 -5
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/components/Relations/utils/normalizeRelations.ts +52 -52
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/constants/attributes.ts +3 -3
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/hooks/useDragAndDrop.ts +253 -253
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/hooks/useKeyboardDragAndDrop.ts +96 -96
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/hooks/usePrev.ts +11 -11
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/utils/dragAndDrop.ts +8 -8
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/utils/paths.ts +29 -29
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/utils/refs.ts +19 -19
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/utils/translations.ts +3 -3
- package/admin/src/components/StrapiCore/content-manager/shared/contracts/collection-types.ts +300 -300
- package/admin/src/components/StrapiCore/content-manager/shared/contracts/components.ts +72 -72
- package/admin/src/components/StrapiCore/content-manager/shared/contracts/content-types.ts +116 -116
- package/admin/src/components/StrapiCore/content-manager/shared/contracts/index.ts +8 -8
- package/admin/src/components/StrapiCore/content-manager/shared/contracts/init.ts +22 -22
- package/admin/src/components/StrapiCore/content-manager/shared/contracts/relations.ts +80 -80
- package/admin/src/components/StrapiCore/content-manager/shared/contracts/review-workflows.ts +88 -88
- package/admin/src/components/StrapiCore/content-manager/shared/contracts/single-types.ts +112 -112
- package/admin/src/components/StrapiCore/content-manager/shared/contracts/uid.ts +48 -48
- package/admin/src/components/StrapiCore/content-manager/shared/index.ts +1 -1
- package/admin/src/constants.ts +3 -3
- package/admin/src/index.tsx +101 -101
- package/admin/src/middlewares/index.tsx +37 -37
- package/admin/src/pages/app/index.tsx +14 -14
- package/admin/src/pluginId.ts +5 -5
- package/admin/src/translations/en.json +9 -9
- package/admin/src/translations/nl.json +9 -9
- package/admin/src/utils/findDomElement.ts +6 -6
- package/admin/src/utils/findElementParent.ts +20 -20
- package/admin/src/utils/getObjectFromFormName.ts +42 -42
- package/admin/src/utils/getRequestUrl.ts +11 -11
- package/admin/src/utils/getTrad.ts +5 -5
- package/admin/src/utils/hooks/useDebounce.ts +17 -17
- package/admin/src/utils/hooks/useDefaultPlatformFromLocalStorage.ts +61 -61
- package/admin/src/utils/hooks/useGetLocaleFromUrl.ts +9 -9
- package/admin/src/utils/hooks/useHideOverviewFilterTags.ts +34 -34
- package/admin/src/utils/hooks/usePlatformFormData.ts +64 -64
- package/admin/src/utils/hooks/usePrevious.ts +12 -12
- package/admin/src/utils/sanitizeModules.ts +93 -93
- package/custom.d.ts +6 -6
- package/dist/package.json +1 -1
- package/dist/server/bootstrap/collection-type-lifecycles.js +1 -1
- package/dist/server/bootstrap.js +1 -1
- package/dist/server/graphql/page-by-path.js +20 -17
- package/dist/server/graphql/page-by-slug.js +9 -9
- package/dist/server/graphql/pages-by-uid.js +5 -5
- package/dist/server/services/private-content/graphql/index.js +27 -27
- package/dist/server/services/private-content/graphql/types/index.js +74 -74
- package/dist/server/utils/graphql.js +18 -18
- package/dist/tsconfig.server.tsbuildinfo +1 -1
- package/package.json +78 -78
- package/server/bootstrap/collection-type-lifecycles.ts +47 -47
- package/server/bootstrap/permissions.ts +161 -161
- package/server/bootstrap.ts +261 -261
- package/server/config/index.ts +4 -4
- package/server/content-types/index.ts +7 -7
- package/server/content-types/user-category/schema.json +23 -23
- package/server/controllers/collection-types.ts +32 -32
- package/server/controllers/index.ts +19 -19
- package/server/controllers/page-type.ts +18 -18
- package/server/controllers/page.ts +9 -9
- package/server/controllers/platform.ts +21 -21
- package/server/controllers/private-content.ts +17 -17
- package/server/controllers/sitemap.ts +32 -32
- package/server/controllers/template.ts +16 -16
- package/server/controllers/user-category.ts +3 -3
- package/server/destroy.ts +5 -5
- package/server/graphql/index.ts +9 -9
- package/server/graphql/page-by-path.ts +135 -132
- package/server/graphql/page-type.ts +67 -67
- package/server/graphql/pages-by-uid.ts +89 -89
- package/server/index.ts +23 -23
- package/server/middlewares/index.ts +1 -1
- package/server/policies/index.ts +5 -5
- package/server/policies/isAuthorizedPage.ts +11 -11
- package/server/register.ts +22 -22
- package/server/routes/index.ts +115 -115
- package/server/routes/user-category.ts +3 -3
- package/server/schema/page-end.json +96 -96
- package/server/schema/page-start.json +87 -87
- package/server/schema/page-type-end.json +53 -53
- package/server/schema/page-type-start.json +38 -38
- package/server/schema/platform-start.json +21 -21
- package/server/schema/template-end.json +40 -40
- package/server/schema/template-start.json +35 -35
- package/server/services/builder.ts +232 -232
- package/server/services/collection-types.ts +95 -95
- package/server/services/email.ts +127 -127
- package/server/services/index.ts +23 -23
- package/server/services/page-type.ts +30 -30
- package/server/services/page.ts +24 -24
- package/server/services/platform.ts +30 -30
- package/server/services/private-content/components/admin-email.json +22 -22
- package/server/services/private-content/components/email.json +22 -22
- package/server/services/private-content/components/platform-email.json +30 -30
- package/server/services/private-content/constants/index.ts +13 -13
- package/server/services/private-content/graphql/index.ts +88 -88
- package/server/services/private-content/graphql/resolvers/findOnePage.ts +40 -40
- package/server/services/private-content/graphql/resolvers/findPage.ts +45 -45
- package/server/services/private-content/graphql/resolvers/forgot-password.ts +34 -34
- package/server/services/private-content/graphql/resolvers/login.ts +56 -56
- package/server/services/private-content/graphql/resolvers/register.ts +78 -78
- package/server/services/private-content/graphql/resolvers/reset-password.ts +44 -44
- package/server/services/private-content/graphql/types/index.ts +96 -96
- package/server/services/private-content/index.ts +95 -95
- package/server/services/private-content/mail-template/txtMail.email.template.text.ts +6 -6
- package/server/services/private-content/page.ts +20 -20
- package/server/services/private-content/platform.ts +19 -19
- package/server/services/private-content/schemas/index.ts +28 -28
- package/server/services/private-content/user.ts +197 -197
- package/server/services/sitemap.ts +83 -83
- package/server/services/template.ts +13 -13
- package/server/services/user-category.ts +3 -3
- package/server/utils/filter-underscore-arguments.ts +12 -12
- package/server/utils/reload-strapi-on-load.ts +13 -13
- package/server/utils/strapi.ts +50 -50
- package/shared/utils/constants.ts +8 -8
- package/shared/utils/sleep.ts +1 -1
- package/strapi-admin.js +3 -3
- package/strapi-server.js +3 -3
- package/tsconfig.json +20 -20
- package/tsconfig.server.json +25 -25
|
@@ -1,197 +1,197 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { useIntl } from 'react-intl';
|
|
3
|
-
import debounce from 'lodash/debounce';
|
|
4
|
-
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
|
5
|
-
import { useSelector } from 'react-redux';
|
|
6
|
-
import { Field } from '@strapi/design-system';
|
|
7
|
-
import { useFetchClient } from '@strapi/helper-plugin';
|
|
8
|
-
import { Link } from '@strapi/icons';
|
|
9
|
-
|
|
10
|
-
import { Combobox, IReactSelectValue } from '../../Combobox';
|
|
11
|
-
import { OptionProps, SingleValue, components } from 'react-select';
|
|
12
|
-
import { useGetLocaleFromUrl } from '../../../utils/hooks/useGetLocaleFromUrl';
|
|
13
|
-
import { getSearchFilteredEntities } from '../../../api/search-filtered-entity';
|
|
14
|
-
import S from './../styles';
|
|
15
|
-
import getTrad from '../../../utils/getTrad';
|
|
16
|
-
import RelationHelper, { IPlatformFilteredSelectFieldProps } from '../utils/relation-helper';
|
|
17
|
-
import { Platform } from '../../../api/platform';
|
|
18
|
-
|
|
19
|
-
export interface ISinglePlatformFilteredSelectFieldProps extends IPlatformFilteredSelectFieldProps {
|
|
20
|
-
selectedPlatform?: Platform;
|
|
21
|
-
hiddenId?: number[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface CustomReactSelectValue extends Omit<IReactSelectValue, 'initialSelected'> {
|
|
25
|
-
href?: string;
|
|
26
|
-
publicationState?: string | false;
|
|
27
|
-
publishedAt?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const SEARCH_DEBOUNCE_MS = 150;
|
|
31
|
-
const PAGE = 1;
|
|
32
|
-
|
|
33
|
-
const SinglePlatformFilteredSelectField = ({
|
|
34
|
-
attribute,
|
|
35
|
-
description,
|
|
36
|
-
labelAction,
|
|
37
|
-
error,
|
|
38
|
-
intlLabel,
|
|
39
|
-
name,
|
|
40
|
-
selectedPlatform,
|
|
41
|
-
hiddenId
|
|
42
|
-
}: ISinglePlatformFilteredSelectFieldProps) => {
|
|
43
|
-
const fetchClient = useFetchClient();
|
|
44
|
-
const { onChange, initialData, modifiedData, isCreatingEntry, layout, allLayoutData } =
|
|
45
|
-
useCMEditViewDataManager() as any;
|
|
46
|
-
|
|
47
|
-
const { locales } = useSelector((state: any) => state.i18n_locales);
|
|
48
|
-
const urlLocale = useGetLocaleFromUrl();
|
|
49
|
-
const defaultLocale = locales.find((locale: any) => locale.isDefault);
|
|
50
|
-
const selectedLocale = initialData?.locale ?? urlLocale ?? defaultLocale.code;
|
|
51
|
-
|
|
52
|
-
const [selected, setSelected] = useState<SingleValue<CustomReactSelectValue | null>>();
|
|
53
|
-
|
|
54
|
-
const { formatMessage } = useIntl();
|
|
55
|
-
|
|
56
|
-
const { targetAttributes, targetFieldValue, targetField } = RelationHelper.getTargetAttributes(
|
|
57
|
-
name,
|
|
58
|
-
attribute,
|
|
59
|
-
modifiedData,
|
|
60
|
-
layout,
|
|
61
|
-
allLayoutData
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
const label = intlLabel?.id
|
|
65
|
-
? formatMessage({ id: intlLabel.id, defaultMessage: intlLabel?.defaultMessage }, { ...intlLabel.values })
|
|
66
|
-
: name;
|
|
67
|
-
|
|
68
|
-
const hint = description?.id
|
|
69
|
-
? formatMessage({ id: description.id, defaultMessage: description.defaultMessage }, { ...description.values })
|
|
70
|
-
: '';
|
|
71
|
-
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
const initialSelect = getInitialSelectItem(targetFieldValue?.[0]);
|
|
74
|
-
|
|
75
|
-
setSelected(initialSelect);
|
|
76
|
-
}, [targetFieldValue]);
|
|
77
|
-
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
if (isCreatingEntry) {
|
|
80
|
-
setSelected(null);
|
|
81
|
-
}
|
|
82
|
-
}, []);
|
|
83
|
-
|
|
84
|
-
const setFormValue = (name: string, value?: string | Record<string, any>[]) => {
|
|
85
|
-
onChange({
|
|
86
|
-
target: {
|
|
87
|
-
name,
|
|
88
|
-
value
|
|
89
|
-
},
|
|
90
|
-
shouldSetInitialValue: true
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const handleChange = (value?: SingleValue<CustomReactSelectValue>) => {
|
|
95
|
-
if (value) {
|
|
96
|
-
const newFormValue = {
|
|
97
|
-
...value,
|
|
98
|
-
id: value.value,
|
|
99
|
-
title: value.label,
|
|
100
|
-
mainField: value.label,
|
|
101
|
-
label: value.label,
|
|
102
|
-
value: value.value
|
|
103
|
-
};
|
|
104
|
-
setFormValue(targetField, [newFormValue]);
|
|
105
|
-
} else {
|
|
106
|
-
setFormValue(targetField, []);
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const getItems = async (inputValue?: string, platformTitle?: string): Promise<CustomReactSelectValue[]> => {
|
|
111
|
-
const pages = await getSearchFilteredEntities({
|
|
112
|
-
fetchClient,
|
|
113
|
-
page: PAGE,
|
|
114
|
-
uid: targetAttributes?.target || '',
|
|
115
|
-
locale: selectedLocale,
|
|
116
|
-
searchQuery: inputValue,
|
|
117
|
-
platformTitle: attribute?.pluginOptions?.filteredSelect?.disablePlatformFilter ? '' : platformTitle,
|
|
118
|
-
customFilters: attribute?.pluginOptions?.filteredSelect?.customFilters,
|
|
119
|
-
notIds: hiddenId
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
return pages?.results.map((x) => ({
|
|
123
|
-
value: String(x.id),
|
|
124
|
-
label: x.title,
|
|
125
|
-
href: x?.href,
|
|
126
|
-
publicationState: x.publicationState,
|
|
127
|
-
publishedAt: x.publishedAt
|
|
128
|
-
}));
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const debouncedFetch = debounce((searchTerm, callback, selectedPlatformTitle?: string) => {
|
|
132
|
-
promiseOptions(searchTerm, selectedPlatformTitle).then((result) => {
|
|
133
|
-
return callback(result || []);
|
|
134
|
-
});
|
|
135
|
-
}, SEARCH_DEBOUNCE_MS);
|
|
136
|
-
|
|
137
|
-
const promiseOptions = (inputValue: string, selectedPlatformTitle?: string): Promise<CustomReactSelectValue[]> =>
|
|
138
|
-
new Promise<CustomReactSelectValue[]>((resolve) => {
|
|
139
|
-
resolve(getItems(inputValue, selectedPlatformTitle));
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
return (
|
|
143
|
-
<Field name={name} id={name} error={error} hint={hint}>
|
|
144
|
-
{targetAttributes?.type === 'relation' && (
|
|
145
|
-
<S.FieldWrapper>
|
|
146
|
-
<Combobox
|
|
147
|
-
key={`rerenderOnPlatformChange-${selectedPlatform?.title}`}
|
|
148
|
-
id="pagesSearch"
|
|
149
|
-
label={label}
|
|
150
|
-
loadOptions={(i, c) => debouncedFetch(i, c, selectedPlatform?.title)}
|
|
151
|
-
cacheOptions
|
|
152
|
-
customOption={CustomOption}
|
|
153
|
-
onChange={handleChange}
|
|
154
|
-
value={selected}
|
|
155
|
-
defaultOptions
|
|
156
|
-
hideSelectedOptions
|
|
157
|
-
labelAction={labelAction}
|
|
158
|
-
isDisabled={!selectedPlatform}
|
|
159
|
-
afterInput={
|
|
160
|
-
<S.LinkToPage
|
|
161
|
-
title={formatMessage({ id: getTrad('platformFilteredSelect.linkToEntity.label') })}
|
|
162
|
-
to={selected?.href}
|
|
163
|
-
disabled={!selected?.href}
|
|
164
|
-
>
|
|
165
|
-
<Link />
|
|
166
|
-
</S.LinkToPage>
|
|
167
|
-
}
|
|
168
|
-
/>
|
|
169
|
-
</S.FieldWrapper>
|
|
170
|
-
)}
|
|
171
|
-
</Field>
|
|
172
|
-
);
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
const CustomOption = (props: OptionProps<CustomReactSelectValue, false>) => {
|
|
176
|
-
return (
|
|
177
|
-
<components.Option {...props}>
|
|
178
|
-
<S.CustomOption>
|
|
179
|
-
{props.data?.publicationState && <S.CustomOptionStatus publicationState={props.data?.publicationState} />}
|
|
180
|
-
{props.children}
|
|
181
|
-
</S.CustomOption>
|
|
182
|
-
</components.Option>
|
|
183
|
-
);
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const getInitialSelectItem = (initialValue?: Record<string, any>): SingleValue<CustomReactSelectValue | null> =>
|
|
187
|
-
initialValue?.id
|
|
188
|
-
? {
|
|
189
|
-
value: String(initialValue?.id),
|
|
190
|
-
label: initialValue?.title ?? '',
|
|
191
|
-
href: initialValue?.href,
|
|
192
|
-
publicationState: initialValue.publicationState,
|
|
193
|
-
publishedAt: initialValue.publishedAt
|
|
194
|
-
}
|
|
195
|
-
: null;
|
|
196
|
-
|
|
197
|
-
export default SinglePlatformFilteredSelectField;
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
import debounce from 'lodash/debounce';
|
|
4
|
+
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
|
5
|
+
import { useSelector } from 'react-redux';
|
|
6
|
+
import { Field } from '@strapi/design-system';
|
|
7
|
+
import { useFetchClient } from '@strapi/helper-plugin';
|
|
8
|
+
import { Link } from '@strapi/icons';
|
|
9
|
+
|
|
10
|
+
import { Combobox, IReactSelectValue } from '../../Combobox';
|
|
11
|
+
import { OptionProps, SingleValue, components } from 'react-select';
|
|
12
|
+
import { useGetLocaleFromUrl } from '../../../utils/hooks/useGetLocaleFromUrl';
|
|
13
|
+
import { getSearchFilteredEntities } from '../../../api/search-filtered-entity';
|
|
14
|
+
import S from './../styles';
|
|
15
|
+
import getTrad from '../../../utils/getTrad';
|
|
16
|
+
import RelationHelper, { IPlatformFilteredSelectFieldProps } from '../utils/relation-helper';
|
|
17
|
+
import { Platform } from '../../../api/platform';
|
|
18
|
+
|
|
19
|
+
export interface ISinglePlatformFilteredSelectFieldProps extends IPlatformFilteredSelectFieldProps {
|
|
20
|
+
selectedPlatform?: Platform;
|
|
21
|
+
hiddenId?: number[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface CustomReactSelectValue extends Omit<IReactSelectValue, 'initialSelected'> {
|
|
25
|
+
href?: string;
|
|
26
|
+
publicationState?: string | false;
|
|
27
|
+
publishedAt?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const SEARCH_DEBOUNCE_MS = 150;
|
|
31
|
+
const PAGE = 1;
|
|
32
|
+
|
|
33
|
+
const SinglePlatformFilteredSelectField = ({
|
|
34
|
+
attribute,
|
|
35
|
+
description,
|
|
36
|
+
labelAction,
|
|
37
|
+
error,
|
|
38
|
+
intlLabel,
|
|
39
|
+
name,
|
|
40
|
+
selectedPlatform,
|
|
41
|
+
hiddenId
|
|
42
|
+
}: ISinglePlatformFilteredSelectFieldProps) => {
|
|
43
|
+
const fetchClient = useFetchClient();
|
|
44
|
+
const { onChange, initialData, modifiedData, isCreatingEntry, layout, allLayoutData } =
|
|
45
|
+
useCMEditViewDataManager() as any;
|
|
46
|
+
|
|
47
|
+
const { locales } = useSelector((state: any) => state.i18n_locales);
|
|
48
|
+
const urlLocale = useGetLocaleFromUrl();
|
|
49
|
+
const defaultLocale = locales.find((locale: any) => locale.isDefault);
|
|
50
|
+
const selectedLocale = initialData?.locale ?? urlLocale ?? defaultLocale.code;
|
|
51
|
+
|
|
52
|
+
const [selected, setSelected] = useState<SingleValue<CustomReactSelectValue | null>>();
|
|
53
|
+
|
|
54
|
+
const { formatMessage } = useIntl();
|
|
55
|
+
|
|
56
|
+
const { targetAttributes, targetFieldValue, targetField } = RelationHelper.getTargetAttributes(
|
|
57
|
+
name,
|
|
58
|
+
attribute,
|
|
59
|
+
modifiedData,
|
|
60
|
+
layout,
|
|
61
|
+
allLayoutData
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const label = intlLabel?.id
|
|
65
|
+
? formatMessage({ id: intlLabel.id, defaultMessage: intlLabel?.defaultMessage }, { ...intlLabel.values })
|
|
66
|
+
: name;
|
|
67
|
+
|
|
68
|
+
const hint = description?.id
|
|
69
|
+
? formatMessage({ id: description.id, defaultMessage: description.defaultMessage }, { ...description.values })
|
|
70
|
+
: '';
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const initialSelect = getInitialSelectItem(targetFieldValue?.[0]);
|
|
74
|
+
|
|
75
|
+
setSelected(initialSelect);
|
|
76
|
+
}, [targetFieldValue]);
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (isCreatingEntry) {
|
|
80
|
+
setSelected(null);
|
|
81
|
+
}
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
const setFormValue = (name: string, value?: string | Record<string, any>[]) => {
|
|
85
|
+
onChange({
|
|
86
|
+
target: {
|
|
87
|
+
name,
|
|
88
|
+
value
|
|
89
|
+
},
|
|
90
|
+
shouldSetInitialValue: true
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const handleChange = (value?: SingleValue<CustomReactSelectValue>) => {
|
|
95
|
+
if (value) {
|
|
96
|
+
const newFormValue = {
|
|
97
|
+
...value,
|
|
98
|
+
id: value.value,
|
|
99
|
+
title: value.label,
|
|
100
|
+
mainField: value.label,
|
|
101
|
+
label: value.label,
|
|
102
|
+
value: value.value
|
|
103
|
+
};
|
|
104
|
+
setFormValue(targetField, [newFormValue]);
|
|
105
|
+
} else {
|
|
106
|
+
setFormValue(targetField, []);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const getItems = async (inputValue?: string, platformTitle?: string): Promise<CustomReactSelectValue[]> => {
|
|
111
|
+
const pages = await getSearchFilteredEntities({
|
|
112
|
+
fetchClient,
|
|
113
|
+
page: PAGE,
|
|
114
|
+
uid: targetAttributes?.target || '',
|
|
115
|
+
locale: selectedLocale,
|
|
116
|
+
searchQuery: inputValue,
|
|
117
|
+
platformTitle: attribute?.pluginOptions?.filteredSelect?.disablePlatformFilter ? '' : platformTitle,
|
|
118
|
+
customFilters: attribute?.pluginOptions?.filteredSelect?.customFilters,
|
|
119
|
+
notIds: hiddenId
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return pages?.results.map((x) => ({
|
|
123
|
+
value: String(x.id),
|
|
124
|
+
label: x.title,
|
|
125
|
+
href: x?.href,
|
|
126
|
+
publicationState: x.publicationState,
|
|
127
|
+
publishedAt: x.publishedAt
|
|
128
|
+
}));
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const debouncedFetch = debounce((searchTerm, callback, selectedPlatformTitle?: string) => {
|
|
132
|
+
promiseOptions(searchTerm, selectedPlatformTitle).then((result) => {
|
|
133
|
+
return callback(result || []);
|
|
134
|
+
});
|
|
135
|
+
}, SEARCH_DEBOUNCE_MS);
|
|
136
|
+
|
|
137
|
+
const promiseOptions = (inputValue: string, selectedPlatformTitle?: string): Promise<CustomReactSelectValue[]> =>
|
|
138
|
+
new Promise<CustomReactSelectValue[]>((resolve) => {
|
|
139
|
+
resolve(getItems(inputValue, selectedPlatformTitle));
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<Field name={name} id={name} error={error} hint={hint}>
|
|
144
|
+
{targetAttributes?.type === 'relation' && (
|
|
145
|
+
<S.FieldWrapper>
|
|
146
|
+
<Combobox
|
|
147
|
+
key={`rerenderOnPlatformChange-${selectedPlatform?.title}`}
|
|
148
|
+
id="pagesSearch"
|
|
149
|
+
label={label}
|
|
150
|
+
loadOptions={(i, c) => debouncedFetch(i, c, selectedPlatform?.title)}
|
|
151
|
+
cacheOptions
|
|
152
|
+
customOption={CustomOption}
|
|
153
|
+
onChange={handleChange}
|
|
154
|
+
value={selected}
|
|
155
|
+
defaultOptions
|
|
156
|
+
hideSelectedOptions
|
|
157
|
+
labelAction={labelAction}
|
|
158
|
+
isDisabled={!selectedPlatform}
|
|
159
|
+
afterInput={
|
|
160
|
+
<S.LinkToPage
|
|
161
|
+
title={formatMessage({ id: getTrad('platformFilteredSelect.linkToEntity.label') })}
|
|
162
|
+
to={selected?.href}
|
|
163
|
+
disabled={!selected?.href}
|
|
164
|
+
>
|
|
165
|
+
<Link />
|
|
166
|
+
</S.LinkToPage>
|
|
167
|
+
}
|
|
168
|
+
/>
|
|
169
|
+
</S.FieldWrapper>
|
|
170
|
+
)}
|
|
171
|
+
</Field>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const CustomOption = (props: OptionProps<CustomReactSelectValue, false>) => {
|
|
176
|
+
return (
|
|
177
|
+
<components.Option {...props}>
|
|
178
|
+
<S.CustomOption>
|
|
179
|
+
{props.data?.publicationState && <S.CustomOptionStatus publicationState={props.data?.publicationState} />}
|
|
180
|
+
{props.children}
|
|
181
|
+
</S.CustomOption>
|
|
182
|
+
</components.Option>
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const getInitialSelectItem = (initialValue?: Record<string, any>): SingleValue<CustomReactSelectValue | null> =>
|
|
187
|
+
initialValue?.id
|
|
188
|
+
? {
|
|
189
|
+
value: String(initialValue?.id),
|
|
190
|
+
label: initialValue?.title ?? '',
|
|
191
|
+
href: initialValue?.href,
|
|
192
|
+
publicationState: initialValue.publicationState,
|
|
193
|
+
publishedAt: initialValue.publishedAt
|
|
194
|
+
}
|
|
195
|
+
: null;
|
|
196
|
+
|
|
197
|
+
export default SinglePlatformFilteredSelectField;
|
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
import get from 'lodash/get';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { useRelation } from '../../StrapiCore/admin/admin/src/content-manager/components/Relations/useRelation';
|
|
4
|
-
import { getInitialDataPathUsingTempKeys } from '../../StrapiCore/admin/admin/src/content-manager/utils/paths';
|
|
5
|
-
import RelationHelper, { IPlatformFilteredSelectFieldProps } from '../utils/relation-helper';
|
|
6
|
-
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
|
7
|
-
import { useRouteMatch } from 'react-router-dom';
|
|
8
|
-
|
|
9
|
-
const RELATIONS_TO_DISPLAY = 5;
|
|
10
|
-
const SEARCH_RESULTS_TO_DISPLAY = 10;
|
|
11
|
-
|
|
12
|
-
interface IRelationLoadProps extends IPlatformFilteredSelectFieldProps {}
|
|
13
|
-
|
|
14
|
-
// This is largely a copy of https://github.com/strapi/strapi/blob/4e6961c7b8127f0f3bb0ad1fc430351ae9c4b8fa/packages/core/admin/admin/src/content-manager/components/Relations/RelationInputDataManager.tsx
|
|
15
|
-
|
|
16
|
-
const useRelationLoad = ({ name, attribute }: IRelationLoadProps) => {
|
|
17
|
-
const fieldName = name || '';
|
|
18
|
-
const { modifiedData, initialData, slug, layout, allLayoutData, isCreatingEntry, relationLoad } =
|
|
19
|
-
useCMEditViewDataManager() as any;
|
|
20
|
-
const { params } =
|
|
21
|
-
useRouteMatch<{ origin?: string }>('/content-manager/collectionType/:collectionType/create/clone/:origin') ?? {};
|
|
22
|
-
const { origin } = params ?? {};
|
|
23
|
-
const isCloningEntry = Boolean(origin);
|
|
24
|
-
const entityId = origin || modifiedData.id;
|
|
25
|
-
|
|
26
|
-
const { componentUid, componentId, relationMainFieldName, targetAttributes, targetField, targetFieldName } =
|
|
27
|
-
RelationHelper.getTargetAttributes(fieldName, attribute, modifiedData, layout, allLayoutData);
|
|
28
|
-
|
|
29
|
-
const nameSplit = targetFieldName.split('.');
|
|
30
|
-
const isComponentRelation = Boolean(componentUid);
|
|
31
|
-
const initialDataPath = getInitialDataPathUsingTempKeys(initialData, modifiedData)(targetField);
|
|
32
|
-
|
|
33
|
-
const currentLastPage = Math.ceil((get(initialData, targetField, []) || []).length / RELATIONS_TO_DISPLAY);
|
|
34
|
-
|
|
35
|
-
// /content-manager/relations/[model]/[id]/[field-name]
|
|
36
|
-
const relationFetchEndpoint = React.useMemo(() => {
|
|
37
|
-
if (isCreatingEntry && !origin) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (componentUid) {
|
|
42
|
-
// repeatable components and dz are dynamically created
|
|
43
|
-
// if no componentId exists in modifiedData it means that the user just created it
|
|
44
|
-
// there then are no relations to request
|
|
45
|
-
return componentId ? `/content-manager/relations/${componentUid}/${componentId}/${nameSplit.at(-1)}` : null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return `/content-manager/relations/${slug}/${entityId}/${targetField.split('.').at(-1)}`;
|
|
49
|
-
}, [isCreatingEntry, origin, componentUid, slug, entityId, targetField, componentId, nameSplit]);
|
|
50
|
-
|
|
51
|
-
// /content-manager/relations/[model]/[field-name]
|
|
52
|
-
const relationSearchEndpoint = React.useMemo(() => {
|
|
53
|
-
if (componentUid) {
|
|
54
|
-
return `/content-manager/relations/${componentUid}/${targetField.split('.').at(-1)}`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return `/content-manager/relations/${slug}/${targetField.split('.').at(-1)}`;
|
|
58
|
-
}, [componentUid, slug, targetField]);
|
|
59
|
-
|
|
60
|
-
const { relations, search, searchFor } = useRelation([slug, initialDataPath.join('.'), modifiedData.id], {
|
|
61
|
-
relation: {
|
|
62
|
-
enabled: !!relationFetchEndpoint,
|
|
63
|
-
endpoint: relationFetchEndpoint!,
|
|
64
|
-
pageGoal: currentLastPage,
|
|
65
|
-
pageParams: {
|
|
66
|
-
pageSize: RELATIONS_TO_DISPLAY
|
|
67
|
-
},
|
|
68
|
-
onLoad(value) {
|
|
69
|
-
relationLoad?.({
|
|
70
|
-
target: {
|
|
71
|
-
initialDataPath: ['initialData', ...initialDataPath],
|
|
72
|
-
modifiedDataPath: ['modifiedData', ...nameSplit],
|
|
73
|
-
value
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
},
|
|
77
|
-
normalizeArguments: {
|
|
78
|
-
// @ts-expect-error
|
|
79
|
-
mainFieldName: relationMainFieldName,
|
|
80
|
-
shouldAddLink: true,
|
|
81
|
-
// @ts-expect-error
|
|
82
|
-
targetModel: targetAttributes?.targetModel
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
search: {
|
|
86
|
-
endpoint: relationSearchEndpoint,
|
|
87
|
-
pageParams: {
|
|
88
|
-
// eslint-disable-next-line no-nested-ternary
|
|
89
|
-
entityId: isCreatingEntry || isCloningEntry ? undefined : isComponentRelation ? componentId : entityId,
|
|
90
|
-
pageSize: SEARCH_RESULTS_TO_DISPLAY
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* How to calculate the total number of relations even if you don't
|
|
97
|
-
* have them all loaded in the browser.
|
|
98
|
-
*
|
|
99
|
-
* 1. The `infiniteQuery` gives you the total number of relations in the pagination result.
|
|
100
|
-
* 2. You can diff the length of the browserState vs the fetchedServerState to determine if you've
|
|
101
|
-
* either added or removed relations.
|
|
102
|
-
* 3. Add them together, if you've removed relations you'll get a negative number and it'll
|
|
103
|
-
* actually subtract from the total number on the server (regardless of how many you fetched).
|
|
104
|
-
*/
|
|
105
|
-
const relationsFromModifiedData = get(modifiedData, targetField, []);
|
|
106
|
-
const browserRelationsCount = (relationsFromModifiedData || [])?.length;
|
|
107
|
-
const serverRelationsCount = (get(initialData, initialDataPath) ?? []).length;
|
|
108
|
-
const realServerRelationsCount = relations.data?.pages[0]?.pagination?.total ?? 0;
|
|
109
|
-
/**
|
|
110
|
-
* _IF_ theres no relations data and the browserCount is the same as serverCount you can therefore assume
|
|
111
|
-
* that the browser count is correct because we've just _made_ this entry and the in-component hook is now fetching.
|
|
112
|
-
*/
|
|
113
|
-
const totalRelations =
|
|
114
|
-
!relations.data && browserRelationsCount === serverRelationsCount
|
|
115
|
-
? browserRelationsCount
|
|
116
|
-
: browserRelationsCount - serverRelationsCount + realServerRelationsCount;
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
relations,
|
|
120
|
-
search,
|
|
121
|
-
searchFor,
|
|
122
|
-
isCloningEntry,
|
|
123
|
-
relationsFromModifiedData,
|
|
124
|
-
totalRelations: totalRelations || 0
|
|
125
|
-
};
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
export { useRelationLoad };
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { useRelation } from '../../StrapiCore/admin/admin/src/content-manager/components/Relations/useRelation';
|
|
4
|
+
import { getInitialDataPathUsingTempKeys } from '../../StrapiCore/admin/admin/src/content-manager/utils/paths';
|
|
5
|
+
import RelationHelper, { IPlatformFilteredSelectFieldProps } from '../utils/relation-helper';
|
|
6
|
+
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
|
7
|
+
import { useRouteMatch } from 'react-router-dom';
|
|
8
|
+
|
|
9
|
+
const RELATIONS_TO_DISPLAY = 5;
|
|
10
|
+
const SEARCH_RESULTS_TO_DISPLAY = 10;
|
|
11
|
+
|
|
12
|
+
interface IRelationLoadProps extends IPlatformFilteredSelectFieldProps {}
|
|
13
|
+
|
|
14
|
+
// This is largely a copy of https://github.com/strapi/strapi/blob/4e6961c7b8127f0f3bb0ad1fc430351ae9c4b8fa/packages/core/admin/admin/src/content-manager/components/Relations/RelationInputDataManager.tsx
|
|
15
|
+
|
|
16
|
+
const useRelationLoad = ({ name, attribute }: IRelationLoadProps) => {
|
|
17
|
+
const fieldName = name || '';
|
|
18
|
+
const { modifiedData, initialData, slug, layout, allLayoutData, isCreatingEntry, relationLoad } =
|
|
19
|
+
useCMEditViewDataManager() as any;
|
|
20
|
+
const { params } =
|
|
21
|
+
useRouteMatch<{ origin?: string }>('/content-manager/collectionType/:collectionType/create/clone/:origin') ?? {};
|
|
22
|
+
const { origin } = params ?? {};
|
|
23
|
+
const isCloningEntry = Boolean(origin);
|
|
24
|
+
const entityId = origin || modifiedData.id;
|
|
25
|
+
|
|
26
|
+
const { componentUid, componentId, relationMainFieldName, targetAttributes, targetField, targetFieldName } =
|
|
27
|
+
RelationHelper.getTargetAttributes(fieldName, attribute, modifiedData, layout, allLayoutData);
|
|
28
|
+
|
|
29
|
+
const nameSplit = targetFieldName.split('.');
|
|
30
|
+
const isComponentRelation = Boolean(componentUid);
|
|
31
|
+
const initialDataPath = getInitialDataPathUsingTempKeys(initialData, modifiedData)(targetField);
|
|
32
|
+
|
|
33
|
+
const currentLastPage = Math.ceil((get(initialData, targetField, []) || []).length / RELATIONS_TO_DISPLAY);
|
|
34
|
+
|
|
35
|
+
// /content-manager/relations/[model]/[id]/[field-name]
|
|
36
|
+
const relationFetchEndpoint = React.useMemo(() => {
|
|
37
|
+
if (isCreatingEntry && !origin) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (componentUid) {
|
|
42
|
+
// repeatable components and dz are dynamically created
|
|
43
|
+
// if no componentId exists in modifiedData it means that the user just created it
|
|
44
|
+
// there then are no relations to request
|
|
45
|
+
return componentId ? `/content-manager/relations/${componentUid}/${componentId}/${nameSplit.at(-1)}` : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return `/content-manager/relations/${slug}/${entityId}/${targetField.split('.').at(-1)}`;
|
|
49
|
+
}, [isCreatingEntry, origin, componentUid, slug, entityId, targetField, componentId, nameSplit]);
|
|
50
|
+
|
|
51
|
+
// /content-manager/relations/[model]/[field-name]
|
|
52
|
+
const relationSearchEndpoint = React.useMemo(() => {
|
|
53
|
+
if (componentUid) {
|
|
54
|
+
return `/content-manager/relations/${componentUid}/${targetField.split('.').at(-1)}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return `/content-manager/relations/${slug}/${targetField.split('.').at(-1)}`;
|
|
58
|
+
}, [componentUid, slug, targetField]);
|
|
59
|
+
|
|
60
|
+
const { relations, search, searchFor } = useRelation([slug, initialDataPath.join('.'), modifiedData.id], {
|
|
61
|
+
relation: {
|
|
62
|
+
enabled: !!relationFetchEndpoint,
|
|
63
|
+
endpoint: relationFetchEndpoint!,
|
|
64
|
+
pageGoal: currentLastPage,
|
|
65
|
+
pageParams: {
|
|
66
|
+
pageSize: RELATIONS_TO_DISPLAY
|
|
67
|
+
},
|
|
68
|
+
onLoad(value) {
|
|
69
|
+
relationLoad?.({
|
|
70
|
+
target: {
|
|
71
|
+
initialDataPath: ['initialData', ...initialDataPath],
|
|
72
|
+
modifiedDataPath: ['modifiedData', ...nameSplit],
|
|
73
|
+
value
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
normalizeArguments: {
|
|
78
|
+
// @ts-expect-error
|
|
79
|
+
mainFieldName: relationMainFieldName,
|
|
80
|
+
shouldAddLink: true,
|
|
81
|
+
// @ts-expect-error
|
|
82
|
+
targetModel: targetAttributes?.targetModel
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
search: {
|
|
86
|
+
endpoint: relationSearchEndpoint,
|
|
87
|
+
pageParams: {
|
|
88
|
+
// eslint-disable-next-line no-nested-ternary
|
|
89
|
+
entityId: isCreatingEntry || isCloningEntry ? undefined : isComponentRelation ? componentId : entityId,
|
|
90
|
+
pageSize: SEARCH_RESULTS_TO_DISPLAY
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* How to calculate the total number of relations even if you don't
|
|
97
|
+
* have them all loaded in the browser.
|
|
98
|
+
*
|
|
99
|
+
* 1. The `infiniteQuery` gives you the total number of relations in the pagination result.
|
|
100
|
+
* 2. You can diff the length of the browserState vs the fetchedServerState to determine if you've
|
|
101
|
+
* either added or removed relations.
|
|
102
|
+
* 3. Add them together, if you've removed relations you'll get a negative number and it'll
|
|
103
|
+
* actually subtract from the total number on the server (regardless of how many you fetched).
|
|
104
|
+
*/
|
|
105
|
+
const relationsFromModifiedData = get(modifiedData, targetField, []);
|
|
106
|
+
const browserRelationsCount = (relationsFromModifiedData || [])?.length;
|
|
107
|
+
const serverRelationsCount = (get(initialData, initialDataPath) ?? []).length;
|
|
108
|
+
const realServerRelationsCount = relations.data?.pages[0]?.pagination?.total ?? 0;
|
|
109
|
+
/**
|
|
110
|
+
* _IF_ theres no relations data and the browserCount is the same as serverCount you can therefore assume
|
|
111
|
+
* that the browser count is correct because we've just _made_ this entry and the in-component hook is now fetching.
|
|
112
|
+
*/
|
|
113
|
+
const totalRelations =
|
|
114
|
+
!relations.data && browserRelationsCount === serverRelationsCount
|
|
115
|
+
? browserRelationsCount
|
|
116
|
+
: browserRelationsCount - serverRelationsCount + realServerRelationsCount;
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
relations,
|
|
120
|
+
search,
|
|
121
|
+
searchFor,
|
|
122
|
+
isCloningEntry,
|
|
123
|
+
relationsFromModifiedData,
|
|
124
|
+
totalRelations: totalRelations || 0
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export { useRelationLoad };
|