diff --git a/examples/getstarted/src/api/kitchensink/content-types/kitchensink/schema.json b/examples/getstarted/src/api/kitchensink/content-types/kitchensink/schema.json index 0915dd34dd..04bf9fd5ea 100644 --- a/examples/getstarted/src/api/kitchensink/content-types/kitchensink/schema.json +++ b/examples/getstarted/src/api/kitchensink/content-types/kitchensink/schema.json @@ -57,19 +57,33 @@ }, "enumeration": { "type": "enumeration", - "enum": ["A", "B", "C", "D", "E"] + "enum": [ + "A", + "B", + "C", + "D", + "E" + ] }, "single_media": { "type": "media", "multiple": false, "required": false, - "allowedTypes": ["images", "files", "videos"] + "allowedTypes": [ + "images", + "files", + "videos" + ] }, "multiple_media": { "type": "media", "multiple": true, "required": false, - "allowedTypes": ["images", "files", "videos"] + "allowedTypes": [ + "images", + "files", + "videos" + ] }, "json": { "type": "json" @@ -86,7 +100,10 @@ }, "dynamiczone": { "type": "dynamiczone", - "components": ["basic.simple"] + "components": [ + "basic.simple", + "blog.test-como" + ] }, "one_way_tag": { "type": "relation", diff --git a/packages/core/admin/admin/src/content-manager/components/DragLayer/RepetableComponentDragPreview.js b/packages/core/admin/admin/src/content-manager/components/DragLayer/ComponentDragPreview.js similarity index 80% rename from packages/core/admin/admin/src/content-manager/components/DragLayer/RepetableComponentDragPreview.js rename to packages/core/admin/admin/src/content-manager/components/DragLayer/ComponentDragPreview.js index 3940a97e6f..0bb51ec7bf 100644 --- a/packages/core/admin/admin/src/content-manager/components/DragLayer/RepetableComponentDragPreview.js +++ b/packages/core/admin/admin/src/content-manager/components/DragLayer/ComponentDragPreview.js @@ -4,6 +4,7 @@ import styled from 'styled-components'; import { pxToRem } from '@strapi/helper-plugin'; import { Box, Flex, Typography, IconButton } from '@strapi/design-system'; import { Trash, Drag, CarretDown } from '@strapi/icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; const DragPreviewBox = styled(Box)` border: 1px solid ${({ theme }) => theme.colors.neutral200}; @@ -26,6 +27,13 @@ const DropdownIconWrapper = styled(Box)` } `; +const Icon = styled(FontAwesomeIcon)` + width: 14px; + height: 14px; + + color: ${({ theme }) => theme.colors.neutral600}; +`; + const ToggleButton = styled.button` border: none; background: transparent; @@ -35,7 +43,7 @@ const ToggleButton = styled.button` padding: 0; `; -const DragPreview = ({ displayedValue }) => { +const DragPreview = ({ displayedValue, icon }) => { return ( { - + + {icon ? : null} {displayedValue} - + @@ -76,8 +85,13 @@ const DragPreview = ({ displayedValue }) => { ); }; +DragPreview.defaultProps = { + icon: undefined, +}; + DragPreview.propTypes = { displayedValue: PropTypes.string.isRequired, + icon: PropTypes.string, }; export default DragPreview; diff --git a/packages/core/admin/admin/src/content-manager/components/DragLayer/index.js b/packages/core/admin/admin/src/content-manager/components/DragLayer/index.js index c0792255b7..cc38b208f7 100644 --- a/packages/core/admin/admin/src/content-manager/components/DragLayer/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DragLayer/index.js @@ -5,7 +5,7 @@ import LayoutDndProvider from '../LayoutDndProvider'; import ItemTypes from '../../utils/ItemTypes'; import CardPreview from '../../pages/ListSettingsView/components/CardPreview'; -import RepeatableComponentPreview from './RepetableComponentDragPreview'; +import ComponentPreview from './ComponentDragPreview'; const layerStyles = { position: 'fixed', @@ -52,11 +52,14 @@ const CustomDragLayer = () => {
- {[ItemTypes.EDIT_RELATION, ItemTypes.EDIT_FIELD, ItemTypes.FIELD].includes(itemType) && ( + {[ItemTypes.EDIT_FIELD, ItemTypes.FIELD].includes(itemType) && ( )} {itemType === ItemTypes.COMPONENT && ( - + + )} + {itemType === ItemTypes.DYNAMIC_ZONE && ( + )}
diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js index 5eea846d45..9370abe0a2 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js @@ -1,24 +1,25 @@ -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import { useIntl } from 'react-intl'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import get from 'lodash/get'; +import { getEmptyImage } from 'react-dnd-html5-backend'; -import { Accordion, AccordionToggle, AccordionContent } from '@strapi/design-system/Accordion'; -import { IconButton } from '@strapi/design-system/IconButton'; -import { Box } from '@strapi/design-system/Box'; -import { Flex } from '@strapi/design-system/Flex'; -import { Stack } from '@strapi/design-system/Stack'; - +import { + Accordion, + AccordionToggle, + AccordionContent, + IconButton, + Box, + Flex, + Stack, +} from '@strapi/design-system'; import { useCMEditViewDataManager } from '@strapi/helper-plugin'; +import { Trash, Drag } from '@strapi/icons'; -import Trash from '@strapi/icons/Trash'; -import ArrowDown from '@strapi/icons/ArrowDown'; -import ArrowUp from '@strapi/icons/ArrowUp'; - -import { useContentTypeLayout } from '../../../hooks'; -import { getTrad } from '../../../utils'; +import { useContentTypeLayout, useDragAndDrop } from '../../../hooks'; +import { composeRefs, getTrad, ItemTypes } from '../../../utils'; import FieldComponent from '../../FieldComponent'; @@ -46,17 +47,22 @@ const Rectangle = styled(Box)` height: ${({ theme }) => theme.spaces[4]}; `; +const Preview = styled.span` + display: block; + background-color: ${({ theme }) => theme.colors.primary100}; + outline: 1px dashed ${({ theme }) => theme.colors.primary500}; + outline-offset: -1px; + padding: ${({ theme }) => theme.spaces[6]}; +`; + const DynamicZoneComponent = ({ componentUid, formErrors, index, isFieldAllowed, - onMoveComponentDownClick, - onMoveComponentUpClick, name, onRemoveComponentClick, - showDownIcon, - showUpIcon, + onMoveComponent, }) => { const [isOpen, setIsOpen] = useState(true); const { formatMessage } = useIntl(); @@ -103,69 +109,85 @@ const DynamicZoneComponent = ({ setIsOpen((s) => !s); }; + const [{ handlerId, isDragging, handleKeyDown }, boxRef, dropRef, dragRef, dragPreviewRef] = + useDragAndDrop(isFieldAllowed, { + type: ItemTypes.DYNAMIC_ZONE, + index, + item: { + displayedValue: `${friendlyName}${mainValue}`, + icon, + }, + onMoveItem: onMoveComponent, + }); + + useEffect(() => { + dragPreviewRef(getEmptyImage(), { captureDraggingState: false }); + }, [dragPreviewRef]); + + const composedBoxRefs = composeRefs(boxRef, dropRef); + return ( - - - } - action={ - - {showDownIcon && ( - } - /> - )} - {showUpIcon && ( - } - /> - )} - {isFieldAllowed && ( - } - /> - )} - - } - title={`${friendlyName}${mainValue}`} - togglePosition="left" - /> - - - - - - + + {isDragging ? ( + + ) : ( + + } + action={ + isFieldAllowed ? ( + + + + + e.stopPropagation()} + data-handler-id={handlerId} + ref={dragRef} + label={formatMessage({ + id: getTrad('components.DragHandle-label'), + defaultMessage: 'Drag', + })} + onKeyDown={handleKeyDown} + > + + + + ) : null + } + title={`${friendlyName}${mainValue}`} + togglePosition="left" + /> + + + + + + + )} ); @@ -175,8 +197,6 @@ DynamicZoneComponent.defaultProps = { formErrors: {}, index: 0, isFieldAllowed: true, - showDownIcon: true, - showUpIcon: true, }; DynamicZoneComponent.propTypes = { @@ -185,11 +205,8 @@ DynamicZoneComponent.propTypes = { index: PropTypes.number, isFieldAllowed: PropTypes.bool, name: PropTypes.string.isRequired, - onMoveComponentDownClick: PropTypes.func.isRequired, - onMoveComponentUpClick: PropTypes.func.isRequired, + onMoveComponent: PropTypes.func.isRequired, onRemoveComponentClick: PropTypes.func.isRequired, - showDownIcon: PropTypes.bool, - showUpIcon: PropTypes.bool, }; export default DynamicZoneComponent; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/index.js index ffb23a6c4f..42081e4ac0 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicZone/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicZone/index.js @@ -27,8 +27,7 @@ const DynamicZone = ({ isFieldAllowed, isFieldReadable, labelAction, - moveComponentUp, - moveComponentDown, + moveComponentField, removeComponentFromDynamicZone, dynamicDisplayedComponents, fieldSchema, @@ -82,12 +81,12 @@ const DynamicZone = ({ } }; - const handleMoveComponentDown = (name, componentIndex) => () => { - moveComponentDown(name, componentIndex); - }; - - const handleMoveComponentUp = (name, componentIndex) => () => { - moveComponentUp(name, componentIndex); + const handleMoveComponent = (newIndex, currentIndex) => { + moveComponentField({ + name, + newIndex, + currentIndex, + }); }; const handleRemoveComponent = (name, currentIndex) => () => { @@ -117,27 +116,19 @@ const DynamicZone = ({ numberOfComponents={dynamicDisplayedComponentsLength} required={fieldSchema.required || false} /> - {dynamicDisplayedComponents.map((componentUid, index) => { - const showDownIcon = isFieldAllowed && index < dynamicDisplayedComponentsLength - 1; - const showUpIcon = isFieldAllowed && index > 0; - - return ( - - ); - })} + {dynamicDisplayedComponents.map((componentUid, index) => ( + + ))}
)} @@ -188,8 +179,7 @@ DynamicZone.propTypes = { description: PropTypes.string, label: PropTypes.string, }).isRequired, - moveComponentUp: PropTypes.func.isRequired, - moveComponentDown: PropTypes.func.isRequired, + moveComponentField: PropTypes.func.isRequired, name: PropTypes.string.isRequired, removeComponentFromDynamicZone: PropTypes.func.isRequired, }; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/tests/index.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/tests/index.test.js index a2bb644945..a8da4c53b2 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicZone/tests/index.test.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicZone/tests/index.test.js @@ -51,8 +51,7 @@ describe('DynamicZone', () => { label: 'dynamic zone', description: 'dynamic description', }, - moveComponentDown: jest.fn(), - moveComponentUp: jest.fn(), + moveComponentField: jest.fn(), name: 'DynamicZoneComponent', removeComponentFromDynamicZone: jest.fn(), }; diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicZone/utils/select.js b/packages/core/admin/admin/src/content-manager/components/DynamicZone/utils/select.js index 0b633ce9b1..02f9d54dac 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicZone/utils/select.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicZone/utils/select.js @@ -9,8 +9,7 @@ function useSelect(name) { isCreatingEntry, formErrors, modifiedData, - moveComponentUp, - moveComponentDown, + moveComponentField, removeComponentFromDynamicZone, readActionAllowedFields, updateActionAllowedFields, @@ -39,8 +38,7 @@ function useSelect(name) { isCreatingEntry, isFieldAllowed, isFieldReadable, - moveComponentUp, - moveComponentDown, + moveComponentField, removeComponentFromDynamicZone, dynamicDisplayedComponents, }; diff --git a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js index 80f980ec83..3e1ced1c5a 100644 --- a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +++ b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js @@ -597,8 +597,14 @@ const EditViewDataManagerProvider = ({ status, layout: currentContentTypeLayout, modifiedData, - moveComponentDown, moveComponentField, + /** + * @deprecated use `moveComponentField` instead. This will be removed in v5. + */ + moveComponentDown, + /** + * @deprecated use `moveComponentField` instead. This will be removed in v5. + */ moveComponentUp, onChange: handleChange, onPublish: handlePublish, diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/components/Component.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/components/Component.js index 48401aa348..378af67a3e 100644 --- a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/components/Component.js +++ b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/components/Component.js @@ -147,7 +147,6 @@ const DraggedItem = ({ })} icon={} /> - {/* react-dnd is broken in firefox with our IconButton, maybe a ref issue */}