@mbao01/common 0.0.35 → 0.0.37
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.
|
@@ -5,7 +5,7 @@ export declare const FileUploader: {
|
|
|
5
5
|
displayName: string;
|
|
6
6
|
Content: import("react").ForwardRefExoticComponent<import("react").HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
7
7
|
Input: {
|
|
8
|
-
({
|
|
8
|
+
({ classes, children, ...props }: FileUploaderInputProps): import("react/jsx-runtime").JSX.Element;
|
|
9
9
|
displayName: string;
|
|
10
10
|
};
|
|
11
11
|
Item: import("react").ForwardRefExoticComponent<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Dispatch, SetStateAction } from "react";
|
|
1
|
+
import type { Dispatch, Ref, SetStateAction } from "react";
|
|
2
2
|
import type { DropzoneState, DropzoneOptions } from "react-dropzone";
|
|
3
3
|
export type FileUploaderProps = {
|
|
4
4
|
value: File[] | null;
|
|
@@ -7,7 +7,14 @@ export type FileUploaderProps = {
|
|
|
7
7
|
dropzoneOptions: DropzoneOptions;
|
|
8
8
|
orientation?: "horizontal" | "vertical";
|
|
9
9
|
} & React.HTMLAttributes<HTMLDivElement>;
|
|
10
|
-
export type FileUploaderInputProps = React.InputHTMLAttributes<HTMLInputElement
|
|
10
|
+
export type FileUploaderInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "className"> & {
|
|
11
|
+
classes?: Partial<{
|
|
12
|
+
all: string;
|
|
13
|
+
accepted: string;
|
|
14
|
+
rejected: string;
|
|
15
|
+
default: string;
|
|
16
|
+
}>;
|
|
17
|
+
};
|
|
11
18
|
export type DirectionOptions = "rtl" | "ltr" | undefined;
|
|
12
19
|
export type FileUploaderContextType = {
|
|
13
20
|
dropzoneState: DropzoneState;
|
|
@@ -19,4 +26,5 @@ export type FileUploaderContextType = {
|
|
|
19
26
|
setActiveIndex: Dispatch<SetStateAction<number>>;
|
|
20
27
|
orientation: "horizontal" | "vertical";
|
|
21
28
|
direction: DirectionOptions;
|
|
29
|
+
hiddenInputRef: Ref<HTMLInputElement>;
|
|
22
30
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mbao01/common",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.37",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Ayomide Bakare",
|
|
7
7
|
"license": "MIT",
|
|
@@ -145,5 +145,5 @@
|
|
|
145
145
|
"react-dom": "^18.2.0",
|
|
146
146
|
"typescript": "^5.2.2"
|
|
147
147
|
},
|
|
148
|
-
"gitHead": "
|
|
148
|
+
"gitHead": "dc6e10ed230647d7e4626895c8d9b6a6808daab5"
|
|
149
149
|
}
|
|
@@ -24,6 +24,7 @@ export const FileUploader = ({
|
|
|
24
24
|
dir,
|
|
25
25
|
...props
|
|
26
26
|
}: FileUploaderProps) => {
|
|
27
|
+
const hiddenInputRef = useRef<HTMLInputElement>(null);
|
|
27
28
|
const [isFileTooBig, setIsFileTooBig] = useState(false);
|
|
28
29
|
const [isLOF, setIsLOF] = useState(false);
|
|
29
30
|
const [activeIndex, setActiveIndex] = useState(-1);
|
|
@@ -54,54 +55,67 @@ export const FileUploader = ({
|
|
|
54
55
|
|
|
55
56
|
const handleKeyDown = useCallback(
|
|
56
57
|
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
?
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
58
|
+
const files = value ?? [];
|
|
59
|
+
const KEY_LIST = [
|
|
60
|
+
"ArrowLeft",
|
|
61
|
+
"ArrowRight",
|
|
62
|
+
"ArrowUp",
|
|
63
|
+
"ArrowDown",
|
|
64
|
+
"Enter",
|
|
65
|
+
"Space",
|
|
66
|
+
"Delete",
|
|
67
|
+
"Backspace",
|
|
68
|
+
"Escape",
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
if (KEY_LIST.includes(e.key)) {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
e.stopPropagation();
|
|
74
|
+
|
|
75
|
+
const moveNext = () => {
|
|
76
|
+
const nextIndex = activeIndex + 1;
|
|
77
|
+
setActiveIndex(nextIndex > files.length - 1 ? 0 : nextIndex);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const movePrev = () => {
|
|
81
|
+
const nextIndex = activeIndex - 1;
|
|
82
|
+
setActiveIndex(nextIndex < 0 ? files.length - 1 : nextIndex);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const prevKey =
|
|
86
|
+
orientation === "horizontal"
|
|
87
|
+
? direction === "ltr"
|
|
88
|
+
? "ArrowLeft"
|
|
89
|
+
: "ArrowRight"
|
|
90
|
+
: "ArrowUp";
|
|
91
|
+
|
|
92
|
+
const nextKey =
|
|
93
|
+
orientation === "horizontal"
|
|
94
|
+
? direction === "ltr"
|
|
95
|
+
? "ArrowRight"
|
|
96
|
+
: "ArrowLeft"
|
|
97
|
+
: "ArrowDown";
|
|
98
|
+
|
|
99
|
+
if (e.key === nextKey) {
|
|
100
|
+
moveNext();
|
|
101
|
+
} else if (e.key === prevKey) {
|
|
101
102
|
movePrev();
|
|
103
|
+
} else if (e.key === "Enter" || e.key === "Space") {
|
|
104
|
+
if (activeIndex === -1) {
|
|
105
|
+
dropzoneState.inputRef.current?.click();
|
|
106
|
+
}
|
|
107
|
+
} else if (e.key === "Delete" || e.key === "Backspace") {
|
|
108
|
+
if (activeIndex !== -1) {
|
|
109
|
+
removeFileFromSet(activeIndex);
|
|
110
|
+
if (files.length - 1 === 0) {
|
|
111
|
+
setActiveIndex(-1);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
movePrev();
|
|
115
|
+
}
|
|
116
|
+
} else if (e.key === "Escape") {
|
|
117
|
+
setActiveIndex(-1);
|
|
102
118
|
}
|
|
103
|
-
} else if (e.key === "Escape") {
|
|
104
|
-
setActiveIndex(-1);
|
|
105
119
|
}
|
|
106
120
|
},
|
|
107
121
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -131,6 +145,16 @@ export const FileUploader = ({
|
|
|
131
145
|
|
|
132
146
|
onValueChange(newValues);
|
|
133
147
|
|
|
148
|
+
if (hiddenInputRef.current) {
|
|
149
|
+
// Note the specific way we need to munge the file into the hidden input
|
|
150
|
+
// https://stackoverflow.com/a/68182158/1068446
|
|
151
|
+
const dataTransfer = new DataTransfer();
|
|
152
|
+
newValues.forEach((v) => {
|
|
153
|
+
dataTransfer.items.add(v);
|
|
154
|
+
});
|
|
155
|
+
hiddenInputRef.current.files = dataTransfer.files;
|
|
156
|
+
}
|
|
157
|
+
|
|
134
158
|
if (rejectedFiles.length > 0) {
|
|
135
159
|
for (const rejectedFile of rejectedFiles) {
|
|
136
160
|
if (rejectedFile.errors[0]?.code === "file-too-large") {
|
|
@@ -182,20 +206,17 @@ export const FileUploader = ({
|
|
|
182
206
|
setActiveIndex,
|
|
183
207
|
orientation,
|
|
184
208
|
direction,
|
|
209
|
+
hiddenInputRef,
|
|
185
210
|
}}
|
|
186
211
|
>
|
|
187
212
|
<div
|
|
188
|
-
tabIndex={0}
|
|
189
213
|
onKeyDownCapture={handleKeyDown}
|
|
190
|
-
className={cn(
|
|
191
|
-
"
|
|
192
|
-
|
|
193
|
-
{
|
|
194
|
-
"gap-2": value && value.length > 0,
|
|
195
|
-
}
|
|
196
|
-
)}
|
|
214
|
+
className={cn("grid w-full overflow-hidden", className, {
|
|
215
|
+
"gap-2": value && value.length > 0,
|
|
216
|
+
})}
|
|
197
217
|
dir={dir}
|
|
198
218
|
{...props}
|
|
219
|
+
tabIndex={-1}
|
|
199
220
|
>
|
|
200
221
|
{children}
|
|
201
222
|
</div>
|
|
@@ -245,9 +266,9 @@ const FileUploaderItem = forwardRef<
|
|
|
245
266
|
<div
|
|
246
267
|
ref={ref}
|
|
247
268
|
className={cn(
|
|
248
|
-
"h-6 p-1 justify-between cursor-pointer relative",
|
|
249
|
-
|
|
250
|
-
|
|
269
|
+
"h-6 p-1 justify-between cursor-pointer relative rounded",
|
|
270
|
+
{ "bg-base-300": isSelected },
|
|
271
|
+
className
|
|
251
272
|
)}
|
|
252
273
|
{...props}
|
|
253
274
|
>
|
|
@@ -263,7 +284,12 @@ const FileUploaderItem = forwardRef<
|
|
|
263
284
|
onClick={() => removeFileFromSet(index)}
|
|
264
285
|
>
|
|
265
286
|
<span className="sr-only">remove item {index}</span>
|
|
266
|
-
<TrashIcon
|
|
287
|
+
<TrashIcon
|
|
288
|
+
className={cn(
|
|
289
|
+
"w-4 h-4 shrink-0 hover:stroke-destructive duration-200 ease-in-out",
|
|
290
|
+
{ "text-error": isSelected }
|
|
291
|
+
)}
|
|
292
|
+
/>
|
|
267
293
|
</button>
|
|
268
294
|
</div>
|
|
269
295
|
);
|
|
@@ -272,41 +298,43 @@ const FileUploaderItem = forwardRef<
|
|
|
272
298
|
FileUploaderItem.displayName = "FileUploaderItem";
|
|
273
299
|
|
|
274
300
|
const FileUploaderInput = ({
|
|
275
|
-
|
|
301
|
+
classes,
|
|
276
302
|
children,
|
|
277
303
|
...props
|
|
278
304
|
}: FileUploaderInputProps) => {
|
|
279
|
-
const { dropzoneState, isFileTooBig, isLOF } =
|
|
305
|
+
const { dropzoneState, isFileTooBig, isLOF, hiddenInputRef } =
|
|
306
|
+
useFileUpload();
|
|
280
307
|
const rootProps = isLOF ? {} : dropzoneState.getRootProps();
|
|
281
308
|
return (
|
|
282
|
-
<div
|
|
283
|
-
{...props}
|
|
284
|
-
className={`relative w-full ${
|
|
285
|
-
isLOF ? "opacity-50 cursor-not-allowed " : "cursor-pointer "
|
|
286
|
-
}`}
|
|
287
|
-
>
|
|
309
|
+
<div {...props} className="relative w-full">
|
|
288
310
|
<div
|
|
289
311
|
className={cn(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
className
|
|
312
|
+
classes?.all,
|
|
313
|
+
"w-full rounded-lg duration-300 ease-in-out",
|
|
314
|
+
isLOF ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
|
|
315
|
+
dropzoneState.isDragAccept
|
|
316
|
+
? classes?.accepted
|
|
317
|
+
: dropzoneState.isDragReject || isFileTooBig
|
|
318
|
+
? classes?.rejected
|
|
319
|
+
: classes?.default
|
|
299
320
|
)}
|
|
300
321
|
{...rootProps}
|
|
301
322
|
>
|
|
302
323
|
{children}
|
|
303
324
|
</div>
|
|
304
325
|
<input
|
|
326
|
+
ref={hiddenInputRef}
|
|
305
327
|
{...props}
|
|
306
|
-
|
|
328
|
+
type="file"
|
|
329
|
+
tabIndex={-1}
|
|
330
|
+
className="hidden opacity-0"
|
|
331
|
+
/>
|
|
332
|
+
<input
|
|
307
333
|
ref={dropzoneState.inputRef}
|
|
334
|
+
disabled={isLOF}
|
|
308
335
|
{...dropzoneState.getInputProps()}
|
|
309
|
-
|
|
336
|
+
tabIndex={-1}
|
|
337
|
+
className={cn({ "cursor-not-allowed": isLOF })}
|
|
310
338
|
/>
|
|
311
339
|
</div>
|
|
312
340
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Dispatch, SetStateAction } from "react";
|
|
1
|
+
import type { Dispatch, Ref, SetStateAction } from "react";
|
|
2
2
|
import type { DropzoneState, DropzoneOptions } from "react-dropzone";
|
|
3
3
|
|
|
4
4
|
export type FileUploaderProps = {
|
|
@@ -9,8 +9,17 @@ export type FileUploaderProps = {
|
|
|
9
9
|
orientation?: "horizontal" | "vertical";
|
|
10
10
|
} & React.HTMLAttributes<HTMLDivElement>;
|
|
11
11
|
|
|
12
|
-
export type FileUploaderInputProps =
|
|
13
|
-
React.InputHTMLAttributes<HTMLInputElement
|
|
12
|
+
export type FileUploaderInputProps = Omit<
|
|
13
|
+
React.InputHTMLAttributes<HTMLInputElement>,
|
|
14
|
+
"className"
|
|
15
|
+
> & {
|
|
16
|
+
classes?: Partial<{
|
|
17
|
+
all: string;
|
|
18
|
+
accepted: string;
|
|
19
|
+
rejected: string;
|
|
20
|
+
default: string;
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
14
23
|
|
|
15
24
|
export type DirectionOptions = "rtl" | "ltr" | undefined;
|
|
16
25
|
|
|
@@ -24,4 +33,5 @@ export type FileUploaderContextType = {
|
|
|
24
33
|
setActiveIndex: Dispatch<SetStateAction<number>>;
|
|
25
34
|
orientation: "horizontal" | "vertical";
|
|
26
35
|
direction: DirectionOptions;
|
|
36
|
+
hiddenInputRef: Ref<HTMLInputElement>;
|
|
27
37
|
};
|