@cloud-ru/ds-dropzone 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +201 -0
  3. package/README.md +356 -0
  4. package/dist/cjs/components/Dropzone/Dropzone.d.ts +25 -0
  5. package/dist/cjs/components/Dropzone/Dropzone.js +11 -0
  6. package/dist/cjs/components/Dropzone/index.d.ts +1 -0
  7. package/dist/cjs/components/Dropzone/index.js +17 -0
  8. package/dist/cjs/components/FileUpload/FileUpload.d.ts +45 -0
  9. package/dist/cjs/components/FileUpload/FileUpload.js +56 -0
  10. package/dist/cjs/components/FileUpload/index.d.ts +1 -0
  11. package/dist/cjs/components/FileUpload/index.js +17 -0
  12. package/dist/cjs/components/FileUpload/styles.module.css +3 -0
  13. package/dist/cjs/components/HiddenDropZone/HiddenDropZone.d.ts +10 -0
  14. package/dist/cjs/components/HiddenDropZone/HiddenDropZone.js +19 -0
  15. package/dist/cjs/components/HiddenDropZone/index.d.ts +1 -0
  16. package/dist/cjs/components/HiddenDropZone/index.js +17 -0
  17. package/dist/cjs/components/HiddenDropZone/styles.module.css +20 -0
  18. package/dist/cjs/components/PrivateDropZone/PrivateDropZone.d.ts +2 -0
  19. package/dist/cjs/components/PrivateDropZone/PrivateDropZone.js +62 -0
  20. package/dist/cjs/components/PrivateDropZone/constants.d.ts +1 -0
  21. package/dist/cjs/components/PrivateDropZone/constants.js +5 -0
  22. package/dist/cjs/components/PrivateDropZone/index.d.ts +2 -0
  23. package/dist/cjs/components/PrivateDropZone/index.js +5 -0
  24. package/dist/cjs/components/PrivateDropZone/styles.module.css +405 -0
  25. package/dist/cjs/components/PrivateDropZone/types.d.ts +23 -0
  26. package/dist/cjs/components/PrivateDropZone/types.js +2 -0
  27. package/dist/cjs/components/index.d.ts +3 -0
  28. package/dist/cjs/components/index.js +19 -0
  29. package/dist/cjs/constants.d.ts +17 -0
  30. package/dist/cjs/constants.js +20 -0
  31. package/dist/cjs/hooks/useDrag.d.ts +7 -0
  32. package/dist/cjs/hooks/useDrag.js +30 -0
  33. package/dist/cjs/index.d.ts +4 -0
  34. package/dist/cjs/index.js +20 -0
  35. package/dist/cjs/package.json +3 -0
  36. package/dist/cjs/style.css +428 -0
  37. package/dist/cjs/types.d.ts +33 -0
  38. package/dist/cjs/types.js +2 -0
  39. package/dist/cjs/utils.d.ts +37 -0
  40. package/dist/cjs/utils.js +70 -0
  41. package/dist/esm/components/Dropzone/Dropzone.d.ts +25 -0
  42. package/dist/esm/components/Dropzone/Dropzone.js +8 -0
  43. package/dist/esm/components/Dropzone/index.d.ts +1 -0
  44. package/dist/esm/components/Dropzone/index.js +1 -0
  45. package/dist/esm/components/FileUpload/FileUpload.d.ts +45 -0
  46. package/dist/esm/components/FileUpload/FileUpload.js +50 -0
  47. package/dist/esm/components/FileUpload/index.d.ts +1 -0
  48. package/dist/esm/components/FileUpload/index.js +1 -0
  49. package/dist/esm/components/FileUpload/styles.module.css +3 -0
  50. package/dist/esm/components/HiddenDropZone/HiddenDropZone.d.ts +10 -0
  51. package/dist/esm/components/HiddenDropZone/HiddenDropZone.js +13 -0
  52. package/dist/esm/components/HiddenDropZone/index.d.ts +1 -0
  53. package/dist/esm/components/HiddenDropZone/index.js +1 -0
  54. package/dist/esm/components/HiddenDropZone/styles.module.css +20 -0
  55. package/dist/esm/components/PrivateDropZone/PrivateDropZone.d.ts +2 -0
  56. package/dist/esm/components/PrivateDropZone/PrivateDropZone.js +56 -0
  57. package/dist/esm/components/PrivateDropZone/constants.d.ts +1 -0
  58. package/dist/esm/components/PrivateDropZone/constants.js +1 -0
  59. package/dist/esm/components/PrivateDropZone/index.d.ts +2 -0
  60. package/dist/esm/components/PrivateDropZone/index.js +1 -0
  61. package/dist/esm/components/PrivateDropZone/styles.module.css +405 -0
  62. package/dist/esm/components/PrivateDropZone/types.d.ts +23 -0
  63. package/dist/esm/components/PrivateDropZone/types.js +1 -0
  64. package/dist/esm/components/index.d.ts +3 -0
  65. package/dist/esm/components/index.js +3 -0
  66. package/dist/esm/constants.d.ts +17 -0
  67. package/dist/esm/constants.js +17 -0
  68. package/dist/esm/hooks/useDrag.d.ts +7 -0
  69. package/dist/esm/hooks/useDrag.js +27 -0
  70. package/dist/esm/index.d.ts +4 -0
  71. package/dist/esm/index.js +4 -0
  72. package/dist/esm/style.css +428 -0
  73. package/dist/esm/types.d.ts +33 -0
  74. package/dist/esm/types.js +1 -0
  75. package/dist/esm/utils.d.ts +37 -0
  76. package/dist/esm/utils.js +64 -0
  77. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  78. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  79. package/package.json +43 -0
  80. package/src/components/Dropzone/Dropzone.tsx +40 -0
  81. package/src/components/Dropzone/index.ts +1 -0
  82. package/src/components/FileUpload/FileUpload.tsx +145 -0
  83. package/src/components/FileUpload/index.ts +1 -0
  84. package/src/components/FileUpload/styles.module.scss +3 -0
  85. package/src/components/HiddenDropZone/HiddenDropZone.tsx +39 -0
  86. package/src/components/HiddenDropZone/index.ts +1 -0
  87. package/src/components/HiddenDropZone/styles.module.scss +20 -0
  88. package/src/components/PrivateDropZone/PrivateDropZone.tsx +122 -0
  89. package/src/components/PrivateDropZone/constants.ts +1 -0
  90. package/src/components/PrivateDropZone/index.ts +2 -0
  91. package/src/components/PrivateDropZone/styles.module.scss +126 -0
  92. package/src/components/PrivateDropZone/types.ts +29 -0
  93. package/src/components/index.ts +3 -0
  94. package/src/constants.ts +19 -0
  95. package/src/hooks/useDrag.ts +49 -0
  96. package/src/index.ts +4 -0
  97. package/src/types.ts +36 -0
  98. package/src/utils.ts +89 -0
