diff --git a/package.json b/package.json index 061d57abf..115071562 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "date-fns": "^2.30.0", "react": ">= 16.8.0", "react-dom": ">= 16.8.0", - "react-dropzone": "11.3.4", + "react-dropzone": "^14.2.3", "react-imask": "^7.2.1", "react-textarea-autosize": "^8.5.3", "react-transition-group": "^4.4.5" diff --git a/src/components/DragNDropField/DragNDropField.tsx b/src/components/DragNDropField/DragNDropField.tsx index 3c2f4b694..5ab6b92dc 100644 --- a/src/components/DragNDropField/DragNDropField.tsx +++ b/src/components/DragNDropField/DragNDropField.tsx @@ -12,6 +12,7 @@ import { isRenderProp } from '##/utils/isRenderProp'; import { DragNDropFieldContent } from './DragNDropFieldContent/DragNDropFieldContent'; import { DragNDropFieldTooltip } from './DragNDropFieldTooltip/DragNDropFieldTooltip'; +import { formatAccept } from './formatAccept'; import { getErrorsList } from './getErrorsList'; import { withdefaultLocale } from './locale'; import { DragNDropFieldProps } from './types'; @@ -55,7 +56,7 @@ export const DragNDropField = forwardRef( rootRef, open, } = useDropzone({ - accept: accept?.length ? accept : undefined, + accept: formatAccept(accept), maxSize: maxSize || undefined, minSize: minSize || undefined, onDrop: handleDrop, diff --git a/src/components/DragNDropField/formatAccept.ts b/src/components/DragNDropField/formatAccept.ts new file mode 100644 index 000000000..4b1c9d0d1 --- /dev/null +++ b/src/components/DragNDropField/formatAccept.ts @@ -0,0 +1,9 @@ +const delSpace = (str: string) => str.replace(' ', ''); + +export const formatAccept = (accept: string | string[] | undefined) => { + const formating = Array.isArray(accept) + ? accept.map(delSpace) + : accept?.split(',').map(delSpace); + + return formating?.length ? { '*': formating } : undefined; +}; diff --git a/src/components/DragNDropFieldCanary/DragNDropField.css b/src/components/DragNDropFieldCanary/DragNDropField.css new file mode 100644 index 000000000..8c2fb7c1c --- /dev/null +++ b/src/components/DragNDropFieldCanary/DragNDropField.css @@ -0,0 +1,34 @@ +.canary--DragNDropField { + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + box-sizing: border-box; + width: 100%; + min-height: 160px; + padding: var(--space-l); + background: var(--color-control-bg-clear); + border: 1px dashed var(--color-control-bg-border-default); + border-radius: var(--control-radius); + + &:not(&_disabled) { + cursor: pointer; + } + + &:hover { + background: var(--color-control-bg-clear-hover); + border-color: var(--color-control-bg-border-default-hover); + } + + &_active { + background: var(--color-control-bg-clear-hover); + border-color: var(--color-control-bg-border-focus); + } + + &_disabled, + &_disabled:hover { + background: var(--color-control-bg-disable); + border-color: transparent; + } +} diff --git a/src/components/DragNDropFieldCanary/DragNDropFieldCanary.tsx b/src/components/DragNDropFieldCanary/DragNDropFieldCanary.tsx new file mode 100644 index 000000000..4b5911361 --- /dev/null +++ b/src/components/DragNDropFieldCanary/DragNDropFieldCanary.tsx @@ -0,0 +1,93 @@ +import './DragNDropField.css'; + +import React, { forwardRef, useCallback } from 'react'; +import { useDropzone } from 'react-dropzone'; + +import { useForkRef } from '##/hooks/useForkRef/useForkRef'; +import { useMutableRef } from '##/hooks/useMutableRef/useMutableRef'; +import { cnCanary } from '##/utils/bem'; + +import { withDefaultLocale } from './locale'; +import { renderCildren } from './renderCildren'; +import { DragNDropFieldProps } from './types'; + +const cnDragNDropField = cnCanary('DragNDropField'); + +export const DragNDropField = forwardRef( + (props, ref) => { + const { + accept, + maxSize, + minSize, + multiple = false, + maxFiles, + children, + locale: localeProp, + disabled, + onClick, + onDrop, + onDropAccepted, + onDropRejected, + onError, + className, + ...otherProps + } = props; + + const onClickRef = useMutableRef(onClick); + + const locale = withDefaultLocale(localeProp); + + const { + getRootProps, + getInputProps, + rootRef, + open: openFileDialog, + isDragActive, + ...otherStateProps + } = useDropzone({ + accept, + maxSize: maxSize || undefined, + minSize: minSize || undefined, + maxFiles: maxFiles || undefined, + multiple, + disabled, + onDrop, + onDropAccepted, + onDropRejected, + onError, + }); + + return ( +
>((e) => { + if (e.target !== rootRef.current) { + e.stopPropagation(); + } + onClickRef.current?.(e); + }, []), + ref: useForkRef([ref, rootRef]), + })} + > + + {renderCildren(children, { + ...otherStateProps, + accept, + maxSize, + minSize, + multiple, + openFileDialog, + locale, + disabled, + isDragActive, + })} +
+ ); + }, +); + +export * from './types'; diff --git a/src/components/DragNDropFieldCanary/DragNDropFieldContent/DragNDropFieldContent.css b/src/components/DragNDropFieldCanary/DragNDropFieldContent/DragNDropFieldContent.css new file mode 100644 index 000000000..a9a4fd080 --- /dev/null +++ b/src/components/DragNDropFieldCanary/DragNDropFieldContent/DragNDropFieldContent.css @@ -0,0 +1,9 @@ +.canary--DragNDropFieldContent { + &-Button { + margin-top: var(--space-s); + } + + &-Text_disabled.Text_view_secondary { + color: var(--color-control-typo-disable); + } +} diff --git a/src/components/DragNDropFieldCanary/DragNDropFieldContent/DragNDropFieldContent.tsx b/src/components/DragNDropFieldCanary/DragNDropFieldContent/DragNDropFieldContent.tsx new file mode 100644 index 000000000..fac27a3b6 --- /dev/null +++ b/src/components/DragNDropFieldCanary/DragNDropFieldContent/DragNDropFieldContent.tsx @@ -0,0 +1,61 @@ +import './DragNDropFieldContent.css'; + +import { IconAttach } from '@consta/icons/IconAttach'; +import React from 'react'; + +import { Button } from '##/components/Button'; +import { Text } from '##/components/Text'; +import { cnCanary } from '##/utils/bem'; + +import { DragNDropFieldContentProps } from '../DragNDropFieldCanary'; +import { getText } from '../locale'; +import { formatFileRequirements } from './formatFileRequirements'; + +const cnDragNDropFieldContent = cnCanary('DragNDropFieldContent'); + +export const DragNDropFieldContent: React.FC = ({ + accept, + maxSize, + minSize, + multiple, + openFileDialog, + locale, + disabled, + isDragActive, +}) => { + const requirements = formatFileRequirements(accept, maxSize, minSize, locale); + const fileText = multiple ? locale.files : locale.file; + + return isDragActive ? ( + + {getText(locale['drag-active-message'])} + + ) : ( + <> + + {getText(locale['call-to-action'], { fileText })} + {requirements && ( + <> +
+ {requirements} + + )} +
+ {' '} + + +
+ Ссылка + + ) + : undefined} + + {withInformer && !disabled && ( + + )} +
+ {filesDropped.map((file, index) => ( + { + deleteFile(index); + }} + /> + ))} +
+ + ); +}; + +export default Variants; diff --git a/src/components/DragNDropFieldCanary/__stand__/DragNDropFieldVariants.css b/src/components/DragNDropFieldCanary/__stand__/DragNDropFieldVariants.css new file mode 100644 index 000000000..97e050e68 --- /dev/null +++ b/src/components/DragNDropFieldCanary/__stand__/DragNDropFieldVariants.css @@ -0,0 +1,13 @@ +.canary--DragNDropFieldVariants { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + max-width: 400px; + height: 100%; + + &-AttachmentList { + width: 100%; + margin-top: var(--space-l); + } +} diff --git a/src/components/DragNDropFieldCanary/__stand__/examples/DragNDropFieldExampleAccept/DragNDropFieldExampleAccept.tsx b/src/components/DragNDropFieldCanary/__stand__/examples/DragNDropFieldExampleAccept/DragNDropFieldExampleAccept.tsx new file mode 100644 index 000000000..f9e76cf54 --- /dev/null +++ b/src/components/DragNDropFieldCanary/__stand__/examples/DragNDropFieldExampleAccept/DragNDropFieldExampleAccept.tsx @@ -0,0 +1,16 @@ +import { Example } from '@consta/stand'; +import React from 'react'; + +import { DragNDropField } from '##/components/DragNDropFieldCanary'; + +export const DragNDropFieldExampleAccept = () => ( + + + +); diff --git a/src/components/DragNDropFieldCanary/__stand__/examples/DragNDropFieldExampleCallback/DragNDropFieldExampleCallback.tsx b/src/components/DragNDropFieldCanary/__stand__/examples/DragNDropFieldExampleCallback/DragNDropFieldExampleCallback.tsx new file mode 100644 index 000000000..ad7f065b1 --- /dev/null +++ b/src/components/DragNDropFieldCanary/__stand__/examples/DragNDropFieldExampleCallback/DragNDropFieldExampleCallback.tsx @@ -0,0 +1,32 @@ +import { Example } from '@consta/stand'; +import React from 'react'; + +import { DragNDropField } from '##/components/DragNDropFieldCanary'; + +export const DragNDropFieldExampleCallbackOnDrop = () => ( + + { + console.log(filesAccepted); + console.log(filesRejected); + alert( + `Файлов принято: ${filesAccepted.length}. Файлов отвергнуто: ${filesRejected.length} `, + ); + }} + onDropAccepted={(filesAccepted) => { + console.log(filesAccepted); + alert(`Файлов принято: ${filesAccepted.length}.`); + }} + onDropRejected={(filesRejected) => { + console.log(filesRejected); + alert(`Файлов отвергнуто: ${filesRejected.length}.`); + }} + onError={(error) => { + console.log(error); + alert(`Ошибка! ${error.message}`); + }} + /> + +); diff --git a/src/components/DragNDropFieldCanary/__stand__/examples/DragNDropFieldExampleChildren/DragNDropFieldExampleChildren.tsx b/src/components/DragNDropFieldCanary/__stand__/examples/DragNDropFieldExampleChildren/DragNDropFieldExampleChildren.tsx new file mode 100644 index 000000000..6d66a4b5a --- /dev/null +++ b/src/components/DragNDropFieldCanary/__stand__/examples/DragNDropFieldExampleChildren/DragNDropFieldExampleChildren.tsx @@ -0,0 +1,30 @@ +import { Example } from '@consta/stand'; +import React from 'react'; + +import { Button } from '##/components/Button'; +import { DragNDropField } from '##/components/DragNDropFieldCanary'; +import { Text } from '##/components/Text'; + +export const DragNDropFieldExampleRenderProps = () => ( + + + {({ openFileDialog }) => ( + <> + + Перетащи сюда файлики или выбери их из списка нажав по кнопке + +
+