From eef9fdb463d97f5c4595c5b643c340d77a1b36e1 Mon Sep 17 00:00:00 2001 From: Josh <37798644+joshuaellis@users.noreply.github.com> Date: Mon, 7 Nov 2022 10:52:14 +0000 Subject: [PATCH] feat: add visual re-ordering with mouse --- .../EditViewDataManagerProvider/reducer.js | 4 - .../components/RelationInput/RelationInput.js | 162 ++++++++++++------ .../RelationInput/components/RelationItem.js | 82 ++++----- .../RelationInputDataManager.js | 14 +- .../__test__/useModifiedDataSelector.test.js | 13 -- .../hooks/useModifiedDataSelector.js | 20 --- 6 files changed, 159 insertions(+), 136 deletions(-) delete mode 100644 packages/core/admin/admin/src/content-manager/hooks/__test__/useModifiedDataSelector.test.js delete mode 100644 packages/core/admin/admin/src/content-manager/hooks/useModifiedDataSelector.js diff --git a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js index 57e2c2d3df..d8e58893f2 100644 --- a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +++ b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js @@ -187,13 +187,9 @@ const reducer = (state, action) => const newRelations = [...modifiedDataRelations]; - console.log(oldIndex, newIndex); - newRelations.splice(oldIndex, 1); newRelations.splice(newIndex, 0, currentItem); - console.log(newRelations); - set(draftState, path, newRelations); break; diff --git a/packages/core/admin/admin/src/content-manager/components/RelationInput/RelationInput.js b/packages/core/admin/admin/src/content-manager/components/RelationInput/RelationInput.js index d968709b72..84483c9825 100644 --- a/packages/core/admin/admin/src/content-manager/components/RelationInput/RelationInput.js +++ b/packages/core/admin/admin/src/content-manager/components/RelationInput/RelationInput.js @@ -293,64 +293,19 @@ const RelationInput = ({ outerRef={outerListRef} itemCount={totalNumberOfRelations} itemSize={RELATION_ITEM_HEIGHT + RELATION_GUTTER} - itemData={relations} - itemKey={(index, listData) => `${listData[index].id}-${listData[index].name}`} + itemData={{ + disabled, + labelDisconnectRelation, + onRelationDisconnect, + publicationStateTranslations, + relations, + totalNumberOfRelations, + updatePositionOfRelation: handleUpdatePositionOfRelation, + }} + itemKey={(index, { relations: relationsItems }) => relationsItems[index].id} innerElementType="ol" > - {({ data, index, style }) => { - const { publicationState, href, mainField, id } = data[index]; - const statusColor = publicationState === 'draft' ? 'secondary' : 'success'; - const canDrag = totalNumberOfRelations > 1; - - return ( - onRelationDisconnect(data[index])} - aria-label={labelDisconnectRelation} - > - - - } - style={{ - ...style, - bottom: style.bottom + RELATION_GUTTER, - height: style.height - RELATION_GUTTER, - }} - > - - - {href ? ( - - {mainField ?? id} - - ) : ( - - {mainField ?? id} - - )} - - - - {publicationState && ( - - - {publicationStateTranslations[publicationState]} - - - )} - - ); - }} + {ListItem} {(description || error) && ( @@ -434,4 +389,99 @@ RelationInput.propTypes = { relations: RelationsResult, }; +/** + * This is in a seperate component to enforce passing all the props the component requires to react-window + * to ensure drag & drop correctly works. + */ +const ListItem = ({ data, index, style }) => { + const { + disabled, + labelDisconnectRelation, + onRelationDisconnect, + publicationStateTranslations, + relations, + totalNumberOfRelations, + updatePositionOfRelation, + } = data; + const { publicationState, href, mainField, id } = relations[index]; + const statusColor = publicationState === 'draft' ? 'secondary' : 'success'; + const canDrag = totalNumberOfRelations > 1; + + return ( + onRelationDisconnect(data[index])} + aria-label={labelDisconnectRelation} + > + + + } + style={{ + ...style, + bottom: style.bottom + RELATION_GUTTER, + height: style.height - RELATION_GUTTER, + }} + > + + + {href ? ( + + {mainField ?? id} + + ) : ( + + {mainField ?? id} + + )} + + + + {publicationState && ( + + + {publicationStateTranslations[publicationState]} + + + )} + + ); +}; + +ListItem.defaultProps = { + data: {}, +}; + +ListItem.propTypes = { + data: PropTypes.shape({ + disabled: PropTypes.bool.isRequired, + labelDisconnectRelation: PropTypes.string.isRequired, + onRelationDisconnect: PropTypes.func.isRequired, + publicationStateTranslations: PropTypes.shape({ + draft: PropTypes.string.isRequired, + published: PropTypes.string.isRequired, + }).isRequired, + relations: PropTypes.arrayOf( + PropTypes.shape({ + href: PropTypes.string, + id: PropTypes.number.isRequired, + publicationState: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + mainField: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + }) + ), + totalNumberOfRelations: PropTypes.number.isRequired, + updatePositionOfRelation: PropTypes.func.isRequired, + }), + index: PropTypes.number.isRequired, + style: PropTypes.object.isRequired, +}; + export default RelationInput; diff --git a/packages/core/admin/admin/src/content-manager/components/RelationInput/components/RelationItem.js b/packages/core/admin/admin/src/content-manager/components/RelationInput/components/RelationItem.js index 0b7427b80b..ef8761886d 100644 --- a/packages/core/admin/admin/src/content-manager/components/RelationInput/components/RelationItem.js +++ b/packages/core/admin/admin/src/content-manager/components/RelationInput/components/RelationItem.js @@ -51,22 +51,11 @@ export const RelationItem = ({ return; } - // Determine rectangle on screen const hoverBoundingRect = relationRef.current.getBoundingClientRect(); - - // Get vertical middle const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; - - // Determine mouse position const clientOffset = monitor.getClientOffset(); - - // Get pixels to the top const hoverClientY = clientOffset.y - hoverBoundingRect.top; - // Only perform the move when the mouse has crossed half of the items height - // When dragging downwards, only move when the cursor is below 50% - // When dragging upwards, only move when the cursor is above 50% - // Dragging downwards if (dragIndex < currentIndex && hoverClientY < hoverMiddleY) { return; @@ -84,42 +73,57 @@ export const RelationItem = ({ }, }); - const [{ isDragging }, dragRef] = useDrag(() => ({ + const [{ isDragging }, dragRef, dragPreviewRef] = useDrag({ type: RELATION_ITEM_DRAG_TYPE, - item: { index }, + item: { index, id }, canDrag, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), - })); + }); - const composedRefs = composeRefs(relationRef, dropRef, dragRef); - - const opacity = isDragging ? 0 : 1; + const composedRefs = composeRefs(relationRef, dragRef); return ( - - - {/* TODO: swap this out for using children when DS is updated */} - {canDrag ? } /> : null} - {children} - {endAction && {endAction}} - + + {isDragging ? ( + + ) : ( + + {/* TODO: swap this out for using children when DS is updated */} + {canDrag ? ( + } /> + ) : null} + {children} + {endAction && {endAction}} + + )} ); }; diff --git a/packages/core/admin/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js b/packages/core/admin/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js index a176019a91..0788e9c643 100644 --- a/packages/core/admin/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +++ b/packages/core/admin/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js @@ -10,7 +10,6 @@ import { useCMEditViewDataManager, NotAllowedInput } from '@strapi/helper-plugin import { RelationInput } from '../RelationInput'; import { useRelation } from '../../hooks/useRelation'; -import { useModifiedDataSelector } from '../../hooks/useModifiedDataSelector'; import { getTrad } from '../../utils'; @@ -38,10 +37,17 @@ export const RelationInputDataManager = ({ targetModel, }) => { const { formatMessage } = useIntl(); - const { connectRelation, disconnectRelation, loadRelation, slug, initialData, reorderRelation } = - useCMEditViewDataManager(); + const { + connectRelation, + disconnectRelation, + loadRelation, + slug, + initialData, + modifiedData, + reorderRelation, + } = useCMEditViewDataManager(); - const relationsFromModifiedData = useModifiedDataSelector(name, []); + const relationsFromModifiedData = get(modifiedData, name); const currentLastPage = Math.ceil(relationsFromModifiedData.length / RELATIONS_TO_DISPLAY); diff --git a/packages/core/admin/admin/src/content-manager/hooks/__test__/useModifiedDataSelector.test.js b/packages/core/admin/admin/src/content-manager/hooks/__test__/useModifiedDataSelector.test.js deleted file mode 100644 index d9c0db242c..0000000000 --- a/packages/core/admin/admin/src/content-manager/hooks/__test__/useModifiedDataSelector.test.js +++ /dev/null @@ -1,13 +0,0 @@ -describe('useModifiedDataSelector', () => { - it.todo('should not re render when the same primitive value is returned from the modifiedData'); - - it.todo('should not re render when the same object value is returned from the modifiedData'); - - it.todo('should not re render when the same array value is returned from the modifiedData'); - - it.todo('should re render when a different primitive value is returned from the modifiedData'); - - it.todo('should re render when a different object value is returned from the modifiedData'); - - it.todo('should re render when a different array value is returned from the modifiedData'); -}); diff --git a/packages/core/admin/admin/src/content-manager/hooks/useModifiedDataSelector.js b/packages/core/admin/admin/src/content-manager/hooks/useModifiedDataSelector.js deleted file mode 100644 index 004a4f621b..0000000000 --- a/packages/core/admin/admin/src/content-manager/hooks/useModifiedDataSelector.js +++ /dev/null @@ -1,20 +0,0 @@ -import { useEffect, useState } from 'react'; -import get from 'lodash/get'; -import isEqual from 'react-fast-compare'; -import { useCMEditViewDataManager } from '@strapi/helper-plugin'; - -export const useModifiedDataSelector = (path, defaultValue) => { - const { modifiedData } = useCMEditViewDataManager(); - - const [value, setValue] = useState(get(modifiedData, path, defaultValue)); - - useEffect(() => { - const newValue = get(modifiedData, path, defaultValue); - - if (!isEqual(newValue, value)) { - setValue(newValue); - } - }, [modifiedData, path, defaultValue, value]); - - return value; -};