@@ -0,0 +1,45 @@
1
+ import { WithSupportProps } from '@cloud-ru/ds-utils';
2
+ import { ChangeEventHandler, InputHTMLAttributes, ReactElement, Ref } from 'react';
3
+ import { UploadMode } from '../../types';
4
+ import { AcceptInput, FileRejection } from '../../utils';
5
+ /**
6
+ * Нативные атрибуты `<input type="file">`, проксируемые на скрытый input:
7
+ * `name`, `id`, `required`, `capture`, `form`, `onBlur` и т.п. — всё, что нужно
8
+ * для работы в HTML-форме и интеграции с react-hook-form (`{...register(name)}`).
9
+ * Управляемые самим компонентом поля исключены.
10
+ */
11
+ type NativeInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'accept' | 'multiple' | 'disabled' | 'value' | 'defaultValue' | 'onChange' | 'children'>;
12
+ export type FileUploadProps = WithSupportProps<{
13
+ /** Триггер открытия диалога выбора файлов (кнопка / лейбл). Клонируется, к нему добавляется `onClick`. */
14
+ children: ReactElement;
15
+ /** Колбек с принятыми файлами (прошедшими проверку `maxSize` и `accept`). */
16
+ onFilesUpload(files: File[]): void;
17
+ /**
18
+ * Колбек с отклонёнными файлами и причиной отказа (`maxSize` / `mime`).
19
+ * Вызывается только когда задан `maxSize` и/или `accept` и есть отклонённые файлы.
20
+ */
21
+ onFilesReject?(rejections: FileRejection[]): void;
22
+ /**
23
+ * Нативный `onChange` скрытого input. Вызывается до валидации с исходным событием —
24
+ * точка интеграции с react-hook-form и другими form-библиотеками.
25
+ */
26
+ onChange?: ChangeEventHandler<HTMLInputElement>;
27
+ /**
28
+ * Режим выбора файлов
29
+ * @default multiple
30
+ */
31
+ mode?: UploadMode;
32
+ /**
33
+ * Разрешённые типы файлов: MIME-тип (`image/png`), шаблон (`image/*`) или расширение (`.pdf`).
34
+ * Строка (через запятую, как нативный `accept`) или массив. Задаёт нативный `accept` и валидацию.
35
+ */
36
+ accept?: AcceptInput;
37
+ /** Максимальный размер одного файла в байтах. Превысившие уходят в `onFilesReject`. */
38
+ maxSize?: number;
39
+ /** Деактивирует триггер и input. */
40
+ disabled?: boolean;
41
+ /** Ссылка на нативный `<input type="file">` — для интеграции с формами (react-hook-form `ref`). */
42
+ innerRef?: Ref<HTMLInputElement>;
43
+ } & NativeInputProps>;
44
+ export declare function FileUpload({ mode, onFilesUpload, onFilesReject, onChange, accept, maxSize, disabled, innerRef, children, ...rest }: FileUploadProps): import("react/jsx-runtime").JSX.Element;
45
+ export {};
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FileUpload = FileUpload;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const utils_1 = require("@cloud-ru/ds-utils");
9
+ const react_1 = require("react");
10
+ const constants_1 = require("../../constants");
11
+ const utils_2 = require("../../utils");
12
+ const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
13
+ // eslint-disable-next-line ds/require-inner-ref-support -- innerRef ведёт на скрытый <input type="file">, а не на корень: маркер соврал бы поповеру.
14
+ function FileUpload({ mode = constants_1.UPLOAD_MODE.Multiple, onFilesUpload, onFilesReject, onChange, accept, maxSize, disabled = false, innerRef, children, ...rest }) {
15
+ const hiddenFileInput = (0, react_1.useRef)(null);
16
+ const setInputRef = (node) => {
17
+ hiddenFileInput.current = node;
18
+ if (typeof innerRef === 'function') {
19
+ innerRef(node);
20
+ }
21
+ else if (innerRef) {
22
+ innerRef.current = node;
23
+ }
24
+ };
25
+ const handleButtonClick = (cb) => (e) => {
26
+ if (disabled)
27
+ return;
28
+ hiddenFileInput.current?.click();
29
+ cb?.(e);
30
+ };
31
+ const handleFileSelect = (e) => {
32
+ onChange?.(e);
33
+ const { files } = e.target;
34
+ if (!files)
35
+ return;
36
+ const filesArray = Array.from(files);
37
+ if (typeof maxSize === 'number' || accept) {
38
+ const { accepted, rejected } = (0, utils_2.partitionFiles)(filesArray, { maxSize, accept });
39
+ if (rejected.length) {
40
+ onFilesReject?.(rejected);
41
+ }
42
+ onFilesUpload(accepted);
43
+ return;
44
+ }
45
+ onFilesUpload(filesArray);
46
+ };
47
+ const handleClick = (e) => {
48
+ e.target.value = '';
49
+ };
50
+ const childProps = children.props;
51
+ const mergedProps = {
52
+ onClick: handleButtonClick(childProps?.onClick),
53
+ disabled: childProps?.disabled ?? disabled,
54
+ };
55
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, react_1.cloneElement)(children, mergedProps), (0, jsx_runtime_1.jsx)("input", { ...rest, onChange: handleFileSelect, multiple: mode === constants_1.UPLOAD_MODE.Multiple, ref: setInputRef, className: styles_module_scss_1.default.hiddenInput, type: 'file', accept: (0, utils_2.buildAcceptAttribute)(accept), disabled: disabled, onClick: handleClick, "data-test-id": constants_1.TEST_IDS.fileUpload.nativeInput, ...(0, utils_1.extractDataTestProps)(rest) })] }));
56
+ }
@@ -0,0 +1 @@
1
+ export * from './FileUpload';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./FileUpload"), exports);
@@ -0,0 +1,3 @@
1
+ .hiddenInput{
2
+ display:none;
3
+ }
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react';
2
+ import { DropzoneProps } from '../Dropzone';
3
+ export type HiddenDropZoneProps = Omit<DropzoneProps, 'children'> & {
4
+ /** Контент dropzone при drag (overlay) */
5
+ content?: ReactNode;
6
+ /** Дочерний контент, поверх которого отображается dropzone при drag */
7
+ children: ReactNode;
8
+ };
9
+ /** Компонент — скрытая дропзона, накрывающая произвольный контент (формы, карточки) */
10
+ export declare function HiddenDropZone(props: HiddenDropZoneProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.HiddenDropZone = HiddenDropZone;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const utils_1 = require("@cloud-ru/ds-utils");
9
+ const classnames_1 = __importDefault(require("classnames"));
10
+ const useDrag_1 = require("../../hooks/useDrag");
11
+ const PrivateDropZone_1 = require("../PrivateDropZone");
12
+ const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
13
+ /** Компонент — скрытая дропзона, накрывающая произвольный контент (формы, карточки) */
14
+ function HiddenDropZone(props) {
15
+ const { disabled = false, children, className, content, 'data-test-id': dataTestId, ...rest } = props;
16
+ const { events, isOver } = (0, useDrag_1.useDrag)(disabled);
17
+ const showOverlay = isOver && !disabled;
18
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.wrapper, className), ...(0, utils_1.extractSupportProps)({ ...rest, 'data-test-id': dataTestId }), ...events, children: [showOverlay && ((0, jsx_runtime_1.jsx)(PrivateDropZone_1.PrivateDropZone, { className: styles_module_scss_1.default.dropZone, ...events, ...rest, isOver: true, disabled: disabled, children: content })), (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.children, showOverlay && styles_module_scss_1.default.hidden), children: children })] }));
19
+ }
@@ -0,0 +1 @@
1
+ export * from './HiddenDropZone';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./HiddenDropZone"), exports);
@@ -0,0 +1,20 @@
1
+ .wrapper{
2
+ position:relative;
3
+ display:inline-block;
4
+ }
5
+
6
+ .dropZone{
7
+ position:absolute;
8
+ width:100%;
9
+ height:100%;
10
+ }
11
+
12
+ .hidden *{
13
+ visibility:hidden;
14
+ opacity:0;
15
+ }
16
+
17
+ .children{
18
+ width:100%;
19
+ height:100%;
20
+ }
@@ -0,0 +1,2 @@
1
+ import { PrivateDropZoneProps } from './types';
2
+ export declare function PrivateDropZone({ disabled, className, isOver, onDragLeave, onDragOver, onDrop, mode, children, onFilesUpload, onFilesReject, accept, maxSize, onChange, innerRef, name, id, required, form, capture, size, ...rest }: PrivateDropZoneProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PrivateDropZone = PrivateDropZone;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const utils_1 = require("@cloud-ru/ds-utils");
9
+ const classnames_1 = __importDefault(require("classnames"));
10
+ const react_1 = require("react");
11
+ const constants_1 = require("../../constants");
12
+ const utils_2 = require("../../utils");
13
+ const constants_2 = require("./constants");
14
+ const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
15
+ // eslint-disable-next-line ds/require-inner-ref-support -- innerRef ведёт на скрытый <input type="file">, а не на корень: маркер соврал бы поповеру.
16
+ function PrivateDropZone({ disabled = false, className, isOver, onDragLeave, onDragOver, onDrop, mode = constants_2.UPLOAD_MODE.Multiple, children, onFilesUpload, onFilesReject, accept, maxSize, onChange, innerRef, name, id, required, form, capture, size = 'm', ...rest }) {
17
+ const hiddenFileInput = (0, react_1.useRef)(null);
18
+ const setInputRef = (node) => {
19
+ hiddenFileInput.current = node;
20
+ if (typeof innerRef === 'function') {
21
+ innerRef(node);
22
+ }
23
+ else if (innerRef) {
24
+ innerRef.current = node;
25
+ }
26
+ };
27
+ const emitFiles = (files) => {
28
+ if (typeof maxSize === 'number' || accept) {
29
+ const { accepted, rejected } = (0, utils_2.partitionFiles)(files, { maxSize, accept });
30
+ if (rejected.length) {
31
+ onFilesReject?.(rejected);
32
+ }
33
+ onFilesUpload(accepted);
34
+ return;
35
+ }
36
+ onFilesUpload(files);
37
+ };
38
+ const handleAttachFile = () => {
39
+ if (disabled)
40
+ return;
41
+ hiddenFileInput.current?.click();
42
+ };
43
+ const handleFileSelect = (e) => {
44
+ onChange?.(e);
45
+ const { files } = e.target;
46
+ if (!files)
47
+ return;
48
+ emitFiles(Array.from(files));
49
+ };
50
+ const handleDrop = (e) => {
51
+ if (disabled)
52
+ return;
53
+ e.preventDefault();
54
+ const filesArray = Array.from(e.dataTransfer.files);
55
+ onDrop?.(e);
56
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
57
+ emitFiles(mode === constants_2.UPLOAD_MODE.Single ? [filesArray[0]] : filesArray);
58
+ };
59
+ return ((0, jsx_runtime_1.jsxs)("button", { className: (0, classnames_1.default)(className, styles_module_scss_1.default.container), type: 'button', disabled: disabled, ...(0, utils_1.extractSupportProps)(rest), "data-over": isOver || undefined, "data-disabled": disabled || undefined, "data-size": size, onClick: handleAttachFile, tabIndex: disabled ? -1 : 0, onDrop: handleDrop, onDragLeave: onDragLeave, onDragOver: onDragOver, children: [(0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.acrylic, "aria-hidden": true }), (0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.borderLayer, "data-state-layer": true, "aria-hidden": true, "data-state": 'borderOnBackground' }), (0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.overLayer, "data-state-layer": true, "aria-hidden": true, "data-state": 'activatedOnBackground' }), children && (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.content, children: children }), (0, jsx_runtime_1.jsx)("input", { "data-test-id": constants_1.TEST_IDS.dropzone.nativeInput, className: styles_module_scss_1.default.hidden, onChange: handleFileSelect, multiple: mode === constants_2.UPLOAD_MODE.Multiple, ref: setInputRef, type: 'file', accept: (0, utils_2.buildAcceptAttribute)(accept), disabled: disabled, name: name, id: id, required: required, form: form, capture: capture, onClick: e => {
60
+ e.target.value = '';
61
+ } })] }));
62
+ }
@@ -0,0 +1 @@
1
+ export { UPLOAD_MODE } from '../../constants';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UPLOAD_MODE = void 0;
4
+ var constants_1 = require("../../constants");
5
+ Object.defineProperty(exports, "UPLOAD_MODE", { enumerable: true, get: function () { return constants_1.UPLOAD_MODE; } });
@@ -0,0 +1,2 @@
1
+ export { PrivateDropZone } from './PrivateDropZone';
2
+ export type { PrivateDropZoneProps } from './types';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PrivateDropZone = void 0;
4
+ var PrivateDropZone_1 = require("./PrivateDropZone");
5
+ Object.defineProperty(exports, "PrivateDropZone", { enumerable: true, get: function () { return PrivateDropZone_1.PrivateDropZone; } });