@strapi/admin 4.5.0-beta.0 → 4.5.1
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/admin/src/StrapiApp.js +17 -6
- package/admin/src/assets/images/hot-air-balloon.png +0 -0
- package/admin/src/assets/images/icon_offline-cloud.svg +3 -3
- package/admin/src/assets/images/logo-strapi-2022.svg +7 -0
- package/admin/src/assets/images/upgrade-details.png +0 -0
- package/admin/src/content-manager/components/DynamicTable/CellContent/CellValue.js +1 -1
- package/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js +5 -4
- package/admin/src/content-manager/components/DynamicTable/CellContent/index.js +10 -0
- package/admin/src/content-manager/components/DynamicTable/index.js +21 -4
- package/admin/src/content-manager/components/DynamicZone/components/{AddComponentButton/index.js → AddComponentButton.js} +12 -6
- package/admin/src/content-manager/components/DynamicZone/components/{ComponentPicker/Category/ComponentCard/index.js → ComponentCard.js} +8 -19
- package/admin/src/content-manager/components/DynamicZone/components/{ComponentPicker/Category/index.js → ComponentCategory.js} +19 -18
- package/admin/src/content-manager/components/DynamicZone/components/{ComponentPicker/index.js → ComponentPicker.js} +36 -38
- package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +195 -0
- package/admin/src/content-manager/components/DynamicZone/components/{DzLabel/index.js → DynamicZoneLabel.js} +13 -5
- package/admin/src/content-manager/components/DynamicZone/index.js +35 -116
- package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +103 -60
- package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +169 -162
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +70 -16
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/findLeafByPathAndReplace.js +52 -0
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/index.js +2 -0
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/recursivelyFindPathsBasedOnCondition.js +72 -0
- package/admin/src/content-manager/components/FieldComponent/index.js +9 -2
- package/admin/src/content-manager/components/PreviewWysiwyg/index.js +1 -1
- package/admin/src/content-manager/components/RelationInput/RelationInput.js +80 -76
- package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +95 -63
- package/admin/src/content-manager/components/RelationInputDataManager/utils/diffRelations.js +24 -0
- package/admin/src/content-manager/components/RelationInputDataManager/utils/index.js +2 -1
- package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeRelations.js +8 -29
- package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeSearchResults.js +8 -4
- package/admin/src/content-manager/components/RelationInputDataManager/utils/select.js +1 -0
- package/admin/src/content-manager/components/RepeatableComponent/index.js +4 -3
- package/admin/src/content-manager/hooks/__test__/usePrev.test.js +26 -0
- package/admin/src/content-manager/hooks/index.js +1 -0
- package/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +19 -48
- package/admin/src/content-manager/hooks/usePrev.js +14 -0
- package/admin/src/content-manager/hooks/useRelation/useRelation.js +100 -7
- package/admin/src/content-manager/pages/App/reducer.js +3 -0
- package/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js +3 -3
- package/admin/src/content-manager/pages/ListSettingsView/components/Settings.js +2 -2
- package/admin/src/content-manager/pages/ListSettingsView/components/SortDisplayedFields.js +1 -1
- package/admin/src/core/apis/CustomFields.js +0 -1
- package/admin/src/core/store/configureStore.js +17 -2
- package/admin/src/favicon.png +0 -0
- package/admin/src/hooks/useFetchMarketplacePlugins/index.js +2 -2
- package/admin/src/hooks/useFetchMarketplacePlugins/utils/api.js +4 -2
- package/admin/src/hooks/useFetchMarketplaceProviders/index.js +3 -3
- package/admin/src/hooks/useFetchMarketplaceProviders/utils/api.js +5 -3
- package/admin/src/index.js +1 -0
- package/admin/src/pages/App/index.js +1 -1
- package/admin/src/pages/HomePage/assets/corner-ornament.svg +48 -0
- package/admin/src/pages/HomePage/index.js +4 -3
- package/admin/src/pages/MarketplacePage/components/NpmPackageCard/CardButton.js +110 -0
- package/admin/src/pages/MarketplacePage/components/NpmPackageCard/InstallPluginButton.js +32 -21
- package/admin/src/pages/MarketplacePage/components/NpmPackageCard/PackageStats.js +79 -0
- package/admin/src/pages/MarketplacePage/components/NpmPackageCard/index.js +28 -11
- package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/FilterSelect.js +42 -0
- package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/FiltersPopover.js +96 -0
- package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/index.js +107 -0
- package/admin/src/pages/MarketplacePage/components/NpmPackagesGrid/index.js +4 -0
- package/admin/src/pages/MarketplacePage/components/SortSelect/index.js +70 -0
- package/admin/src/pages/MarketplacePage/index.js +68 -8
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormApiTokenContainer/index.js +5 -4
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormBody/index.js +4 -3
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormHead/index.js +6 -2
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +1 -1
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +5 -4
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/schema.js +1 -1
- package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js +7 -38
- package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/options.js +31 -0
- package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/index.js +32 -43
- package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js +1 -1
- package/admin/src/pages/SettingsPage/pages/Roles/ListPage/components/RoleRow/index.js +3 -1
- package/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js +2 -1
- package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventInput/index.js +2 -2
- package/admin/src/translations/ca.json +1 -1
- package/admin/src/translations/de.json +1 -1
- package/admin/src/translations/dk.json +1 -1
- package/admin/src/translations/en.json +21 -7
- package/admin/src/translations/es.json +1 -1
- package/admin/src/translations/fr.json +1 -1
- package/admin/src/translations/gu.json +1 -1
- package/admin/src/translations/he.json +1 -1
- package/admin/src/translations/hi.json +1 -1
- package/admin/src/translations/hu.json +1 -1
- package/admin/src/translations/id.json +1 -1
- package/admin/src/translations/it.json +1 -1
- package/admin/src/translations/ja.json +1 -1
- package/admin/src/translations/ko.json +1 -1
- package/admin/src/translations/ml.json +1 -1
- package/admin/src/translations/nl.json +1 -1
- package/admin/src/translations/no.json +1 -1
- package/admin/src/translations/pl.json +1 -1
- package/admin/src/translations/pt-BR.json +15 -15
- package/admin/src/translations/ru.json +1 -1
- package/admin/src/translations/sa.json +1 -1
- package/admin/src/translations/sk.json +1 -1
- package/admin/src/translations/sv.json +118 -86
- package/admin/src/translations/th.json +1 -1
- package/admin/src/translations/zh-Hans.json +1 -1
- package/admin/src/translations/zh.json +1 -1
- package/build/1856.db9f5782.chunk.js +174 -0
- package/build/2077.fed8c9c3.chunk.js +206 -0
- package/build/2912.fccb2c43.chunk.js +259 -0
- package/build/4318.5e670740.chunk.js +30 -0
- package/build/{9166.8fcb3019.chunk.js → 4610.7614b003.chunk.js} +22 -21
- package/build/4715.8e33d630.chunk.js +387 -0
- package/build/{4800.d09f1225.chunk.js → 4800.a6935af6.chunk.js} +1 -1
- package/build/4982.9e58ea3f.chunk.js +325 -0
- package/build/617f9c948fa79e6d73bd.png +0 -0
- package/build/6925.bb6dd64d.chunk.js +762 -0
- package/build/6d21938306785f176538.png +0 -0
- package/build/70674f63fc3904c20de0.svg +7 -0
- package/build/{7379.d246dd38.chunk.js → 7379.e972985f.chunk.js} +1 -1
- package/build/7692.31e83caa.chunk.js +470 -0
- package/build/7841.4804bd98.chunk.js +259 -0
- package/build/7866.6db2248d.chunk.js +505 -0
- package/build/7e9af4fb7e723fcebf1f.svg +48 -0
- package/build/8380.37126e0d.chunk.js +299 -0
- package/build/8549.5e5fb6b6.chunk.js +159 -0
- package/build/8738.5a02bffb.chunk.js +463 -0
- package/build/{9066.26faf397.chunk.js → 9066.5d980488.chunk.js} +5 -5
- package/build/9420.7addc099.chunk.js +505 -0
- package/build/9649.b6afc945.chunk.js +199 -0
- package/build/9d5d788027e86620c234.svg +5 -0
- package/build/Admin-authenticatedApp.c07d2a86.chunk.js +80 -0
- package/build/{Admin_homePage.4b2be829.chunk.js → Admin_homePage.26d32e30.chunk.js} +5 -4
- package/build/Admin_marketplace.444ff7b8.chunk.js +22 -0
- package/build/Admin_settingsPage.bf2234e1.chunk.js +178 -0
- package/build/admin-app.b157c10a.chunk.js +112 -0
- package/build/{admin-edit-roles-page.4dd6bcb9.chunk.js → admin-edit-roles-page.69d9fcb2.chunk.js} +1 -1
- package/build/ca-json.07ae0f2c.chunk.js +1 -0
- package/build/content-manager.f38edbb6.chunk.js +1202 -0
- package/build/content-type-builder-translation-pt-BR-json.6fe3b8d1.chunk.js +1 -0
- package/build/content-type-builder-translation-sv-json.6deff030.chunk.js +1 -0
- package/build/{content-type-builder.a6e29716.chunk.js → content-type-builder.16af63a6.chunk.js} +13 -13
- package/build/de-json.6b3e1894.chunk.js +1 -0
- package/build/dk-json.144c6a8e.chunk.js +1 -0
- package/build/{email-settings-page.bfe6227f.chunk.js → email-settings-page.91c925a5.chunk.js} +6 -6
- package/build/email-translation-en-json.ebad8943.chunk.js +1 -0
- package/build/en-json.4a269f6b.chunk.js +1 -0
- package/build/es-json.6d123a82.chunk.js +1 -0
- package/build/fr-json.28ab54cb.chunk.js +1 -0
- package/build/gu-json.9a50ea64.chunk.js +1 -0
- package/build/he-json.72f18790.chunk.js +1 -0
- package/build/hi-json.0301b7ba.chunk.js +1 -0
- package/build/hu-json.c4b641bb.chunk.js +1 -0
- package/build/{i18n-settings-page.18166125.chunk.js → i18n-settings-page.4ef64441.chunk.js} +5 -5
- package/build/id-json.86035797.chunk.js +1 -0
- package/build/index.html +1 -1
- package/build/it-json.bbdc8993.chunk.js +1 -0
- package/build/ja-json.1c9eeeec.chunk.js +1 -0
- package/build/ko-json.e1f66398.chunk.js +1 -0
- package/build/main.ca8b0ee3.js +9465 -0
- package/build/ml-json.963c889f.chunk.js +1 -0
- package/build/nl-json.2b8cc3a0.chunk.js +1 -0
- package/build/no-json.a58c28bd.chunk.js +1 -0
- package/build/pl-json.249626b3.chunk.js +1 -0
- package/build/pt-BR-json.2b72b1d6.chunk.js +1 -0
- package/build/ru-json.d7cfc2ff.chunk.js +1 -0
- package/build/runtime~main.ede9da1e.js +2 -0
- package/build/sa-json.44e95991.chunk.js +1 -0
- package/build/sk-json.7ba4b330.chunk.js +1 -0
- package/build/sv-json.fb1081ff.chunk.js +1 -0
- package/build/th-json.a67309b1.chunk.js +1 -0
- package/build/{upload-settings.3d613216.chunk.js → upload-settings.3f7ad973.chunk.js} +5 -5
- package/build/{users-advanced-settings-page.f4051d92.chunk.js → users-advanced-settings-page.6a838320.chunk.js} +5 -5
- package/build/users-permissions-translation-sv-json.d5d11648.chunk.js +1 -0
- package/build/{webhook-edit-page.9e46fc3f.chunk.js → webhook-edit-page.dc9442ce.chunk.js} +1 -1
- package/build/webhook-list-page.a110c462.chunk.js +134 -0
- package/build/zh-Hans-json.21617c24.chunk.js +1 -0
- package/build/zh-json.608aaf24.chunk.js +1 -0
- package/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js +3 -2
- package/env.js +1 -0
- package/package.json +18 -17
- package/scripts/build.js +11 -0
- package/server/content-types/api-token.js +1 -1
- package/utils/create-plugins-exclude-path.js +40 -0
- package/webpack.alias.js +0 -13
- package/webpack.config.js +4 -1
- package/admin/src/assets/images/banner_strapi-rocket.png +0 -0
- package/admin/src/assets/images/big-logo-home.png +0 -0
- package/admin/src/assets/images/homepage-logo.png +0 -0
- package/admin/src/assets/images/icon_made-by-strapi.svg +0 -5
- package/admin/src/assets/images/logo_strapi_auth.png +0 -0
- package/admin/src/assets/images/logo_strapi_auth_v4.png +0 -0
- package/admin/src/assets/images/logo_strapi_menu.png +0 -0
- package/admin/src/assets/images/oops.png +0 -0
- package/admin/src/content-manager/components/DynamicZone/components/Component/Rectangle.js +0 -19
- package/admin/src/content-manager/components/DynamicZone/components/Component/index.js +0 -191
- package/admin/src/content-manager/components/State/index.js +0 -37
- package/admin/src/content-manager/icons/Bold/index.js +0 -22
- package/admin/src/content-manager/icons/Code/index.js +0 -13
- package/admin/src/content-manager/icons/Cross/index.js +0 -28
- package/admin/src/content-manager/icons/Italic/index.js +0 -23
- package/admin/src/content-manager/icons/Link/index.js +0 -21
- package/admin/src/content-manager/icons/Media/index.js +0 -14
- package/admin/src/content-manager/icons/Na/index.js +0 -39
- package/admin/src/content-manager/icons/Ol/index.js +0 -13
- package/admin/src/content-manager/icons/Quote/index.js +0 -13
- package/admin/src/content-manager/icons/Striked/index.js +0 -24
- package/admin/src/content-manager/icons/Ul/index.js +0 -15
- package/admin/src/content-manager/icons/Underline/index.js +0 -22
- package/admin/src/favicon.ico +0 -0
- package/build/15026a3d58aeb2828134.png +0 -0
- package/build/1856.d8f13391.chunk.js +0 -173
- package/build/1939.e3c87653.chunk.js +0 -325
- package/build/2077.31a2d91e.chunk.js +0 -205
- package/build/2912.ab68a736.chunk.js +0 -258
- package/build/4318.7d167b58.chunk.js +0 -30
- package/build/4715.44b1ef9b.chunk.js +0 -386
- package/build/4982.c2a311b7.chunk.js +0 -324
- package/build/6925.f5c8b6fc.chunk.js +0 -761
- package/build/7841.4b67af3f.chunk.js +0 -258
- package/build/7866.5fbeb7e5.chunk.js +0 -504
- package/build/8380.9b53a31d.chunk.js +0 -284
- package/build/8549.cf10b5d1.chunk.js +0 -158
- package/build/8738.a30a2160.chunk.js +0 -461
- package/build/90f49a385afb000fb1d4.svg +0 -5
- package/build/9420.0fe11290.chunk.js +0 -504
- package/build/962.8651ba3f.chunk.js +0 -184
- package/build/Admin-authenticatedApp.883449a5.chunk.js +0 -80
- package/build/Admin_marketplace.82c0570b.chunk.js +0 -11
- package/build/Admin_settingsPage.98e2a62b.chunk.js +0 -178
- package/build/a6b842e0b6d2b61135d1.svg +0 -5
- package/build/admin-app.a61d5c2e.chunk.js +0 -112
- package/build/b997a22a2e0b87ef1fa2.ico +0 -0
- package/build/bd81ba6c07827282255d.png +0 -0
- package/build/c3de6118ef47086ad05c.png +0 -0
- package/build/ca-json.82df6eab.chunk.js +0 -1
- package/build/content-manager.933dc286.chunk.js +0 -1201
- package/build/content-type-builder-translation-pt-BR-json.d6c7fcc1.chunk.js +0 -1
- package/build/de-json.0ad554eb.chunk.js +0 -1
- package/build/dk-json.e195ea1a.chunk.js +0 -1
- package/build/email-translation-en-json.3d74ff95.chunk.js +0 -1
- package/build/en-json.1889403c.chunk.js +0 -1
- package/build/es-json.09f80f6e.chunk.js +0 -1
- package/build/fb376b132d18bf4522ca.png +0 -0
- package/build/fde9b1ad0670d29a2516.png +0 -0
- package/build/fr-json.606d056b.chunk.js +0 -1
- package/build/gu-json.9881264f.chunk.js +0 -1
- package/build/he-json.3b825d80.chunk.js +0 -1
- package/build/hi-json.83dcf48f.chunk.js +0 -1
- package/build/hu-json.6f328bce.chunk.js +0 -1
- package/build/id-json.1f3c4303.chunk.js +0 -1
- package/build/it-json.494ac432.chunk.js +0 -1
- package/build/ja-json.6f262117.chunk.js +0 -1
- package/build/ko-json.36dc3b9a.chunk.js +0 -1
- package/build/main.63e7ea0a.js +0 -9338
- package/build/ml-json.9566bf9a.chunk.js +0 -1
- package/build/nl-json.94c3a289.chunk.js +0 -1
- package/build/no-json.40386397.chunk.js +0 -1
- package/build/pl-json.ccc6ef23.chunk.js +0 -1
- package/build/pt-BR-json.744f024d.chunk.js +0 -1
- package/build/ru-json.d22ea13c.chunk.js +0 -1
- package/build/runtime~main.3a5e1b07.js +0 -2
- package/build/sa-json.8fb1c04d.chunk.js +0 -1
- package/build/sk-json.6c7335d4.chunk.js +0 -1
- package/build/sv-json.2e589a7d.chunk.js +0 -1
- package/build/th-json.72e8de3d.chunk.js +0 -1
- package/build/users-permissions-translation-sv-json.83c60841.chunk.js +0 -1
- package/build/webhook-list-page.a712ae40.chunk.js +0 -134
- package/build/zh-Hans-json.a4d7dc69.chunk.js +0 -1
- package/build/zh-json.66aa2ae1.chunk.js +0 -1
|
@@ -3,8 +3,15 @@ import unset from 'lodash/unset';
|
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import set from 'lodash/set';
|
|
5
5
|
import take from 'lodash/take';
|
|
6
|
-
import
|
|
7
|
-
import
|
|
6
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
7
|
+
import uniqBy from 'lodash/uniqBy';
|
|
8
|
+
import merge from 'lodash/merge';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
findLeafByPathAndReplace,
|
|
12
|
+
moveFields,
|
|
13
|
+
recursivelyFindPathsBasedOnCondition,
|
|
14
|
+
} from './utils';
|
|
8
15
|
import { getMaxTempKey } from '../../utils';
|
|
9
16
|
|
|
10
17
|
const initialState = {
|
|
@@ -26,63 +33,111 @@ const reducer = (state, action) =>
|
|
|
26
33
|
produce(state, (draftState) => {
|
|
27
34
|
switch (action.type) {
|
|
28
35
|
case 'ADD_NON_REPEATABLE_COMPONENT_TO_FIELD': {
|
|
29
|
-
|
|
30
|
-
draftState,
|
|
31
|
-
['modifiedData', ...action.keys],
|
|
32
|
-
state.componentsDataStructure[action.componentUid]
|
|
33
|
-
);
|
|
36
|
+
const { componentLayoutData, allComponents } = action;
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
const relationPaths = recursivelyFindPathsBasedOnCondition(
|
|
39
|
+
allComponents,
|
|
40
|
+
(value) => value.type === 'relation'
|
|
41
|
+
)(componentLayoutData.attributes);
|
|
39
42
|
|
|
40
43
|
const defaultDataStructure = {
|
|
41
|
-
...state.componentsDataStructure[
|
|
42
|
-
__temp_key__: getMaxTempKey(currentValue) + 1,
|
|
44
|
+
...state.componentsDataStructure[componentLayoutData.uid],
|
|
43
45
|
};
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
47
|
+
const repeatableFields = recursivelyFindPathsBasedOnCondition(
|
|
48
|
+
allComponents,
|
|
49
|
+
(value) => value.type === 'component' && value.repeatable
|
|
50
|
+
)(componentLayoutData.attributes);
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
const componentDataStructure = relationPaths.reduce((acc, current) => {
|
|
53
|
+
const [componentName] = current.split('.');
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Why do we do this? Because if a repeatable component
|
|
57
|
+
* has another repeatable component inside of it we
|
|
58
|
+
* don't need to attach the array at this point because that will be
|
|
59
|
+
* done again deeper in the nest.
|
|
60
|
+
*/
|
|
61
|
+
if (!repeatableFields.includes(componentName)) {
|
|
62
|
+
set(acc, current, []);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return acc;
|
|
66
|
+
}, defaultDataStructure);
|
|
67
|
+
|
|
68
|
+
set(draftState, ['modifiedData', ...action.keys], componentDataStructure);
|
|
56
69
|
|
|
57
70
|
break;
|
|
58
71
|
}
|
|
59
|
-
case 'ADD_COMPONENT_TO_DYNAMIC_ZONE':
|
|
60
|
-
|
|
72
|
+
case 'ADD_COMPONENT_TO_DYNAMIC_ZONE':
|
|
73
|
+
case 'ADD_REPEATABLE_COMPONENT_TO_FIELD': {
|
|
74
|
+
const { keys, allComponents, componentLayoutData, shouldCheckErrors } = action;
|
|
61
75
|
|
|
62
|
-
if (
|
|
76
|
+
if (shouldCheckErrors) {
|
|
63
77
|
draftState.shouldCheckErrors = !state.shouldCheckErrors;
|
|
64
78
|
}
|
|
65
79
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
};
|
|
80
|
+
if (action.type === 'ADD_COMPONENT_TO_DYNAMIC_ZONE') {
|
|
81
|
+
draftState.modifiedDZName = keys[0];
|
|
82
|
+
}
|
|
70
83
|
|
|
71
|
-
const currentValue = get(state, ['modifiedData', ...
|
|
72
|
-
const updatedValue = currentValue
|
|
73
|
-
? [...currentValue, defaultDataStructure]
|
|
74
|
-
: [defaultDataStructure];
|
|
84
|
+
const currentValue = get(state, ['modifiedData', ...keys], []);
|
|
75
85
|
|
|
76
|
-
|
|
86
|
+
const defaultDataStructure =
|
|
87
|
+
action.type === 'ADD_COMPONENT_TO_DYNAMIC_ZONE'
|
|
88
|
+
? {
|
|
89
|
+
...state.componentsDataStructure[componentLayoutData.uid],
|
|
90
|
+
__component: componentLayoutData.uid,
|
|
91
|
+
}
|
|
92
|
+
: {
|
|
93
|
+
...state.componentsDataStructure[componentLayoutData.uid],
|
|
94
|
+
__temp_key__: getMaxTempKey(currentValue) + 1,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const relationPaths = recursivelyFindPathsBasedOnCondition(
|
|
98
|
+
allComponents,
|
|
99
|
+
(value) => value.type === 'relation'
|
|
100
|
+
)(componentLayoutData.attributes);
|
|
101
|
+
|
|
102
|
+
const repeatableFields = recursivelyFindPathsBasedOnCondition(
|
|
103
|
+
allComponents,
|
|
104
|
+
(value) => value.type === 'component' && value.repeatable
|
|
105
|
+
)(componentLayoutData.attributes);
|
|
106
|
+
|
|
107
|
+
const componentDataStructure = relationPaths.reduce((acc, current) => {
|
|
108
|
+
const [componentName] = current.split('.');
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Why do we do this? Because if a repeatable component
|
|
112
|
+
* has another repeatable component inside of it we
|
|
113
|
+
* don't need to attach the array at this point because that will be
|
|
114
|
+
* done again deeper in the nest.
|
|
115
|
+
*/
|
|
116
|
+
if (!repeatableFields.includes(componentName)) {
|
|
117
|
+
set(acc, current, []);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return acc;
|
|
121
|
+
}, defaultDataStructure);
|
|
122
|
+
|
|
123
|
+
const newValue = Array.isArray(currentValue)
|
|
124
|
+
? [...currentValue, componentDataStructure]
|
|
125
|
+
: [componentDataStructure];
|
|
126
|
+
|
|
127
|
+
set(draftState, ['modifiedData', ...keys], newValue);
|
|
77
128
|
|
|
78
129
|
break;
|
|
79
130
|
}
|
|
131
|
+
|
|
80
132
|
case 'LOAD_RELATION': {
|
|
81
|
-
const initialDataPath = ['initialData', ...action.keys
|
|
82
|
-
const modifiedDataPath = ['modifiedData', ...action.keys
|
|
133
|
+
const initialDataPath = ['initialData', ...action.keys];
|
|
134
|
+
const modifiedDataPath = ['modifiedData', ...action.keys];
|
|
83
135
|
const { value } = action;
|
|
84
136
|
|
|
85
|
-
|
|
137
|
+
const initialDataRelations = get(state, initialDataPath);
|
|
138
|
+
const modifiedDataRelations = get(state, modifiedDataPath);
|
|
139
|
+
|
|
140
|
+
set(draftState, initialDataPath, uniqBy([...value, ...initialDataRelations], 'id'));
|
|
86
141
|
|
|
87
142
|
/**
|
|
88
143
|
* We need to set the value also on modifiedData, because initialData
|
|
@@ -90,156 +145,108 @@ const reducer = (state, action) =>
|
|
|
90
145
|
* both states, to render the dirty UI state
|
|
91
146
|
*/
|
|
92
147
|
|
|
93
|
-
set(draftState, modifiedDataPath, value);
|
|
148
|
+
set(draftState, modifiedDataPath, uniqBy([...value, ...modifiedDataRelations], 'id'));
|
|
94
149
|
|
|
95
150
|
break;
|
|
96
151
|
}
|
|
97
152
|
case 'CONNECT_RELATION': {
|
|
98
153
|
const path = ['modifiedData', ...action.keys];
|
|
99
|
-
const { value,
|
|
100
|
-
const connectedRelations = get(state, [...path, 'connect']);
|
|
101
|
-
const disconnectedRelations = get(state, [...path, 'disconnect']) ?? [];
|
|
102
|
-
const savedRelations = get(state, [...path, 'results']) ?? [];
|
|
103
|
-
const existInSavedRelation =
|
|
104
|
-
savedRelations?.findIndex((savedRelations) => savedRelations.id === value.id) !== -1;
|
|
105
|
-
|
|
106
|
-
if (!connectedRelations) {
|
|
107
|
-
set(draftState, [...path, 'connect'], []);
|
|
108
|
-
}
|
|
154
|
+
const { value, toOneRelation } = action;
|
|
109
155
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// Disconnect array handling
|
|
121
|
-
if (replace) {
|
|
122
|
-
// In xToOne relations we should place the saved relation in disconnected array to not display it
|
|
123
|
-
// only needed if there is a saved relation and it is not already stored in disconnected array
|
|
124
|
-
if (savedRelations.length && !disconnectedRelations.length) {
|
|
125
|
-
set(draftState, [...path, 'disconnect'], savedRelations);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// If the saved relation is stored in disconnected array
|
|
129
|
-
// We should remove it when an action requires to reconnect this relation
|
|
130
|
-
// We then reset the connect/disconnect state
|
|
131
|
-
if (disconnectedRelations.length) {
|
|
132
|
-
const existsInDisconnectedRelations =
|
|
133
|
-
disconnectedRelations.findIndex(
|
|
134
|
-
(disconnectedRelation) => disconnectedRelation?.id === value.id
|
|
135
|
-
) > -1;
|
|
136
|
-
|
|
137
|
-
if (existsInDisconnectedRelations) {
|
|
138
|
-
set(draftState, [...path, 'disconnect'], []);
|
|
139
|
-
set(draftState, [...path, 'connect'], []);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
} else if (disconnectedRelations.length) {
|
|
143
|
-
// In xToMany relations, when an action requires to connect a relation
|
|
144
|
-
// We should remove it from the disconnected array if it existed in it
|
|
145
|
-
const existsInDisconnect = disconnectedRelations.find(
|
|
146
|
-
(disconnectValue) => disconnectValue.id === value.id
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
if (existsInDisconnect) {
|
|
150
|
-
const newDisconnectArray = pull([...disconnectedRelations], existsInDisconnect);
|
|
151
|
-
set(draftState, [...path, 'disconnect'], newDisconnectArray);
|
|
152
|
-
}
|
|
156
|
+
/**
|
|
157
|
+
* If the field is a single relation field we don't want to append
|
|
158
|
+
* we just want to replace the value.
|
|
159
|
+
*/
|
|
160
|
+
if (toOneRelation) {
|
|
161
|
+
set(draftState, path, [value]);
|
|
162
|
+
} else {
|
|
163
|
+
const modifiedDataRelations = get(state, path);
|
|
164
|
+
const newRelations = [...modifiedDataRelations, value];
|
|
165
|
+
set(draftState, path, newRelations);
|
|
153
166
|
}
|
|
154
167
|
|
|
155
168
|
break;
|
|
156
169
|
}
|
|
157
170
|
case 'DISCONNECT_RELATION': {
|
|
158
171
|
const path = ['modifiedData', ...action.keys];
|
|
159
|
-
const {
|
|
160
|
-
const
|
|
161
|
-
const disconnectedRelations = get(state, [...path, 'disconnect']);
|
|
162
|
-
|
|
163
|
-
if (!disconnectedRelations) {
|
|
164
|
-
set(draftState, [...path, 'disconnect'], []);
|
|
165
|
-
}
|
|
172
|
+
const { id } = action;
|
|
173
|
+
const modifiedDataRelation = get(state, [...path]);
|
|
166
174
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const existsInConnect = connectedRelations.find(
|
|
172
|
-
(connectValue) => connectValue.id === value.id
|
|
173
|
-
);
|
|
175
|
+
/**
|
|
176
|
+
* TODO: before merge make this performant (e.g. 1000 relations === long time)
|
|
177
|
+
*/
|
|
178
|
+
const newRelations = modifiedDataRelation.filter((rel) => rel.id !== id);
|
|
174
179
|
|
|
175
|
-
|
|
176
|
-
const newConnectArray = pull([...connectedRelations], existsInConnect);
|
|
177
|
-
set(draftState, [...path, 'connect'], newConnectArray);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
+
set(draftState, path, newRelations);
|
|
180
181
|
|
|
181
182
|
break;
|
|
182
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* This action will be called when you open your entry (first load)
|
|
186
|
+
* but also every time you press publish.
|
|
187
|
+
*/
|
|
183
188
|
case 'INIT_FORM': {
|
|
184
|
-
const {
|
|
189
|
+
const {
|
|
190
|
+
initialValues,
|
|
191
|
+
relationalFieldPaths = [],
|
|
192
|
+
componentPaths = [],
|
|
193
|
+
repeatableComponentPaths = [],
|
|
194
|
+
dynamicZonePaths = [],
|
|
195
|
+
} = action;
|
|
185
196
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
197
|
+
/**
|
|
198
|
+
* You can't mutate an actions value.
|
|
199
|
+
* and spreading an object only clones
|
|
200
|
+
* the first level, the deeply nested values
|
|
201
|
+
* are a reference.
|
|
202
|
+
*/
|
|
203
|
+
const data = cloneDeep(initialValues);
|
|
190
204
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
* }
|
|
198
|
-
*
|
|
199
|
-
* The content API only returns { count: <int> }, which is why
|
|
200
|
-
* we need to extend the existing state rather than overwriting it.
|
|
201
|
-
*/
|
|
205
|
+
/**
|
|
206
|
+
* relationalFieldPaths won't be an array which is what we're expecting
|
|
207
|
+
* Therefore we reset these bits of state to the correct data type
|
|
208
|
+
* which is an array. Hence why we replace those fields.
|
|
209
|
+
*
|
|
210
|
+
*/
|
|
202
211
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
212
|
+
const mergeDataWithPreparedRelations = relationalFieldPaths
|
|
213
|
+
.map((path) => path.split('.'))
|
|
214
|
+
.reduce((acc, currentPaths) => {
|
|
215
|
+
const [componentName] = currentPaths;
|
|
216
|
+
|
|
217
|
+
if (state.modifiedData && get(state.modifiedData, componentName)) {
|
|
218
|
+
/**
|
|
219
|
+
* this will be null on initial load, however subsequent calls
|
|
220
|
+
* will have data in them correlating to the names of the relational fields.
|
|
221
|
+
*
|
|
222
|
+
* We also merge the fetched data so that things like `id` for components can be copied over
|
|
223
|
+
* which would be `undefined` in the `browserState`.
|
|
224
|
+
*/
|
|
225
|
+
const currentState = cloneDeep(get(state.modifiedData, componentName));
|
|
226
|
+
set(acc, componentName, merge(currentState, get(initialValues, componentName)));
|
|
227
|
+
} else if (
|
|
228
|
+
repeatableComponentPaths.includes(componentName) ||
|
|
229
|
+
dynamicZonePaths.includes(componentName) ||
|
|
230
|
+
componentPaths.includes(componentName)
|
|
231
|
+
) {
|
|
232
|
+
/**
|
|
233
|
+
* if the componentName is a repeatable field or dynamic zone we collect the list of paths e.g.
|
|
234
|
+
* ["repeatable_single_component_relation","categories"] and then reduce this
|
|
235
|
+
* recursively
|
|
236
|
+
*/
|
|
237
|
+
const findleaf = findLeafByPathAndReplace(currentPaths.slice(-1)[0], []);
|
|
238
|
+
currentPaths.reduce(findleaf, acc);
|
|
239
|
+
} else {
|
|
240
|
+
set(acc, currentPaths, []);
|
|
241
|
+
}
|
|
208
242
|
|
|
209
243
|
return acc;
|
|
210
|
-
},
|
|
211
|
-
};
|
|
244
|
+
}, data);
|
|
212
245
|
|
|
213
|
-
draftState.
|
|
214
|
-
|
|
246
|
+
draftState.initialData = mergeDataWithPreparedRelations;
|
|
247
|
+
draftState.modifiedData = mergeDataWithPreparedRelations;
|
|
215
248
|
|
|
216
|
-
|
|
217
|
-
* The client sends the following to the content API:
|
|
218
|
-
*
|
|
219
|
-
* {
|
|
220
|
-
* connect: [<Relation>],
|
|
221
|
-
* disconnect: [<Relation>]
|
|
222
|
-
* }
|
|
223
|
-
*
|
|
224
|
-
* but receives only { count: <int> } in return. After save/ publish
|
|
225
|
-
* we have to:
|
|
226
|
-
*
|
|
227
|
-
* 1) reset the connect/ disconnect arrays
|
|
228
|
-
* 2) extend the existing state with the API response, so that `count`
|
|
229
|
-
* stays in sync
|
|
230
|
-
*/
|
|
231
|
-
|
|
232
|
-
...relationalFields.reduce((acc, name) => {
|
|
233
|
-
const { connect, disconnect, ...currentState } = state.modifiedData?.[name] ?? {};
|
|
234
|
-
|
|
235
|
-
acc[name] = {
|
|
236
|
-
...(currentState ?? {}),
|
|
237
|
-
...(initialValues?.[name] ?? {}),
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
return acc;
|
|
241
|
-
}, {}),
|
|
242
|
-
};
|
|
249
|
+
draftState.formErrors = {};
|
|
243
250
|
|
|
244
251
|
draftState.modifiedDZName = null;
|
|
245
252
|
draftState.shouldCheckErrors = false;
|
|
@@ -4,14 +4,31 @@ import isObject from 'lodash/isObject';
|
|
|
4
4
|
|
|
5
5
|
/* eslint-disable indent */
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param {{ browserState: object, serverState: object }} browserState – the modifiedData from REDUX, serverState - the initialData from REDUX
|
|
10
|
+
* @param {object} currentSchema
|
|
11
|
+
* @param {object} componentsSchema
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
14
|
+
const cleanData = ({ browserState, serverState }, currentSchema, componentsSchema) => {
|
|
8
15
|
const getType = (schema, attrName) => get(schema, ['attributes', attrName, 'type'], '');
|
|
9
16
|
const getOtherInfos = (schema, arr) => get(schema, ['attributes', ...arr], '');
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {object} browserState – the modifiedData from REDUX
|
|
21
|
+
* @param {object} serverState – the initialData from REDUX
|
|
22
|
+
* @param {*} schema
|
|
23
|
+
* @returns
|
|
24
|
+
*/
|
|
25
|
+
const recursiveCleanData = (browserState, serverState, schema) => {
|
|
26
|
+
return Object.keys(browserState).reduce((acc, current) => {
|
|
13
27
|
const attrType = getType(schema, current);
|
|
14
|
-
|
|
28
|
+
|
|
29
|
+
// This is the field value
|
|
30
|
+
const value = get(browserState, current);
|
|
31
|
+
const oldValue = get(serverState, current);
|
|
15
32
|
const component = getOtherInfos(schema, [current, 'component']);
|
|
16
33
|
const isRepeatable = getOtherInfos(schema, [current, 'repeatable']);
|
|
17
34
|
let cleanedData;
|
|
@@ -40,34 +57,71 @@ const cleanData = (retrievedData, currentSchema, componentsSchema) => {
|
|
|
40
57
|
case 'component':
|
|
41
58
|
if (isRepeatable) {
|
|
42
59
|
cleanedData = value
|
|
43
|
-
? value.map((data) => {
|
|
44
|
-
const subCleanedData = recursiveCleanData(
|
|
60
|
+
? value.map((data, index) => {
|
|
61
|
+
const subCleanedData = recursiveCleanData(
|
|
62
|
+
data,
|
|
63
|
+
(oldValue ?? [])[index],
|
|
64
|
+
componentsSchema[component]
|
|
65
|
+
);
|
|
45
66
|
|
|
46
67
|
return subCleanedData;
|
|
47
68
|
})
|
|
48
69
|
: value;
|
|
49
70
|
} else {
|
|
50
|
-
cleanedData = value
|
|
71
|
+
cleanedData = value
|
|
72
|
+
? recursiveCleanData(value, oldValue, componentsSchema[component])
|
|
73
|
+
: value;
|
|
51
74
|
}
|
|
52
75
|
|
|
53
76
|
break;
|
|
54
77
|
|
|
55
|
-
case 'relation':
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
78
|
+
case 'relation': {
|
|
79
|
+
/**
|
|
80
|
+
* Because of how repeatable components work when you dig into them the server
|
|
81
|
+
* will have no object to compare too therefore no relation array will be setup
|
|
82
|
+
* because the component has not been initialised, therefore we can safely assume
|
|
83
|
+
* it needs to be added and provide a default empty array.
|
|
84
|
+
*/
|
|
85
|
+
let actualOldValue = oldValue ?? [];
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Instead of the full relation object, we only want to send its ID
|
|
89
|
+
* connectedRelations are the items that are in the browserState
|
|
90
|
+
* array but not in the serverState
|
|
91
|
+
*/
|
|
92
|
+
const connectedRelations = value.reduce((acc, relation) => {
|
|
93
|
+
if (!actualOldValue.find((oldRelation) => oldRelation.id === relation.id)) {
|
|
94
|
+
return [...acc, { id: relation.id }];
|
|
61
95
|
}
|
|
62
96
|
|
|
63
97
|
return acc;
|
|
64
|
-
},
|
|
98
|
+
}, []);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* disconnectedRelations are the items that are in the serverState but
|
|
102
|
+
* are no longer in the browserState
|
|
103
|
+
*/
|
|
104
|
+
const disconnectedRelations = actualOldValue.reduce((acc, relation) => {
|
|
105
|
+
if (!value.find((newRelation) => newRelation.id === relation.id)) {
|
|
106
|
+
return [...acc, { id: relation.id }];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return acc;
|
|
110
|
+
}, []);
|
|
111
|
+
|
|
112
|
+
cleanedData = {
|
|
113
|
+
disconnect: disconnectedRelations,
|
|
114
|
+
connect: connectedRelations,
|
|
115
|
+
};
|
|
116
|
+
|
|
65
117
|
break;
|
|
118
|
+
}
|
|
66
119
|
|
|
67
120
|
case 'dynamiczone':
|
|
68
|
-
cleanedData = value.map((componentData) => {
|
|
121
|
+
cleanedData = value.map((componentData, index) => {
|
|
69
122
|
const subCleanedData = recursiveCleanData(
|
|
70
123
|
componentData,
|
|
124
|
+
(oldValue ?? [])[index],
|
|
71
125
|
componentsSchema[componentData.__component]
|
|
72
126
|
);
|
|
73
127
|
|
|
@@ -84,7 +138,7 @@ const cleanData = (retrievedData, currentSchema, componentsSchema) => {
|
|
|
84
138
|
}, {});
|
|
85
139
|
};
|
|
86
140
|
|
|
87
|
-
return recursiveCleanData(
|
|
141
|
+
return recursiveCleanData(browserState, serverState, currentSchema);
|
|
88
142
|
};
|
|
89
143
|
|
|
90
144
|
// TODO: check which parts are still needed: I suspect the
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import set from 'lodash/set';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {string} endpath – the final path that you're looking to replace
|
|
5
|
+
* @returns {function} findLeafReducer – a function that will be used in the reduce
|
|
6
|
+
*/
|
|
7
|
+
export const findLeafByPathAndReplace = (endpath, replaceWith) => {
|
|
8
|
+
/**
|
|
9
|
+
* @param {object} acc – the data tree
|
|
10
|
+
* @param {*} curr – string, this _could_ be used to index the accumulator
|
|
11
|
+
* @param {*} ind - your current index of the array you're reducing
|
|
12
|
+
* @returns {object} – the new object with the replaced values
|
|
13
|
+
*/
|
|
14
|
+
const findLeafAndReplace = (acc, curr, ind, currentArr) => {
|
|
15
|
+
/**
|
|
16
|
+
* Because we're returning the `accumulator[current]` at the bottom
|
|
17
|
+
* and some components may not exist at this point, we check if `accumulator`
|
|
18
|
+
* exists before trying to access & replace properties.
|
|
19
|
+
*/
|
|
20
|
+
if (!acc) return acc;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* If this is the last item in the array of paths
|
|
24
|
+
* and the current path is not undefined in the accumulator
|
|
25
|
+
* then we assume it's a leaf and we can replace it.
|
|
26
|
+
*/
|
|
27
|
+
if (endpath === curr && acc[curr] !== undefined) {
|
|
28
|
+
set(acc, curr, replaceWith);
|
|
29
|
+
|
|
30
|
+
return acc;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* If the value of the accumulator[current] is an array
|
|
35
|
+
* then we need to loop over it and call the reducer again
|
|
36
|
+
* this time with each array item being the accumulator.
|
|
37
|
+
*/
|
|
38
|
+
if (Array.isArray(acc[curr])) {
|
|
39
|
+
acc[curr].forEach((item) => {
|
|
40
|
+
currentArr.slice(ind + 1).reduce(findLeafAndReplace, item);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* accumulator[current]return accumulator[current] instead of the main accumulator,
|
|
46
|
+
* this will stop the same keys overwrite the wrong objects
|
|
47
|
+
*/
|
|
48
|
+
return acc[curr];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return findLeafAndReplace;
|
|
52
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { default as moveFields } from './moveFields';
|
|
2
2
|
export { default as cleanData } from './cleanData';
|
|
3
3
|
export { default as createYupSchema } from './schema';
|
|
4
|
+
export { recursivelyFindPathsBasedOnCondition } from './recursivelyFindPathsBasedOnCondition';
|
|
5
|
+
export { findLeafByPathAndReplace } from './findLeafByPathAndReplace';
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef Attribute
|
|
3
|
+
* @type { { type: string }}
|
|
4
|
+
*
|
|
5
|
+
* @typedef Attributes
|
|
6
|
+
* @type {{ [key: string]: Attribute }}
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This function will recursively find all the paths in the `currentContentTypeLayout.attributes`
|
|
11
|
+
* based on the boolean return of the condition e.g. `type === 'relation'`.
|
|
12
|
+
*
|
|
13
|
+
* It's original use was for the preperation of action items for the INIT_FORM action. It requires
|
|
14
|
+
* knowledge of the `components` in the entity, however `components` doesn't change nor does the predicate
|
|
15
|
+
* function so we don't need to pass it everytime hence why it's curried.
|
|
16
|
+
*
|
|
17
|
+
*
|
|
18
|
+
* @param {{[key: string]: { attributes: Attributes }}} components
|
|
19
|
+
* @param {(value: Attribute) => boolean} predicate
|
|
20
|
+
* @returns {(attributes: Attributes) => string[]}
|
|
21
|
+
*/
|
|
22
|
+
const recursivelyFindPathsBasedOnConditionSetup = (components, predicate = () => false) => {
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
* @param {Attributes} attributes
|
|
26
|
+
* @returns {string[]}
|
|
27
|
+
*/
|
|
28
|
+
const recursivelyFindPathsBasedOnCondition = (attributes) => {
|
|
29
|
+
return Object.entries(attributes).reduce((acc, [key, value]) => {
|
|
30
|
+
if (predicate(value)) {
|
|
31
|
+
acc = [...acc, key];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (value.type === 'component') {
|
|
35
|
+
const componentAttributes = components[value.component].attributes;
|
|
36
|
+
|
|
37
|
+
const attributesInComponent = recursivelyFindPathsBasedOnCondition(componentAttributes);
|
|
38
|
+
|
|
39
|
+
const attributesInComponentPaths = attributesInComponent.map((path) => `${key}.${path}`);
|
|
40
|
+
|
|
41
|
+
acc = [...acc, attributesInComponentPaths];
|
|
42
|
+
} else if (value.type === 'dynamiczone') {
|
|
43
|
+
const dynamicComponents = value.components;
|
|
44
|
+
|
|
45
|
+
const attributesInDynamicComponents = dynamicComponents
|
|
46
|
+
.flatMap((componentName) => {
|
|
47
|
+
return recursivelyFindPathsBasedOnCondition({
|
|
48
|
+
[componentName]: { type: 'component', component: componentName },
|
|
49
|
+
/**
|
|
50
|
+
* DynamicZones are an array of components, therefore the componentName shouldn't
|
|
51
|
+
* be part of the path because it's not a property of the component.
|
|
52
|
+
*
|
|
53
|
+
* e.g. { dynamic_zone: [{ __component: 'basic.simple', id: 36, my_name: null, categories: { count: 1, } }] }
|
|
54
|
+
* where the path to `id` is `dynamic_zone.id` and not `dynamic_zone.basic.simple.id`
|
|
55
|
+
*
|
|
56
|
+
* NOTE: we don't need to know the path to the `array` because it's about data shape not about the actual data
|
|
57
|
+
*/
|
|
58
|
+
}).map((path) => path.split(`${componentName}.`)[1]);
|
|
59
|
+
})
|
|
60
|
+
.map((path) => `${key}.${path}`);
|
|
61
|
+
|
|
62
|
+
acc = [...acc, attributesInDynamicComponents];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return acc.flat();
|
|
66
|
+
}, []);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return recursivelyFindPathsBasedOnCondition;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export { recursivelyFindPathsBasedOnConditionSetup as recursivelyFindPathsBasedOnCondition };
|