@strapi/admin 4.6.0-alpha.0 → 4.6.0-beta.0
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/content-manager/components/ComponentInitializer/index.js +1 -7
- package/admin/src/content-manager/components/{RepeatableComponent/DragPreview.js → DragLayer/ComponentDragPreview.js} +25 -12
- package/admin/src/content-manager/components/DragLayer/RelationDragPreview.js +75 -0
- package/admin/src/content-manager/components/DragLayer/index.js +23 -7
- package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +130 -84
- package/admin/src/content-manager/components/DynamicZone/index.js +99 -24
- package/admin/src/content-manager/components/DynamicZone/utils/select.js +9 -5
- package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +76 -14
- package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +23 -23
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +24 -5
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/recursivelyFindPathsBasedOnCondition.js +8 -1
- package/admin/src/content-manager/components/Inputs/index.js +5 -19
- package/admin/src/content-manager/components/NonRepeatableComponent/index.js +4 -0
- package/admin/src/content-manager/components/RelationInput/RelationInput.js +203 -63
- package/admin/src/content-manager/components/RelationInput/components/RelationItem.js +134 -21
- package/admin/src/content-manager/components/RelationInput/components/RelationList.js +1 -2
- package/admin/src/content-manager/components/RelationInput/constants.js +1 -0
- package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +132 -10
- package/admin/src/content-manager/components/RepeatableComponent/components/Accordion.js +77 -0
- package/admin/src/content-manager/components/RepeatableComponent/components/Component.js +262 -0
- package/admin/src/content-manager/components/RepeatableComponent/{DraggedItem → components}/Preview.js +0 -0
- package/admin/src/content-manager/components/RepeatableComponent/index.js +147 -87
- package/admin/src/content-manager/components/RepeatableComponent/utils/getComponentErrorKeys.js +1 -1
- package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +1 -1
- package/admin/src/content-manager/components/Wysiwyg/Editor.js +1 -1
- package/admin/src/content-manager/hooks/index.js +2 -0
- package/admin/src/content-manager/hooks/useDragAndDrop.js +120 -0
- package/admin/src/content-manager/hooks/useKeyboardDragAndDrop.js +98 -0
- package/admin/src/content-manager/hooks/useLazyComponents/index.js +69 -0
- package/admin/src/content-manager/pages/CollectionTypeRecursivePath/components/ErrorFallback.js +13 -0
- package/admin/src/content-manager/pages/CollectionTypeRecursivePath/index.js +2 -1
- package/admin/src/content-manager/pages/EditView/GridRow/index.js +62 -0
- package/admin/src/content-manager/pages/EditView/index.js +74 -154
- package/admin/src/content-manager/pages/EditView/selectors.js +14 -0
- package/admin/src/content-manager/pages/EditView/utils/createAttributesLayout.js +11 -6
- package/admin/src/content-manager/pages/EditView/utils/getCustomFieldUidsFromLayout.js +18 -0
- package/admin/src/content-manager/pages/EditView/utils/index.js +1 -0
- package/admin/src/content-manager/pages/EditViewLayoutManager/index.js +1 -1
- package/admin/src/content-manager/sharedReducers/crudReducer/actions.js +5 -0
- package/admin/src/content-manager/sharedReducers/crudReducer/constants.js +2 -0
- package/admin/src/content-manager/sharedReducers/crudReducer/reducer.js +7 -0
- package/admin/src/content-manager/utils/ItemTypes.js +1 -1
- package/admin/src/content-manager/utils/composeRefs.js +28 -0
- package/admin/src/content-manager/utils/getMaxTempKey.js +1 -1
- package/admin/src/content-manager/utils/index.js +7 -0
- package/admin/src/core/utils/axiosInstance.js +4 -2
- package/admin/src/hooks/index.js +1 -0
- package/admin/src/hooks/useFetchClient/index.js +23 -0
- package/admin/src/pages/HomePage/SocialLinks.js +4 -4
- package/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js +23 -18
- package/admin/src/translations/en.json +6 -0
- package/admin/src/translations/ru.json +789 -489
- package/admin/src/utils/fetchClient.js +45 -0
- package/admin/src/utils/getFetchClient.js +10 -0
- package/build/4306.df40a798.chunk.js +98 -0
- package/build/{4318.daf31770.chunk.js → 4318.80bdf035.chunk.js} +2 -2
- package/build/5057.195a59ff.chunk.js +65 -0
- package/build/{805.a1894307.chunk.js → 805.e991a370.chunk.js} +6 -6
- package/build/{4986.3820d11d.chunk.js → 8176.b19bc128.chunk.js} +32 -32
- package/build/{1233.32d6888d.chunk.js → 8186.55910742.chunk.js} +94 -94
- package/build/{8633.8da5488a.chunk.js → 8633.59223842.chunk.js} +1 -1
- package/build/8881.c693411a.chunk.js +245 -0
- package/build/9161.4a0ab137.chunk.js +2119 -0
- package/build/9279.6290c87a.chunk.js +117 -0
- package/build/9707.a0cc4ad8.chunk.js +70 -0
- package/build/Admin-authenticatedApp.f9e74dc0.chunk.js +80 -0
- package/build/{Admin_homePage.b4db4df8.chunk.js → Admin_homePage.8945f71a.chunk.js} +5 -5
- package/build/{Admin_marketplace.fa51405b.chunk.js → Admin_marketplace.ed754a4a.chunk.js} +1 -1
- package/build/{Admin_pluginsPage.14d2840f.chunk.js → Admin_pluginsPage.3c872de7.chunk.js} +1 -1
- package/build/{Admin_profilePage.6c2c8398.chunk.js → Admin_profilePage.c07bdf08.chunk.js} +1 -1
- package/build/{Admin_settingsPage.5e740514.chunk.js → Admin_settingsPage.50a8765b.chunk.js} +5 -5
- package/build/admin-app.2861b6d2.chunk.js +112 -0
- package/build/{admin-edit-roles-page.c7c338b3.chunk.js → admin-edit-roles-page.f407538c.chunk.js} +1 -1
- package/build/{admin-edit-users.d254c128.chunk.js → admin-edit-users.85231e4c.chunk.js} +1 -1
- package/build/{admin-users.7c423e41.chunk.js → admin-users.a2707644.chunk.js} +2 -2
- package/build/api-tokens-create-page.dd4ddfcb.chunk.js +1 -0
- package/build/api-tokens-edit-page.821c5a6c.chunk.js +1 -0
- package/build/{api-tokens-list-page.fe994b6b.chunk.js → api-tokens-list-page.700e575f.chunk.js} +1 -1
- package/build/content-manager.ee948f75.chunk.js +1186 -0
- package/build/content-type-builder-list-view.4412efc3.chunk.js +201 -0
- package/build/content-type-builder.b132b5f4.chunk.js +145 -0
- package/build/email-settings-page.db0d98d1.chunk.js +15 -0
- package/build/{en-json.7dd57947.chunk.js → en-json.4a56dca7.chunk.js} +1 -1
- package/build/index.html +1 -1
- package/build/main.faac89ee.js +2025 -0
- package/build/ru-json.8830286f.chunk.js +1 -0
- package/build/{runtime~main.6e7d95b9.js → runtime~main.75a15b8e.js} +1 -1
- package/build/{sso-settings-page.eb30a02e.chunk.js → sso-settings-page.adb12ac3.chunk.js} +1 -1
- package/build/upload-settings.450cab1a.chunk.js +18 -0
- package/build/upload.e2034370.chunk.js +64 -0
- package/build/users-advanced-settings-page.0c0b8230.chunk.js +13 -0
- package/build/users-email-settings-page.3126ff8c.chunk.js +28 -0
- package/build/users-providers-settings-page.b7b602e2.chunk.js +33 -0
- package/build/users-roles-settings-page.ce5b582d.chunk.js +30 -0
- package/build/webhook-edit-page.1215a6b7.chunk.js +75 -0
- package/build/{webhook-list-page.42533b59.chunk.js → webhook-list-page.b87821f2.chunk.js} +1 -1
- package/ee/server/services/passport/provider-registry.js +1 -1
- package/package.json +15 -15
- package/utils/get-plugins-path.js +17 -3
- package/admin/src/content-manager/components/RepeatableComponent/AccordionGroupCustom/index.js +0 -122
- package/admin/src/content-manager/components/RepeatableComponent/AddFieldButton.js +0 -58
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DraggingSibling.js +0 -72
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/IconButtonCustoms.js +0 -32
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js +0 -322
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/connect.js +0 -11
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/index.js +0 -2
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/select.js +0 -30
- package/admin/src/content-manager/components/RepeatableComponent/utils/connect.js +0 -11
- package/admin/src/content-manager/components/RepeatableComponent/utils/select.js +0 -12
- package/admin/src/content-manager/hooks/__test__/usePrev.test.js +0 -26
- package/build/2438.afe24949.chunk.js +0 -2525
- package/build/2517.5cc235ba.chunk.js +0 -117
- package/build/4306.53359960.chunk.js +0 -98
- package/build/8881.bfdb6877.chunk.js +0 -245
- package/build/9707.932a3c12.chunk.js +0 -70
- package/build/Admin-authenticatedApp.cfc3b4c9.chunk.js +0 -80
- package/build/admin-app.ee1211cb.chunk.js +0 -112
- package/build/api-tokens-create-page.4ca2778d.chunk.js +0 -1
- package/build/api-tokens-edit-page.70a791c2.chunk.js +0 -1
- package/build/content-manager.794d3373.chunk.js +0 -1200
- package/build/content-type-builder-list-view.95012cf0.chunk.js +0 -201
- package/build/content-type-builder.95b9d6a2.chunk.js +0 -145
- package/build/email-settings-page.4bb3606f.chunk.js +0 -15
- package/build/main.a6470578.js +0 -2031
- package/build/ru-json.d7cfc2ff.chunk.js +0 -1
- package/build/upload-settings.3010911f.chunk.js +0 -18
- package/build/upload.9f19f2e8.chunk.js +0 -64
- package/build/users-advanced-settings-page.9df41d67.chunk.js +0 -13
- package/build/users-email-settings-page.56d82eaf.chunk.js +0 -28
- package/build/users-providers-settings-page.96bb7da0.chunk.js +0 -33
- package/build/users-roles-settings-page.445e5e16.chunk.js +0 -30
- package/build/webhook-edit-page.c5efc08b.chunk.js +0 -75
|
@@ -57,13 +57,7 @@ const ComponentInitializer = ({ error, isReadOnly, onClick }) => {
|
|
|
57
57
|
</Box>
|
|
58
58
|
{error?.id && (
|
|
59
59
|
<Typography textColor="danger600" variant="pi">
|
|
60
|
-
{formatMessage(
|
|
61
|
-
{
|
|
62
|
-
id: error.id,
|
|
63
|
-
defaultMessage: error.id,
|
|
64
|
-
},
|
|
65
|
-
{ ...error.values }
|
|
66
|
-
)}
|
|
60
|
+
{formatMessage(error, { ...error.values })}
|
|
67
61
|
</Typography>
|
|
68
62
|
)}
|
|
69
63
|
</>
|
|
@@ -2,13 +2,9 @@ import React from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
4
|
import { pxToRem } from '@strapi/helper-plugin';
|
|
5
|
-
import { Box } from '@strapi/design-system
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { IconButton } from '@strapi/design-system/IconButton';
|
|
9
|
-
import Trash from '@strapi/icons/Trash';
|
|
10
|
-
import DragHandle from '@strapi/icons/Drag';
|
|
11
|
-
import CarretDown from '@strapi/icons/CarretDown';
|
|
5
|
+
import { Box, Flex, Typography, IconButton } from '@strapi/design-system';
|
|
6
|
+
import { Trash, Drag, CarretDown } from '@strapi/icons';
|
|
7
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
12
8
|
|
|
13
9
|
const DragPreviewBox = styled(Box)`
|
|
14
10
|
border: 1px solid ${({ theme }) => theme.colors.neutral200};
|
|
@@ -31,6 +27,13 @@ const DropdownIconWrapper = styled(Box)`
|
|
|
31
27
|
}
|
|
32
28
|
`;
|
|
33
29
|
|
|
30
|
+
const Icon = styled(FontAwesomeIcon)`
|
|
31
|
+
width: ${pxToRem(12)};
|
|
32
|
+
height: ${pxToRem(12)};
|
|
33
|
+
|
|
34
|
+
color: ${({ theme }) => theme.colors.neutral600};
|
|
35
|
+
`;
|
|
36
|
+
|
|
34
37
|
const ToggleButton = styled.button`
|
|
35
38
|
border: none;
|
|
36
39
|
background: transparent;
|
|
@@ -40,7 +43,7 @@ const ToggleButton = styled.button`
|
|
|
40
43
|
padding: 0;
|
|
41
44
|
`;
|
|
42
45
|
|
|
43
|
-
const DragPreview = ({ displayedValue }) => {
|
|
46
|
+
const DragPreview = ({ displayedValue, icon }) => {
|
|
44
47
|
return (
|
|
45
48
|
<DragPreviewBox
|
|
46
49
|
paddingLeft={3}
|
|
@@ -57,18 +60,23 @@ const DragPreview = ({ displayedValue }) => {
|
|
|
57
60
|
<DropdownIconWrapper background="neutral200">
|
|
58
61
|
<CarretDown />
|
|
59
62
|
</DropdownIconWrapper>
|
|
60
|
-
<
|
|
63
|
+
<Flex gap={2} paddingLeft={icon ? 3 : 6} maxWidth={pxToRem(150)}>
|
|
64
|
+
{icon ? <Icon icon={icon} /> : null}
|
|
61
65
|
<Typography textColor="neutral700" ellipsis>
|
|
62
66
|
{displayedValue}
|
|
63
67
|
</Typography>
|
|
64
|
-
</
|
|
68
|
+
</Flex>
|
|
65
69
|
</Flex>
|
|
66
70
|
</ToggleButton>
|
|
67
71
|
<Box paddingLeft={3}>
|
|
68
72
|
<Flex>
|
|
69
|
-
<IconButton
|
|
73
|
+
<IconButton noBorder>
|
|
74
|
+
<Trash />
|
|
75
|
+
</IconButton>
|
|
70
76
|
<Box paddingLeft={2}>
|
|
71
|
-
<IconButton
|
|
77
|
+
<IconButton noBorder>
|
|
78
|
+
<Drag />
|
|
79
|
+
</IconButton>
|
|
72
80
|
</Box>
|
|
73
81
|
</Flex>
|
|
74
82
|
</Box>
|
|
@@ -77,8 +85,13 @@ const DragPreview = ({ displayedValue }) => {
|
|
|
77
85
|
);
|
|
78
86
|
};
|
|
79
87
|
|
|
88
|
+
DragPreview.defaultProps = {
|
|
89
|
+
icon: undefined,
|
|
90
|
+
};
|
|
91
|
+
|
|
80
92
|
DragPreview.propTypes = {
|
|
81
93
|
displayedValue: PropTypes.string.isRequired,
|
|
94
|
+
icon: PropTypes.string,
|
|
82
95
|
};
|
|
83
96
|
|
|
84
97
|
export default DragPreview;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { Box, Flex, IconButton, Typography, Status, Icon } from '@strapi/design-system';
|
|
5
|
+
import { Drag, Cross } from '@strapi/icons';
|
|
6
|
+
|
|
7
|
+
import { getTrad } from '../../utils';
|
|
8
|
+
import { PUBLICATION_STATES } from '../RelationInputDataManager/constants';
|
|
9
|
+
import { ChildrenWrapper, StackWrapper } from '../RelationInput/components/RelationItem';
|
|
10
|
+
import { BoxEllipsis, DisconnectButton } from '../RelationInput/RelationInput';
|
|
11
|
+
|
|
12
|
+
export const RelationDragPreview = ({ status, displayedValue, width }) => {
|
|
13
|
+
const { formatMessage } = useIntl();
|
|
14
|
+
|
|
15
|
+
const stateMessage = {
|
|
16
|
+
[PUBLICATION_STATES.DRAFT]: formatMessage({
|
|
17
|
+
id: getTrad('relation.publicationState.draft'),
|
|
18
|
+
defaultMessage: 'Draft',
|
|
19
|
+
}),
|
|
20
|
+
|
|
21
|
+
[PUBLICATION_STATES.PUBLISHED]: formatMessage({
|
|
22
|
+
id: getTrad('relation.publicationState.published'),
|
|
23
|
+
defaultMessage: 'Published',
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const statusColor = status === PUBLICATION_STATES.DRAFT ? 'secondary' : 'success';
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Box style={{ width }}>
|
|
31
|
+
<Flex
|
|
32
|
+
paddingTop={2}
|
|
33
|
+
paddingBottom={2}
|
|
34
|
+
paddingLeft={2}
|
|
35
|
+
paddingRight={4}
|
|
36
|
+
hasRadius
|
|
37
|
+
borderSize={1}
|
|
38
|
+
background="neutral0"
|
|
39
|
+
borderColor="neutral200"
|
|
40
|
+
justifyContent="space-between"
|
|
41
|
+
>
|
|
42
|
+
<StackWrapper spacing={1} horizontal>
|
|
43
|
+
<IconButton noBorder>
|
|
44
|
+
<Drag />
|
|
45
|
+
</IconButton>
|
|
46
|
+
<ChildrenWrapper justifyContent="space-between">
|
|
47
|
+
<BoxEllipsis minWidth={0} paddingTop={1} paddingBottom={1} paddingRight={4}>
|
|
48
|
+
<Typography textColor="primary600" ellipsis>
|
|
49
|
+
{displayedValue}
|
|
50
|
+
</Typography>
|
|
51
|
+
</BoxEllipsis>
|
|
52
|
+
{status && (
|
|
53
|
+
<Status variant={statusColor} showBullet={false} size="S">
|
|
54
|
+
<Typography fontWeight="bold" textColor={`${statusColor}700`}>
|
|
55
|
+
{stateMessage[status]}
|
|
56
|
+
</Typography>
|
|
57
|
+
</Status>
|
|
58
|
+
)}
|
|
59
|
+
</ChildrenWrapper>
|
|
60
|
+
</StackWrapper>
|
|
61
|
+
<Box paddingLeft={4}>
|
|
62
|
+
<DisconnectButton type="button">
|
|
63
|
+
<Icon width="12px" as={Cross} />
|
|
64
|
+
</DisconnectButton>
|
|
65
|
+
</Box>
|
|
66
|
+
</Flex>
|
|
67
|
+
</Box>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
RelationDragPreview.propTypes = {
|
|
72
|
+
status: PropTypes.string.isRequired,
|
|
73
|
+
displayedValue: PropTypes.string.isRequired,
|
|
74
|
+
width: PropTypes.number.isRequired,
|
|
75
|
+
};
|
|
@@ -3,11 +3,10 @@ import { useDragLayer } from 'react-dnd';
|
|
|
3
3
|
import LayoutDndProvider from '../LayoutDndProvider';
|
|
4
4
|
|
|
5
5
|
import ItemTypes from '../../utils/ItemTypes';
|
|
6
|
-
import RepeatableComponentPreview from '../RepeatableComponent/DragPreview';
|
|
7
6
|
import CardPreview from '../../pages/ListSettingsView/components/CardPreview';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
|
|
8
|
+
import ComponentPreview from './ComponentDragPreview';
|
|
9
|
+
import { RelationDragPreview } from './RelationDragPreview';
|
|
11
10
|
|
|
12
11
|
const layerStyles = {
|
|
13
12
|
position: 'fixed',
|
|
@@ -50,15 +49,32 @@ const CustomDragLayer = () => {
|
|
|
50
49
|
return null;
|
|
51
50
|
}
|
|
52
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Because a user may have multiple relations / dynamic zones / repeable fields in the same content type,
|
|
54
|
+
* we append the fieldName for the item type to make them unique, however, we then want to extract that
|
|
55
|
+
* first type to apply the correct preview.
|
|
56
|
+
*/
|
|
57
|
+
const [actualType] = itemType.split('_');
|
|
58
|
+
|
|
53
59
|
return (
|
|
54
60
|
<LayoutDndProvider>
|
|
55
61
|
<div style={layerStyles}>
|
|
56
62
|
<div style={getItemStyles(initialOffset, currentOffset, mouseOffset)} className="col-md-2">
|
|
57
|
-
{[ItemTypes.
|
|
63
|
+
{[ItemTypes.EDIT_FIELD, ItemTypes.FIELD].includes(itemType) && (
|
|
58
64
|
<CardPreview labelField={item.labelField} />
|
|
59
65
|
)}
|
|
60
|
-
{
|
|
61
|
-
<
|
|
66
|
+
{actualType === ItemTypes.COMPONENT && (
|
|
67
|
+
<ComponentPreview displayedValue={item.displayedValue} />
|
|
68
|
+
)}
|
|
69
|
+
{actualType === ItemTypes.DYNAMIC_ZONE && (
|
|
70
|
+
<ComponentPreview icon={item.icon} displayedValue={item.displayedValue} />
|
|
71
|
+
)}
|
|
72
|
+
{actualType === ItemTypes.RELATION && (
|
|
73
|
+
<RelationDragPreview
|
|
74
|
+
displayedValue={item.displayedValue}
|
|
75
|
+
status={item.status}
|
|
76
|
+
width={item.width}
|
|
77
|
+
/>
|
|
62
78
|
)}
|
|
63
79
|
</div>
|
|
64
80
|
</div>
|
|
@@ -1,27 +1,39 @@
|
|
|
1
|
-
import React, { useMemo, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
4
|
import { useIntl } from 'react-intl';
|
|
5
5
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
6
6
|
import get from 'lodash/get';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
import { getEmptyImage } from 'react-dnd-html5-backend';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
Accordion,
|
|
11
|
+
AccordionToggle,
|
|
12
|
+
AccordionContent,
|
|
13
|
+
IconButton,
|
|
14
|
+
Box,
|
|
15
|
+
Flex,
|
|
16
|
+
Stack,
|
|
17
|
+
} from '@strapi/design-system';
|
|
14
18
|
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
|
19
|
+
import { Trash, Drag } from '@strapi/icons';
|
|
15
20
|
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import ArrowUp from '@strapi/icons/ArrowUp';
|
|
19
|
-
|
|
20
|
-
import { useContentTypeLayout } from '../../../hooks';
|
|
21
|
-
import { getTrad } from '../../../utils';
|
|
21
|
+
import { useContentTypeLayout, useDragAndDrop } from '../../../hooks';
|
|
22
|
+
import { composeRefs, getTrad, ItemTypes } from '../../../utils';
|
|
22
23
|
|
|
23
24
|
import FieldComponent from '../../FieldComponent';
|
|
24
25
|
|
|
26
|
+
const ActionsStack = styled(Stack)`
|
|
27
|
+
/*
|
|
28
|
+
we need to remove the background from the button but we can't
|
|
29
|
+
wrap the element in styled because it breaks the forwardedAs which
|
|
30
|
+
we need for drag handler to work on firefox
|
|
31
|
+
*/
|
|
32
|
+
div[role='button'] {
|
|
33
|
+
background: transparent;
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
25
37
|
const IconButtonCustom = styled(IconButton)`
|
|
26
38
|
background-color: transparent;
|
|
27
39
|
|
|
@@ -46,17 +58,31 @@ const Rectangle = styled(Box)`
|
|
|
46
58
|
height: ${({ theme }) => theme.spaces[4]};
|
|
47
59
|
`;
|
|
48
60
|
|
|
61
|
+
const Preview = styled.span`
|
|
62
|
+
display: block;
|
|
63
|
+
background-color: ${({ theme }) => theme.colors.primary100};
|
|
64
|
+
outline: 1px dashed ${({ theme }) => theme.colors.primary500};
|
|
65
|
+
outline-offset: -1px;
|
|
66
|
+
padding: ${({ theme }) => theme.spaces[6]};
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const ComponentContainer = styled(Box)`
|
|
70
|
+
list-style: none;
|
|
71
|
+
padding: 0;
|
|
72
|
+
margin: 0;
|
|
73
|
+
`;
|
|
74
|
+
|
|
49
75
|
const DynamicZoneComponent = ({
|
|
50
76
|
componentUid,
|
|
51
77
|
formErrors,
|
|
52
78
|
index,
|
|
53
79
|
isFieldAllowed,
|
|
54
|
-
onMoveComponentDownClick,
|
|
55
|
-
onMoveComponentUpClick,
|
|
56
80
|
name,
|
|
57
81
|
onRemoveComponentClick,
|
|
58
|
-
|
|
59
|
-
|
|
82
|
+
onMoveComponent,
|
|
83
|
+
onGrabItem,
|
|
84
|
+
onDropItem,
|
|
85
|
+
onCancel,
|
|
60
86
|
}) => {
|
|
61
87
|
const [isOpen, setIsOpen] = useState(true);
|
|
62
88
|
const { formatMessage } = useIntl();
|
|
@@ -103,71 +129,90 @@ const DynamicZoneComponent = ({
|
|
|
103
129
|
setIsOpen((s) => !s);
|
|
104
130
|
};
|
|
105
131
|
|
|
132
|
+
const [{ handlerId, isDragging, handleKeyDown }, boxRef, dropRef, dragRef, dragPreviewRef] =
|
|
133
|
+
useDragAndDrop(isFieldAllowed, {
|
|
134
|
+
type: `${ItemTypes.DYNAMIC_ZONE}_${name}`,
|
|
135
|
+
index,
|
|
136
|
+
item: {
|
|
137
|
+
displayedValue: `${friendlyName}${mainValue}`,
|
|
138
|
+
icon,
|
|
139
|
+
},
|
|
140
|
+
onMoveItem: onMoveComponent,
|
|
141
|
+
onGrabItem,
|
|
142
|
+
onDropItem,
|
|
143
|
+
onCancel,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
dragPreviewRef(getEmptyImage(), { captureDraggingState: false });
|
|
148
|
+
}, [dragPreviewRef, index]);
|
|
149
|
+
|
|
150
|
+
const composedBoxRefs = composeRefs(boxRef, dropRef);
|
|
151
|
+
|
|
152
|
+
const accordionActions = !isFieldAllowed ? null : (
|
|
153
|
+
<ActionsStack horizontal spacing={0} expanded={isOpen}>
|
|
154
|
+
<IconButtonCustom
|
|
155
|
+
noBorder
|
|
156
|
+
label={formatMessage(
|
|
157
|
+
{
|
|
158
|
+
id: getTrad('components.DynamicZone.delete-label'),
|
|
159
|
+
defaultMessage: 'Delete {name}',
|
|
160
|
+
},
|
|
161
|
+
{ name: friendlyName }
|
|
162
|
+
)}
|
|
163
|
+
onClick={onRemoveComponentClick}
|
|
164
|
+
>
|
|
165
|
+
<Trash />
|
|
166
|
+
</IconButtonCustom>
|
|
167
|
+
<IconButton
|
|
168
|
+
forwardedAs="div"
|
|
169
|
+
role="button"
|
|
170
|
+
noBorder
|
|
171
|
+
tabIndex={0}
|
|
172
|
+
onClick={(e) => e.stopPropagation()}
|
|
173
|
+
data-handler-id={handlerId}
|
|
174
|
+
ref={dragRef}
|
|
175
|
+
label={formatMessage({
|
|
176
|
+
id: getTrad('components.DragHandle-label'),
|
|
177
|
+
defaultMessage: 'Drag',
|
|
178
|
+
})}
|
|
179
|
+
onKeyDown={handleKeyDown}
|
|
180
|
+
>
|
|
181
|
+
<Drag />
|
|
182
|
+
</IconButton>
|
|
183
|
+
</ActionsStack>
|
|
184
|
+
);
|
|
185
|
+
|
|
106
186
|
return (
|
|
107
|
-
<
|
|
187
|
+
<ComponentContainer as="li">
|
|
108
188
|
<Flex justifyContent="center">
|
|
109
189
|
<Rectangle background="neutral200" />
|
|
110
190
|
</Flex>
|
|
111
|
-
<StyledBox hasRadius>
|
|
112
|
-
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
})}
|
|
135
|
-
onClick={onMoveComponentUpClick}
|
|
136
|
-
icon={<ArrowUp />}
|
|
137
|
-
/>
|
|
138
|
-
)}
|
|
139
|
-
{isFieldAllowed && (
|
|
140
|
-
<IconButtonCustom
|
|
141
|
-
noBorder
|
|
142
|
-
label={formatMessage(
|
|
143
|
-
{
|
|
144
|
-
id: getTrad('components.DynamicZone.delete-label'),
|
|
145
|
-
defaultMessage: 'Delete {name}',
|
|
146
|
-
},
|
|
147
|
-
{ name: friendlyName }
|
|
148
|
-
)}
|
|
149
|
-
onClick={onRemoveComponentClick}
|
|
150
|
-
icon={<Trash />}
|
|
151
|
-
/>
|
|
152
|
-
)}
|
|
153
|
-
</Stack>
|
|
154
|
-
}
|
|
155
|
-
title={`${friendlyName}${mainValue}`}
|
|
156
|
-
togglePosition="left"
|
|
157
|
-
/>
|
|
158
|
-
<AccordionContent>
|
|
159
|
-
<AccordionContentRadius background="neutral0">
|
|
160
|
-
<FieldComponent
|
|
161
|
-
componentUid={componentUid}
|
|
162
|
-
icon={icon}
|
|
163
|
-
name={`${name}.${index}`}
|
|
164
|
-
isFromDynamicZone
|
|
165
|
-
/>
|
|
166
|
-
</AccordionContentRadius>
|
|
167
|
-
</AccordionContent>
|
|
168
|
-
</Accordion>
|
|
191
|
+
<StyledBox ref={composedBoxRefs} hasRadius>
|
|
192
|
+
{isDragging ? (
|
|
193
|
+
<Preview padding={6} background="primary100" />
|
|
194
|
+
) : (
|
|
195
|
+
<Accordion expanded={isOpen} onToggle={handleToggle} size="S" error={errorMessage}>
|
|
196
|
+
<AccordionToggle
|
|
197
|
+
startIcon={icon && <FontAwesomeIcon icon={icon} />}
|
|
198
|
+
action={accordionActions}
|
|
199
|
+
title={`${friendlyName}${mainValue}`}
|
|
200
|
+
togglePosition="left"
|
|
201
|
+
/>
|
|
202
|
+
<AccordionContent>
|
|
203
|
+
<AccordionContentRadius background="neutral0">
|
|
204
|
+
<FieldComponent
|
|
205
|
+
componentUid={componentUid}
|
|
206
|
+
icon={icon}
|
|
207
|
+
name={`${name}.${index}`}
|
|
208
|
+
isFromDynamicZone
|
|
209
|
+
/>
|
|
210
|
+
</AccordionContentRadius>
|
|
211
|
+
</AccordionContent>
|
|
212
|
+
</Accordion>
|
|
213
|
+
)}
|
|
169
214
|
</StyledBox>
|
|
170
|
-
</
|
|
215
|
+
</ComponentContainer>
|
|
171
216
|
);
|
|
172
217
|
};
|
|
173
218
|
|
|
@@ -175,8 +220,9 @@ DynamicZoneComponent.defaultProps = {
|
|
|
175
220
|
formErrors: {},
|
|
176
221
|
index: 0,
|
|
177
222
|
isFieldAllowed: true,
|
|
178
|
-
|
|
179
|
-
|
|
223
|
+
onGrabItem: undefined,
|
|
224
|
+
onDropItem: undefined,
|
|
225
|
+
onCancel: undefined,
|
|
180
226
|
};
|
|
181
227
|
|
|
182
228
|
DynamicZoneComponent.propTypes = {
|
|
@@ -185,11 +231,11 @@ DynamicZoneComponent.propTypes = {
|
|
|
185
231
|
index: PropTypes.number,
|
|
186
232
|
isFieldAllowed: PropTypes.bool,
|
|
187
233
|
name: PropTypes.string.isRequired,
|
|
188
|
-
|
|
189
|
-
|
|
234
|
+
onGrabItem: PropTypes.func,
|
|
235
|
+
onDropItem: PropTypes.func,
|
|
236
|
+
onCancel: PropTypes.func,
|
|
237
|
+
onMoveComponent: PropTypes.func.isRequired,
|
|
190
238
|
onRemoveComponentClick: PropTypes.func.isRequired,
|
|
191
|
-
showDownIcon: PropTypes.bool,
|
|
192
|
-
showUpIcon: PropTypes.bool,
|
|
193
239
|
};
|
|
194
240
|
|
|
195
241
|
export default DynamicZoneComponent;
|
|
@@ -2,9 +2,9 @@ import React, { memo, useMemo, useState } from 'react';
|
|
|
2
2
|
import get from 'lodash/get';
|
|
3
3
|
import isEqual from 'react-fast-compare';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
|
-
import { Stack } from '@strapi/design-system
|
|
6
|
-
import { Box } from '@strapi/design-system/Box';
|
|
5
|
+
import { Box, Stack, VisuallyHidden } from '@strapi/design-system';
|
|
7
6
|
import { NotAllowedInput, useNotification } from '@strapi/helper-plugin';
|
|
7
|
+
import { useIntl } from 'react-intl';
|
|
8
8
|
|
|
9
9
|
import { getTrad } from '../../utils';
|
|
10
10
|
|
|
@@ -27,14 +27,16 @@ const DynamicZone = ({
|
|
|
27
27
|
isFieldAllowed,
|
|
28
28
|
isFieldReadable,
|
|
29
29
|
labelAction,
|
|
30
|
-
|
|
31
|
-
moveComponentDown,
|
|
30
|
+
moveComponentField,
|
|
32
31
|
removeComponentFromDynamicZone,
|
|
33
32
|
dynamicDisplayedComponents,
|
|
34
33
|
fieldSchema,
|
|
35
34
|
metadatas,
|
|
36
35
|
}) => {
|
|
37
36
|
const [addComponentIsOpen, setAddComponentIsOpen] = useState(false);
|
|
37
|
+
const [liveText, setLiveText] = useState('');
|
|
38
|
+
|
|
39
|
+
const { formatMessage } = useIntl();
|
|
38
40
|
|
|
39
41
|
const toggleNotification = useNotification();
|
|
40
42
|
const { getComponentLayout, components } = useContentTypeLayout();
|
|
@@ -82,12 +84,76 @@ const DynamicZone = ({
|
|
|
82
84
|
}
|
|
83
85
|
};
|
|
84
86
|
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
+
const handleMoveComponent = (newIndex, currentIndex) => {
|
|
88
|
+
setLiveText(
|
|
89
|
+
formatMessage(
|
|
90
|
+
{
|
|
91
|
+
id: getTrad('dnd.reorder'),
|
|
92
|
+
defaultMessage: '{item}, moved. New position in list: {position}.',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
item: `${name}.${currentIndex}`,
|
|
96
|
+
position: getItemPos(newIndex),
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
moveComponentField({
|
|
102
|
+
name,
|
|
103
|
+
newIndex,
|
|
104
|
+
currentIndex,
|
|
105
|
+
});
|
|
87
106
|
};
|
|
88
107
|
|
|
89
|
-
|
|
90
|
-
|
|
108
|
+
/**
|
|
109
|
+
*
|
|
110
|
+
* @param {number} index
|
|
111
|
+
* @returns {string}
|
|
112
|
+
*/
|
|
113
|
+
const getItemPos = (index) => `${index + 1} of ${dynamicDisplayedComponents.length}`;
|
|
114
|
+
|
|
115
|
+
const handleCancel = (index) => {
|
|
116
|
+
setLiveText(
|
|
117
|
+
formatMessage(
|
|
118
|
+
{
|
|
119
|
+
id: getTrad('dnd.cancel-item'),
|
|
120
|
+
defaultMessage: '{item}, dropped. Re-order cancelled.',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
item: `${name}.${index}`,
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const handleGrabItem = (index) => {
|
|
130
|
+
setLiveText(
|
|
131
|
+
formatMessage(
|
|
132
|
+
{
|
|
133
|
+
id: getTrad('dnd.grab-item'),
|
|
134
|
+
defaultMessage: `{item}, grabbed. Current position in list: {position}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.`,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
item: `${name}.${index}`,
|
|
138
|
+
position: getItemPos(index),
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const handleDropItem = (index) => {
|
|
145
|
+
setLiveText(
|
|
146
|
+
formatMessage(
|
|
147
|
+
{
|
|
148
|
+
id: getTrad('dnd.drop-item'),
|
|
149
|
+
defaultMessage: `{item}, dropped. Final position in list: {position}.`,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
item: `${name}.${index}`,
|
|
153
|
+
position: getItemPos(index),
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
);
|
|
91
157
|
};
|
|
92
158
|
|
|
93
159
|
const handleRemoveComponent = (name, currentIndex) => () => {
|
|
@@ -105,6 +171,8 @@ const DynamicZone = ({
|
|
|
105
171
|
);
|
|
106
172
|
}
|
|
107
173
|
|
|
174
|
+
const ariaDescriptionId = `${name}-item-instructions`;
|
|
175
|
+
|
|
108
176
|
return (
|
|
109
177
|
<Stack spacing={6}>
|
|
110
178
|
{dynamicDisplayedComponentsLength > 0 && (
|
|
@@ -117,27 +185,30 @@ const DynamicZone = ({
|
|
|
117
185
|
numberOfComponents={dynamicDisplayedComponentsLength}
|
|
118
186
|
required={fieldSchema.required || false}
|
|
119
187
|
/>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
188
|
+
<VisuallyHidden id={ariaDescriptionId}>
|
|
189
|
+
{formatMessage({
|
|
190
|
+
id: getTrad('dnd.instructions'),
|
|
191
|
+
defaultMessage: `Press spacebar to grab and re-order`,
|
|
192
|
+
})}
|
|
193
|
+
</VisuallyHidden>
|
|
194
|
+
<VisuallyHidden aria-live="assertive">{liveText}</VisuallyHidden>
|
|
195
|
+
<ol aria-describedby={ariaDescriptionId}>
|
|
196
|
+
{dynamicDisplayedComponents.map(({ componentUid, id }, index) => (
|
|
125
197
|
<DynamicZoneComponent
|
|
126
198
|
componentUid={componentUid}
|
|
127
199
|
formErrors={formErrors}
|
|
128
|
-
|
|
129
|
-
key={index}
|
|
200
|
+
key={`${componentUid}-${id}`}
|
|
130
201
|
index={index}
|
|
131
202
|
isFieldAllowed={isFieldAllowed}
|
|
132
|
-
onMoveComponentDownClick={handleMoveComponentDown(name, index)}
|
|
133
|
-
onMoveComponentUpClick={handleMoveComponentUp(name, index)}
|
|
134
203
|
name={name}
|
|
204
|
+
onMoveComponent={handleMoveComponent}
|
|
135
205
|
onRemoveComponentClick={handleRemoveComponent(name, index)}
|
|
136
|
-
|
|
137
|
-
|
|
206
|
+
onCancel={handleCancel}
|
|
207
|
+
onDropItem={handleDropItem}
|
|
208
|
+
onGrabItem={handleGrabItem}
|
|
138
209
|
/>
|
|
139
|
-
)
|
|
140
|
-
|
|
210
|
+
))}
|
|
211
|
+
</ol>
|
|
141
212
|
</Box>
|
|
142
213
|
)}
|
|
143
214
|
|
|
@@ -172,7 +243,12 @@ DynamicZone.defaultProps = {
|
|
|
172
243
|
|
|
173
244
|
DynamicZone.propTypes = {
|
|
174
245
|
addComponentToDynamicZone: PropTypes.func.isRequired,
|
|
175
|
-
dynamicDisplayedComponents: PropTypes.
|
|
246
|
+
dynamicDisplayedComponents: PropTypes.arrayOf(
|
|
247
|
+
PropTypes.shape({
|
|
248
|
+
componentUid: PropTypes.string.isRequired,
|
|
249
|
+
id: PropTypes.number.isRequired,
|
|
250
|
+
})
|
|
251
|
+
),
|
|
176
252
|
fieldSchema: PropTypes.shape({
|
|
177
253
|
components: PropTypes.array.isRequired,
|
|
178
254
|
max: PropTypes.number,
|
|
@@ -188,8 +264,7 @@ DynamicZone.propTypes = {
|
|
|
188
264
|
description: PropTypes.string,
|
|
189
265
|
label: PropTypes.string,
|
|
190
266
|
}).isRequired,
|
|
191
|
-
|
|
192
|
-
moveComponentDown: PropTypes.func.isRequired,
|
|
267
|
+
moveComponentField: PropTypes.func.isRequired,
|
|
193
268
|
name: PropTypes.string.isRequired,
|
|
194
269
|
removeComponentFromDynamicZone: PropTypes.func.isRequired,
|
|
195
270
|
};
|