Merge pull request #11618 from strapi/dnd-preview-box

DnD SelectedStep in ML / CM
This commit is contained in:
cyril lopez 2021-11-18 15:40:54 +01:00 committed by GitHub
commit 1a7f02a19d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 145 additions and 7 deletions

View File

@ -34,5 +34,5 @@ export const useSelectionState = (key, initialValue) => {
}
};
return [selections, { selectOne, selectAll, selectOnly }];
return [selections, { selectOne, selectAll, selectOnly, setSelections }];
};

View File

@ -0,0 +1,38 @@
<!--- useLockScroll.stories.mdx --->
import { Meta } from '@storybook/addon-docs';
<Meta title="hooks/useSelectionState" />
# useSelectionState
This hook is used in order to facilitate the select / partial selection of a global set
## Usage
```js
import { useSelectionState } from '@strapi/helper-plugin';
const Modal = ({ onToggle, isOpen }) => {
const [selectedAssets, { selectOne, selectAll, selectOnly, setSelections }] = useSelectionState(
'id', // This is the comparaison attribute name
[]
);
const elements = [{ id: 1, name: 'Hello' }, { id: 2, name: 'World' }];
// selectOne({ id: 1 name: 'Hello' }) add the object to the selection list
// selectOnly({ id: 1 name: 'Hello' }) add the object to the selection list and remove every others in the list
// selectAll(assets) select all or remove all
// setSelections(): regular state used in react
return (
<div>
<button onClick={() => selectAll(assets)}>Select all</button>
{elements.map(el => (
<div key={el.id}>{el.name}</div>
))}
</div>
);
};
```

View File

@ -6,7 +6,7 @@ import { Typography } from '@strapi/design-system/Typography';
import { AssetList } from '../../AssetList';
import getTrad from '../../../utils/getTrad';
export const SelectedStep = ({ selectedAssets, onSelectAsset }) => {
export const SelectedStep = ({ selectedAssets, onSelectAsset, onReorderAsset }) => {
const { formatMessage } = useIntl();
return (
@ -35,13 +35,18 @@ export const SelectedStep = ({ selectedAssets, onSelectAsset }) => {
assets={selectedAssets}
onSelectAsset={onSelectAsset}
selectedAssets={selectedAssets}
onEditAsset={() => {}}
onReorderAsset={onReorderAsset}
/>
</Stack>
);
};
SelectedStep.defaultProps = {
onReorderAsset: undefined,
};
SelectedStep.propTypes = {
onSelectAsset: PropTypes.func.isRequired,
selectedAssets: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
onReorderAsset: PropTypes.func,
};

View File

@ -23,6 +23,7 @@ import { DialogTitle } from './DialogTitle';
import { DialogFooter } from './DialogFooter';
import { EditAssetDialog } from '../EditAssetDialog';
import { EmptyAssets } from '../EmptyAssets';
import { moveElement } from '../../utils/moveElement';
export const AssetDialog = ({
allowedTypes,
@ -49,7 +50,7 @@ export const AssetDialog = ({
] = useModalQueryParams();
const { data, isLoading, error } = useModalAssets({ skipWhen: !canRead, rawQuery });
const [selectedAssets, { selectOne, selectAll, selectOnly }] = useSelectionState(
const [selectedAssets, { selectOne, selectAll, selectOnly, setSelections }] = useSelectionState(
'id',
initiallySelectedAssets
);
@ -165,6 +166,14 @@ export const AssetDialog = ({
);
}
const handleMoveItem = (hoverIndex, destIndex) => {
const offset = destIndex - hoverIndex;
const orderedAssetsClone = selectedAssets.slice();
const nextAssets = moveElement(orderedAssetsClone, hoverIndex, offset);
setSelections(nextAssets);
};
return (
<ModalLayout onClose={onClose} labelledBy="asset-dialog-title" aria-busy={loading}>
<DialogTitle />
@ -226,7 +235,11 @@ export const AssetDialog = ({
</TabPanel>
<TabPanel>
<ModalBody>
<SelectedStep selectedAssets={selectedAssets} onSelectAsset={handleSelectAsset} />
<SelectedStep
selectedAssets={selectedAssets}
onSelectAsset={handleSelectAsset}
onReorderAsset={handleMoveItem}
/>
</ModalBody>
</TabPanel>
</TabPanels>

View File

@ -0,0 +1,49 @@
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import { useDrag, useDrop } from 'react-dnd';
export const Draggable = ({ children, id, index, moveItem }) => {
const ref = useRef(null);
const [, drop] = useDrop({
accept: 'draggable',
hover(hoveredOverItem) {
if (!ref.current) {
return;
}
if (hoveredOverItem.id !== id) {
moveItem(hoveredOverItem.index, index);
hoveredOverItem.index = index;
}
},
});
const [{ isDragging }, drag] = useDrag({
type: 'draggable',
item: () => {
return { index, id };
},
collect: monitor => ({
isDragging: monitor.isDragging(),
}),
});
const opacity = isDragging ? 0.2 : 1;
drag(drop(ref));
return (
<div ref={ref} style={{ opacity, cursor: 'move' }}>
{children}
</div>
);
};
Draggable.propTypes = {
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
index: PropTypes.number.isRequired,
children: PropTypes.node.isRequired,
moveItem: PropTypes.func.isRequired,
};

View File

@ -4,6 +4,7 @@ import styled from 'styled-components';
import { Box } from '@strapi/design-system/Box';
import { KeyboardNavigable } from '@strapi/design-system/KeyboardNavigable';
import { AssetCard } from '../AssetCard/AssetCard';
import { Draggable } from './Draggable';
const GridColSize = {
S: 180,
@ -23,19 +24,35 @@ export const AssetList = ({
onSelectAsset,
selectedAssets,
size,
onReorderAsset,
}) => {
return (
<KeyboardNavigable tagName="article">
<GridLayout size={size}>
{assets.map(asset => {
{assets.map((asset, index) => {
const isSelected = Boolean(
selectedAssets.find(currentAsset => currentAsset.id === asset.id)
);
if (onReorderAsset) {
return (
<Draggable key={asset.id} index={index} moveItem={onReorderAsset} id={asset.id}>
<AssetCard
allowedTypes={allowedTypes}
asset={asset}
isSelected={isSelected}
onEdit={onEditAsset ? () => onEditAsset(asset) : undefined}
onSelect={() => onSelectAsset(asset)}
size={size}
/>
</Draggable>
);
}
return (
<AssetCard
allowedTypes={allowedTypes}
key={asset.id}
allowedTypes={allowedTypes}
asset={asset}
isSelected={isSelected}
onEdit={onEditAsset ? () => onEditAsset(asset) : undefined}
@ -61,6 +78,7 @@ AssetList.defaultProps = {
allowedTypes: ['images', 'files', 'videos'],
onEditAsset: undefined,
size: 'M',
onReorderAsset: undefined,
};
AssetList.propTypes = {
@ -70,4 +88,5 @@ AssetList.propTypes = {
onSelectAsset: PropTypes.func.isRequired,
selectedAssets: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
size: PropTypes.oneOf(['S', 'M']),
onReorderAsset: PropTypes.func,
};

View File

@ -0,0 +1,14 @@
const move = (array, oldIndex, newIndex) => {
if (newIndex >= array.length) {
newIndex = array.length - 1;
}
array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
return array;
};
export const moveElement = (array, index, offset) => {
const newIndex = index + offset;
return move(array, index, newIndex);
};