feat: file drag and drop, multiple file select in media library (#783)
This commit is contained in:
committed by
GitHub
parent
4ff3967e18
commit
e78ebbe65e
@ -13,17 +13,22 @@ import Button from '@staticcms/core/components/common/button/Button';
|
||||
import Field from '@staticcms/core/components/common/field/Field';
|
||||
import Image from '@staticcms/core/components/common/image/Image';
|
||||
import Link from '@staticcms/core/components/common/link/Link';
|
||||
import useDragHandlers from '@staticcms/core/lib/hooks/useDragHandlers';
|
||||
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';
|
||||
import useMediaPersist from '@staticcms/core/lib/hooks/useMediaPersist';
|
||||
import useUUID from '@staticcms/core/lib/hooks/useUUID';
|
||||
import { basename } from '@staticcms/core/lib/util';
|
||||
import classNames from '@staticcms/core/lib/util/classNames.util';
|
||||
import { KeyboardSensor, PointerSensor } from '@staticcms/core/lib/util/dnd.util';
|
||||
import { isEmpty } from '@staticcms/core/lib/util/string.util';
|
||||
import { selectConfig } from '@staticcms/core/reducers/selectors/config';
|
||||
import { useAppSelector } from '@staticcms/core/store/hooks';
|
||||
import SortableImage from './components/SortableImage';
|
||||
import SortableLink from './components/SortableLink';
|
||||
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
import type { FileOrImageField, MediaPath, WidgetControlProps } from '@staticcms/core/interface';
|
||||
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';
|
||||
import type { FC, MouseEvent } from 'react';
|
||||
|
||||
const MAX_DISPLAY_LENGTH = 50;
|
||||
@ -128,6 +133,41 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => {
|
||||
handleOnChange,
|
||||
);
|
||||
|
||||
const config = useAppSelector(selectConfig);
|
||||
|
||||
const handlePersistCallback = useCallback(
|
||||
(_files: File[], assetProxies: (AssetProxy | null)[]) => {
|
||||
const newPath =
|
||||
assetProxies.length > 1 && allowsMultiple
|
||||
? [
|
||||
...(Array.isArray(internalValue) ? internalValue : [internalValue]),
|
||||
...assetProxies.filter(f => f).map(f => f!.path),
|
||||
]
|
||||
: assetProxies[0]?.path;
|
||||
|
||||
if ((Array.isArray(newPath) && newPath.length === 0) || !newPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleOnChange({
|
||||
path: newPath,
|
||||
});
|
||||
},
|
||||
[allowsMultiple, handleOnChange, internalValue],
|
||||
);
|
||||
|
||||
/**
|
||||
* Upload a file.
|
||||
*/
|
||||
const handlePersist = useMediaPersist({
|
||||
mediaConfig: field.media_library ?? config?.media_library,
|
||||
field,
|
||||
callback: handlePersistCallback,
|
||||
});
|
||||
|
||||
const { dragOverActive, handleDragEnter, handleDragLeave, handleDragOver, handleDrop } =
|
||||
useDragHandlers(handlePersist);
|
||||
|
||||
const chooseUrl = useMemo(() => field.choose_url ?? false, [field.choose_url]);
|
||||
|
||||
const handleUrl = useCallback(
|
||||
@ -417,20 +457,73 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => {
|
||||
|
||||
return useMemo(
|
||||
() => (
|
||||
<Field
|
||||
inputRef={allowsMultiple ? undefined : uploadButtonRef}
|
||||
label={label}
|
||||
errors={errors}
|
||||
noPadding={!hasErrors}
|
||||
hint={field.hint}
|
||||
forSingleList={forSingleList}
|
||||
cursor={allowsMultiple ? 'default' : 'pointer'}
|
||||
disabled={disabled}
|
||||
<div
|
||||
onDrop={handleDrop}
|
||||
onDragEnter={handleDragEnter}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDragOver={handleDragOver}
|
||||
className={classNames(
|
||||
`
|
||||
relative
|
||||
border-2
|
||||
transition-colors
|
||||
`,
|
||||
dragOverActive ? 'border-blue-500' : 'border-transparent',
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</Field>
|
||||
<div className="-m-0.5">
|
||||
<Field
|
||||
inputRef={allowsMultiple ? undefined : uploadButtonRef}
|
||||
label={label}
|
||||
errors={errors}
|
||||
noPadding={!hasErrors}
|
||||
hint={field.hint}
|
||||
forSingleList={forSingleList}
|
||||
cursor={allowsMultiple ? 'default' : 'pointer'}
|
||||
disabled={disabled}
|
||||
>
|
||||
{content}
|
||||
</Field>
|
||||
<div
|
||||
className={classNames(
|
||||
`
|
||||
absolute
|
||||
inset-0
|
||||
flex
|
||||
items-center
|
||||
justify-center
|
||||
pointer-events-none
|
||||
font-bold
|
||||
text-blue-500
|
||||
bg-white/75
|
||||
dark:text-blue-400
|
||||
dark:bg-slate-800/75
|
||||
transition-opacity
|
||||
`,
|
||||
dragOverActive ? 'opacity-100' : 'opacity-0',
|
||||
)}
|
||||
>
|
||||
{t(`mediaLibrary.mediaLibraryModal.${forImage ? 'dropImages' : 'dropFiles'}`)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
[content, disabled, errors, field.hint, allowsMultiple, forSingleList, hasErrors, label],
|
||||
[
|
||||
handleDrop,
|
||||
handleDragEnter,
|
||||
handleDragLeave,
|
||||
handleDragOver,
|
||||
dragOverActive,
|
||||
allowsMultiple,
|
||||
label,
|
||||
errors,
|
||||
hasErrors,
|
||||
field.hint,
|
||||
forSingleList,
|
||||
disabled,
|
||||
content,
|
||||
t,
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
Reference in New Issue
Block a user