@homecode/ui 5.0.1 → 5.1.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.
- package/dist/esm/index.js +1 -0
- package/dist/esm/src/components/Autocomplete/Autocomplete.js +1 -0
- package/dist/esm/src/components/DropZone/DropZone.js +112 -0
- package/dist/esm/src/components/DropZone/DropZone.styl.js +7 -0
- package/dist/esm/src/components/InputFile/InputFile.js +1 -0
- package/dist/esm/types/src/components/DropZone/DropZone.d.ts +3 -0
- package/dist/esm/types/src/components/DropZone/DropZone.types.d.ts +18 -0
- package/dist/esm/types/src/components/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export { DatePickerInput } from './src/components/DatePickerInput/DatePickerInpu
|
|
|
12
12
|
export { DateTime, formatDate } from './src/components/DateTime/DateTime.js';
|
|
13
13
|
export { Dialogue } from './src/components/Dialogue/Dialogue.js';
|
|
14
14
|
export { Draggable } from './src/components/Draggable/Draggable.js';
|
|
15
|
+
export { DropZone } from './src/components/DropZone/DropZone.js';
|
|
15
16
|
export { Expand } from './src/components/Expand/Expand.js';
|
|
16
17
|
export { Flex } from './src/components/Flex/Flex.js';
|
|
17
18
|
export { Form } from './src/components/Form/Form.js';
|
|
@@ -36,6 +36,7 @@ import '../DatePickerInput/DatePickerInput.styl.js';
|
|
|
36
36
|
import '../Dialogue/Dialogue.js';
|
|
37
37
|
import '../../tools/queryParams.js';
|
|
38
38
|
import '../Draggable/Draggable.styl.js';
|
|
39
|
+
import '../DropZone/DropZone.styl.js';
|
|
39
40
|
import '../Expand/Expand.styl.js';
|
|
40
41
|
import '../Flex/Flex.styl.js';
|
|
41
42
|
import '../Form/Form.styl.js';
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
3
|
+
import cn from 'classnames';
|
|
4
|
+
import { generateUID } from '../../tools/uid.js';
|
|
5
|
+
import S from './DropZone.styl.js';
|
|
6
|
+
|
|
7
|
+
function matchesAcceptPart(file, part) {
|
|
8
|
+
const p = part.trim().toLowerCase();
|
|
9
|
+
if (!p)
|
|
10
|
+
return false;
|
|
11
|
+
if (p.startsWith('.')) {
|
|
12
|
+
return file.name.toLowerCase().endsWith(p);
|
|
13
|
+
}
|
|
14
|
+
if (p.includes('/')) {
|
|
15
|
+
const t = file.type.toLowerCase();
|
|
16
|
+
if (p.endsWith('/*')) {
|
|
17
|
+
const prefix = p.slice(0, -1);
|
|
18
|
+
return t.startsWith(prefix);
|
|
19
|
+
}
|
|
20
|
+
return t === p;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
function matchesAccept(file, accept) {
|
|
25
|
+
const trimmed = accept.trim();
|
|
26
|
+
if (!trimmed)
|
|
27
|
+
return true;
|
|
28
|
+
const parts = trimmed
|
|
29
|
+
.split(',')
|
|
30
|
+
.map(s => s.trim())
|
|
31
|
+
.filter(Boolean);
|
|
32
|
+
if (parts.length === 0)
|
|
33
|
+
return true;
|
|
34
|
+
return parts.some(part => matchesAcceptPart(file, part));
|
|
35
|
+
}
|
|
36
|
+
function DropZone(props) {
|
|
37
|
+
const { accept, label, error, disabled = false, ghost = false, id, className, } = props;
|
|
38
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
39
|
+
const dropAreaRef = useRef(null);
|
|
40
|
+
const fallbackInputIdRef = useRef(generateUID());
|
|
41
|
+
const inputId = id ?? fallbackInputIdRef.current;
|
|
42
|
+
const handleDrop = useCallback((e) => {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
setIsDragging(false);
|
|
45
|
+
if (disabled)
|
|
46
|
+
return;
|
|
47
|
+
const list = Array.from(e.dataTransfer.files).filter(f => matchesAccept(f, accept));
|
|
48
|
+
if (props.multiple === true) {
|
|
49
|
+
if (list.length > 0) {
|
|
50
|
+
props.onFiles(list);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const file = list[0];
|
|
55
|
+
if (file) {
|
|
56
|
+
props.onFile(file);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}, [accept, disabled, props]);
|
|
60
|
+
const handleFileInput = useCallback((e) => {
|
|
61
|
+
if (disabled)
|
|
62
|
+
return;
|
|
63
|
+
const raw = e.target.files;
|
|
64
|
+
if (!raw?.length)
|
|
65
|
+
return;
|
|
66
|
+
const list = Array.from(raw).filter(f => matchesAccept(f, accept));
|
|
67
|
+
if (props.multiple === true) {
|
|
68
|
+
if (list.length > 0) {
|
|
69
|
+
props.onFiles(list);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const file = list[0];
|
|
74
|
+
if (file) {
|
|
75
|
+
props.onFile(file);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
e.target.value = '';
|
|
79
|
+
}, [accept, disabled, props]);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
const dialog = dropAreaRef.current?.closest('[role="dialog"]');
|
|
82
|
+
const targetElement = dialog || document.body;
|
|
83
|
+
const handleGlobalDragOver = (e) => {
|
|
84
|
+
e.preventDefault();
|
|
85
|
+
setIsDragging(true);
|
|
86
|
+
};
|
|
87
|
+
const handleGlobalDragLeave = (e) => {
|
|
88
|
+
if (!e.relatedTarget ||
|
|
89
|
+
!targetElement.contains(e.relatedTarget)) {
|
|
90
|
+
setIsDragging(false);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const handleGlobalDrop = () => {
|
|
94
|
+
setIsDragging(false);
|
|
95
|
+
};
|
|
96
|
+
targetElement.addEventListener('dragover', handleGlobalDragOver);
|
|
97
|
+
targetElement.addEventListener('dragleave', handleGlobalDragLeave);
|
|
98
|
+
targetElement.addEventListener('drop', handleGlobalDrop);
|
|
99
|
+
return () => {
|
|
100
|
+
targetElement.removeEventListener('dragover', handleGlobalDragOver);
|
|
101
|
+
targetElement.removeEventListener('dragleave', handleGlobalDragLeave);
|
|
102
|
+
targetElement.removeEventListener('drop', handleGlobalDrop);
|
|
103
|
+
};
|
|
104
|
+
}, []);
|
|
105
|
+
const shouldShowDropArea = !ghost || isDragging;
|
|
106
|
+
const multiple = props.multiple === true;
|
|
107
|
+
return (jsxs("div", { className: cn(S.root, className), children: [shouldShowDropArea && (jsxs("div", { ref: dropAreaRef, className: cn(S.dropArea, isDragging && S.isDragging, disabled && S.disabled), onDrop: handleDrop, children: [jsx("input", { type: "file", accept: accept, multiple: multiple, onChange: handleFileInput, className: S.fileInput, id: inputId, disabled: disabled }), jsx("label", { htmlFor: inputId, className: S.label, style: {
|
|
108
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
109
|
+
}, children: label })] })), error && jsx("div", { className: S.error, children: error })] }));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export { DropZone };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import styleInject from '../../../node_modules/style-inject/dist/style-inject.es.js';
|
|
2
|
+
|
|
3
|
+
var css_248z = ".DropZone_root__gk-Ef{display:flex;flex-direction:column;gap:var(--p-2)}.DropZone_dropArea__u-8Ye{background-color:var(--accent-color-alpha-100);border:2px dashed var(--decent-color-alpha-200);border-radius:var(--p-4);cursor:pointer;padding:var(--p-4);position:relative;text-align:center;transition:all .2s ease}.DropZone_dropArea__u-8Ye label:before{border-radius:var(--p-4);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}.DropZone_dropArea__u-8Ye.DropZone_isDragging__S-aQp,.DropZone_dropArea__u-8Ye:hover:not(.DropZone_disabled__qsAf6){border:2px dashed var(--active-color-alpha-500)}.DropZone_dropArea__u-8Ye:hover:not(.DropZone_disabled__qsAf6){background-color:var(--accent-color-alpha-50)}.DropZone_dropArea__u-8Ye.DropZone_isDragging__S-aQp{align-items:center;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);background-color:var(--decent-color-alpha-900)!important;display:flex;height:calc(100% - var(--p-2));justify-content:center;left:var(--p-1);position:fixed;top:var(--p-1);width:calc(100% - var(--p-2));z-index:9999}.DropZone_dropArea__u-8Ye.DropZone_disabled__qsAf6{cursor:not-allowed;opacity:.6}.DropZone_fileInput__E8DeE{display:none}.DropZone_label__u7-Pb{cursor:pointer}.DropZone_error__vSiwM{color:var(--danger-color);font-size:.875rem;padding:var(--p-1) var(--p-2)}";
|
|
4
|
+
var S = {"root":"DropZone_root__gk-Ef","dropArea":"DropZone_dropArea__u-8Ye","disabled":"DropZone_disabled__qsAf6","isDragging":"DropZone_isDragging__S-aQp","fileInput":"DropZone_fileInput__E8DeE","label":"DropZone_label__u7-Pb","error":"DropZone_error__vSiwM"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { S as default };
|
|
@@ -39,6 +39,7 @@ import 'moment';
|
|
|
39
39
|
import '../DatePickerInput/DatePickerInput.styl.js';
|
|
40
40
|
import '../Dialogue/Dialogue.js';
|
|
41
41
|
import { Draggable } from '../Draggable/Draggable.js';
|
|
42
|
+
import '../DropZone/DropZone.styl.js';
|
|
42
43
|
import '../Expand/Expand.styl.js';
|
|
43
44
|
import '../Flex/Flex.styl.js';
|
|
44
45
|
import '../Form/Form.styl.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface DropZoneBaseProps {
|
|
2
|
+
accept: string;
|
|
3
|
+
label: string;
|
|
4
|
+
error?: string | null;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
ghost?: boolean;
|
|
7
|
+
id?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DropZoneSingleProps extends DropZoneBaseProps {
|
|
11
|
+
multiple?: false;
|
|
12
|
+
onFile: (file: File) => void;
|
|
13
|
+
}
|
|
14
|
+
export interface DropZoneMultiProps extends DropZoneBaseProps {
|
|
15
|
+
multiple: true;
|
|
16
|
+
onFiles: (files: File[]) => void;
|
|
17
|
+
}
|
|
18
|
+
export type Props = DropZoneSingleProps | DropZoneMultiProps;
|
|
@@ -12,6 +12,7 @@ export * from './DatePickerInput/DatePickerInput';
|
|
|
12
12
|
export * from './DateTime/DateTime';
|
|
13
13
|
export * from './Dialogue';
|
|
14
14
|
export * from './Draggable/Draggable';
|
|
15
|
+
export * from './DropZone/DropZone';
|
|
15
16
|
export * from './Expand/Expand';
|
|
16
17
|
export * from './Flex/Flex';
|
|
17
18
|
export * from './Form/Form';
|