Skip to content

Commit

Permalink
feat(DragNDropFieldCanary): create new component (#3600)
Browse files Browse the repository at this point in the history
closes #3531

Важно: Обновите react-dropzone до `^14.2.3`

---------

Co-authored-by: gizeasy <[email protected]>
Co-authored-by: alyonurchick1 <[email protected]>
  • Loading branch information
3 people authored Apr 26, 2024
1 parent 4c054d3 commit cd312fe
Show file tree
Hide file tree
Showing 35 changed files with 1,943 additions and 2 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
3 changes: 2 additions & 1 deletion src/components/DragNDropField/DragNDropField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -55,7 +56,7 @@ export const DragNDropField = forwardRef<HTMLDivElement, DragNDropFieldProps>(
rootRef,
open,
} = useDropzone({
accept: accept?.length ? accept : undefined,
accept: formatAccept(accept),
maxSize: maxSize || undefined,
minSize: minSize || undefined,
onDrop: handleDrop,
Expand Down
9 changes: 9 additions & 0 deletions src/components/DragNDropField/formatAccept.ts
Original file line number Diff line number Diff line change
@@ -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;
};
34 changes: 34 additions & 0 deletions src/components/DragNDropFieldCanary/DragNDropField.css
Original file line number Diff line number Diff line change
@@ -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;
}
}
93 changes: 93 additions & 0 deletions src/components/DragNDropFieldCanary/DragNDropFieldCanary.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLDivElement, DragNDropFieldProps>(
(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 (
<div
{...getRootProps({
...otherProps,
className: cnDragNDropField({ active: isDragActive, disabled }, [
className,
]),
onClick: useCallback<React.MouseEventHandler<HTMLDivElement>>((e) => {
if (e.target !== rootRef.current) {
e.stopPropagation();
}
onClickRef.current?.(e);
}, []),
ref: useForkRef([ref, rootRef]),
})}
>
<input {...getInputProps()} />
{renderCildren(children, {
...otherStateProps,
accept,
maxSize,
minSize,
multiple,
openFileDialog,
locale,
disabled,
isDragActive,
})}
</div>
);
},
);

export * from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.canary--DragNDropFieldContent {
&-Button {
margin-top: var(--space-s);
}

&-Text_disabled.Text_view_secondary {
color: var(--color-control-typo-disable);
}
}
Original file line number Diff line number Diff line change
@@ -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<DragNDropFieldContentProps> = ({
accept,
maxSize,
minSize,
multiple,
openFileDialog,
locale,
disabled,
isDragActive,
}) => {
const requirements = formatFileRequirements(accept, maxSize, minSize, locale);
const fileText = multiple ? locale.files : locale.file;

return isDragActive ? (
<Text view="secondary" size="s" align="center" lineHeight="m">
{getText(locale['drag-active-message'])}
</Text>
) : (
<>
<Text
className={cnDragNDropFieldContent('Text', { disabled })}
view="secondary"
size="s"
lineHeight="s"
align="center"
>
{getText(locale['call-to-action'], { fileText })}
{requirements && (
<>
<br />
{requirements}
</>
)}
</Text>
<Button
className={cnDragNDropFieldContent('Button')}
label={getText(locale['action-button'], { fileText })}
iconLeft={IconAttach}
view="ghost"
size="s"
onClick={openFileDialog}
disabled={disabled}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Accept } from 'react-dropzone';

import { isNotNil } from '../../../utils/type-guards';
import {
DragNDropFieldPropLocale,
DragNDropFieldProps,
} from '../DragNDropFieldCanary';
import { formatFileSize } from '../formatFileSize';
import { defaultLocale } from '../locale';

const formatAccept = (accept: Accept | undefined): string | undefined => {
return (
accept &&
Object.entries(accept)
.map(([key, values]) => {
return values.length > 0 ? values.join(', ') : key;
})
.join(', ')
);
};

const formatSize = (
prefix: string,
size: number | undefined,
locale: Required<DragNDropFieldPropLocale>,
) => (size ? `${prefix} ${formatFileSize(size, locale)}` : undefined);

export const formatFileRequirements = (
accept: DragNDropFieldProps['accept'],
maxSize: DragNDropFieldProps['maxSize'],
minSize: DragNDropFieldProps['minSize'],
locale: Required<DragNDropFieldPropLocale> = defaultLocale,
): string => {
return [
locale['fit-files'],
formatAccept(accept),
formatSize(locale.from, minSize, locale),
formatSize(locale.before, maxSize, locale),
]
.filter(isNotNil)
.join(' ');
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './DragNDropFieldContent';
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.canary--DragNDropFieldInformer {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
margin-top: var(--space-xs);
padding: var(--space-xs) var(--space-xs) var(--space-xs) var(--space-s);
color: var(--color-typo-primary);
background-color: var(--informer-status-bg-color);
border: var(--control-border-width) solid var(--informer-status-border-color);
border-radius: var(--control-radius);

&_status {
&_default {
--informer-status-border-color: var(--color-bg-border);
--informer-status-bg-color: var(--color-control-bg-default);
}

&_alert {
--informer-status-border-color: var(--color-bg-alert);
--informer-status-bg-color: rgba(235, 87, 87, 0.05);
}

&_warning {
--informer-status-border-color: var(--color-bg-alert);
--informer-status-bg-color: rgba(243, 139, 0, 0.05);
}
}

&-Text {
width: 100%;
min-height: var(--space-xl);
align-content: center;
}

&-Progress {
margin-right: var(--space-xs);
color: var(--informer-status-border-color);
}

&-Button {
margin-left: var(--space-xs);
}

&-Icon {
margin-right: var(--space-xs);
}
}
Loading

0 comments on commit cd312fe

Please sign in to comment.