mirror of
https://github.com/strapi/strapi.git
synced 2025-09-25 16:29:34 +00:00
Merge pull request #11618 from strapi/dnd-preview-box
DnD SelectedStep in ML / CM
This commit is contained in:
commit
1a7f02a19d
@ -34,5 +34,5 @@ export const useSelectionState = (key, initialValue) => {
|
||||
}
|
||||
};
|
||||
|
||||
return [selections, { selectOne, selectAll, selectOnly }];
|
||||
return [selections, { selectOne, selectAll, selectOnly, setSelections }];
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
```
|
@ -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,
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
};
|
@ -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,
|
||||
};
|
||||
|
14
packages/core/upload/admin/src/utils/moveElement.js
Normal file
14
packages/core/upload/admin/src/utils/moveElement.js
Normal 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);
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user