@functionalcms/svelte-components 4.8.10 → 4.8.12
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/components/form/Dropzone.svelte +176 -334
- package/dist/components/form/Dropzone.svelte.d.ts +41 -41
- package/dist/components/form/dropzone.d.ts +10 -58
- package/dist/components/form/dropzone.js +19 -150
- package/package.json +5 -4
- package/dist/components/form/file.d.ts +0 -7
- package/dist/components/form/file.js +0 -1255
- package/dist/components/form/from-event.d.ts +0 -12
- package/dist/components/form/from-event.js +0 -175
|
@@ -1,376 +1,218 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
export type FileRejectedReason =
|
|
6
|
+
| 'Maximum file size exceeded'
|
|
7
|
+
| 'File type not allowed'
|
|
8
|
+
| 'Maximum files uploaded';
|
|
9
|
+
|
|
10
|
+
export interface FileDropZoneProps extends Omit<HTMLInputAttributes, 'multiple'> {
|
|
11
|
+
/** Called with the uploaded files when the user drops or clicks and selects their files.
|
|
12
|
+
*
|
|
13
|
+
* @param files
|
|
14
|
+
*/
|
|
15
|
+
onUpload: (files: File[]) => Promise<void>;
|
|
16
|
+
/** The maximum amount files allowed to be uploaded */
|
|
17
|
+
maxFiles?: number;
|
|
18
|
+
fileCount?: number;
|
|
19
|
+
/** The maximum size of a file in bytes */
|
|
20
|
+
maxFileSize?: number;
|
|
21
|
+
children?: Snippet<[]>;
|
|
22
|
+
/** Called when a file does not meet the upload criteria (size, or type) */
|
|
23
|
+
onFileRejected?: (opts: { reason: FileRejectedReason; file: File }) => void;
|
|
24
|
+
|
|
25
|
+
// just for extra documentation
|
|
26
|
+
/** Takes a comma separated list of one or more file types.
|
|
27
|
+
*
|
|
28
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept)
|
|
29
|
+
*
|
|
30
|
+
* ### Usage
|
|
31
|
+
* ```svelte
|
|
32
|
+
* <FileDropZone
|
|
33
|
+
* accept=".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
34
|
+
* />
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ### Common Values
|
|
38
|
+
* ```svelte
|
|
39
|
+
* <FileDropZone accept="audio/*"/>
|
|
40
|
+
* <FileDropZone accept="image/*"/>
|
|
41
|
+
* <FileDropZone accept="video/*"/>
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
accept?: string;
|
|
30
45
|
}
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<script lang="ts">
|
|
49
|
+
import Upload from '@lucide/svelte/icons/upload.svelte';
|
|
50
|
+
import { displaySize } from './dropzone.ts';
|
|
51
|
+
import { cn } from '../../utils.ts';
|
|
31
52
|
|
|
32
53
|
let {
|
|
33
|
-
|
|
54
|
+
id,
|
|
55
|
+
children,
|
|
56
|
+
maxFiles,
|
|
57
|
+
maxFileSize,
|
|
58
|
+
fileCount,
|
|
34
59
|
disabled = false,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
noDrag = false,
|
|
42
|
-
noDragEventsBubbling = false,
|
|
43
|
-
containerClasses = '',
|
|
44
|
-
containerStyles = '',
|
|
45
|
-
disableDefaultStyles = false,
|
|
46
|
-
name = '',
|
|
47
|
-
// inputElement = undefined,
|
|
48
|
-
required = false
|
|
49
|
-
}: Partial<Props> = $props();
|
|
50
|
-
|
|
51
|
-
let inputElement: HTMLInputElement | undefined;
|
|
52
|
-
|
|
53
|
-
let state = $state({
|
|
54
|
-
isFocused: false,
|
|
55
|
-
isFileDialogActive: false,
|
|
56
|
-
isDragActive: false,
|
|
57
|
-
isDragAccept: false,
|
|
58
|
-
isDragReject: false,
|
|
59
|
-
draggedFiles: [],
|
|
60
|
-
acceptedFiles: [],
|
|
61
|
-
fileRejections: []
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
let rootRef: any;
|
|
65
|
-
|
|
66
|
-
function resetState() {
|
|
67
|
-
state.isFileDialogActive = false;
|
|
68
|
-
state.isDragActive = false;
|
|
69
|
-
state.draggedFiles = [];
|
|
70
|
-
state.acceptedFiles = [];
|
|
71
|
-
state.fileRejections = [];
|
|
72
|
-
}
|
|
60
|
+
onUpload,
|
|
61
|
+
onFileRejected,
|
|
62
|
+
accept,
|
|
63
|
+
class: className,
|
|
64
|
+
...rest
|
|
65
|
+
}: FileDropZoneProps = $props();
|
|
73
66
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
state.isFileDialogActive = true;
|
|
79
|
-
inputElement.click();
|
|
80
|
-
}
|
|
67
|
+
if (maxFiles !== undefined && fileCount === undefined) {
|
|
68
|
+
console.warn(
|
|
69
|
+
'Make sure to provide FileDropZone with `fileCount` when using the `maxFiles` prompt'
|
|
70
|
+
);
|
|
81
71
|
}
|
|
82
72
|
|
|
83
|
-
|
|
84
|
-
function onKeyDownCb(event) {
|
|
85
|
-
// Ignore keyboard events bubbling up the DOM tree
|
|
86
|
-
if (!rootRef || !rootRef.isEqualNode(event.target)) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
73
|
+
let uploading = $state(false);
|
|
89
74
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
75
|
+
const drop = async (
|
|
76
|
+
e: DragEvent & {
|
|
77
|
+
currentTarget: EventTarget & HTMLLabelElement;
|
|
93
78
|
}
|
|
94
|
-
|
|
79
|
+
) => {
|
|
80
|
+
if (disabled || !canUploadFiles) return;
|
|
95
81
|
|
|
96
|
-
|
|
97
|
-
function onFocusCb() {
|
|
98
|
-
state.isFocused = true;
|
|
99
|
-
}
|
|
100
|
-
function onBlurCb() {
|
|
101
|
-
state.isFocused = false;
|
|
102
|
-
}
|
|
82
|
+
e.preventDefault();
|
|
103
83
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
84
|
+
const droppedFiles = Array.from(e.dataTransfer?.files ?? []);
|
|
85
|
+
|
|
86
|
+
await upload(droppedFiles);
|
|
87
|
+
};
|
|
109
88
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (isIeOrEdge()) {
|
|
114
|
-
setTimeout(openFileDialog, 0);
|
|
115
|
-
} else {
|
|
116
|
-
openFileDialog();
|
|
89
|
+
const change = async (
|
|
90
|
+
e: Event & {
|
|
91
|
+
currentTarget: EventTarget & HTMLInputElement;
|
|
117
92
|
}
|
|
118
|
-
|
|
93
|
+
) => {
|
|
94
|
+
if (disabled) return;
|
|
119
95
|
|
|
120
|
-
|
|
121
|
-
event.preventDefault();
|
|
122
|
-
stopPropagation(event);
|
|
96
|
+
const selectedFiles = e.currentTarget.files;
|
|
123
97
|
|
|
124
|
-
|
|
98
|
+
if (!selectedFiles) return;
|
|
125
99
|
|
|
126
|
-
|
|
127
|
-
Promise.resolve(getFilesFromEvent(event)).then((draggedFiles) => {
|
|
128
|
-
if (isPropagationStopped(event) && !noDragEventsBubbling) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
100
|
+
await upload(Array.from(selectedFiles));
|
|
131
101
|
|
|
132
|
-
|
|
133
|
-
|
|
102
|
+
// this if a file fails and we upload the same file again we still get feedback
|
|
103
|
+
(e.target as HTMLInputElement).value = '';
|
|
104
|
+
};
|
|
134
105
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function onDragOverCb(event) {
|
|
143
|
-
event.preventDefault();
|
|
144
|
-
stopPropagation(event);
|
|
106
|
+
const shouldAcceptFile = (file: File, fileNumber: number): FileRejectedReason | undefined => {
|
|
107
|
+
if (maxFileSize !== undefined && file.size > maxFileSize) return 'Maximum file size exceeded';
|
|
145
108
|
|
|
146
|
-
if (
|
|
147
|
-
try {
|
|
148
|
-
event.dataTransfer.dropEffect = 'copy';
|
|
149
|
-
} catch {} /* eslint-disable-line no-empty */
|
|
150
|
-
}
|
|
109
|
+
if (maxFiles !== undefined && fileNumber > maxFiles) return 'Maximum files uploaded';
|
|
151
110
|
|
|
152
|
-
if (
|
|
153
|
-
dispatch('dragover', {
|
|
154
|
-
dragEvent: event
|
|
155
|
-
});
|
|
156
|
-
}
|
|
111
|
+
if (!accept) return undefined;
|
|
157
112
|
|
|
158
|
-
|
|
159
|
-
|
|
113
|
+
const acceptedTypes = accept.split(',').map((a) => a.trim().toLowerCase());
|
|
114
|
+
const fileType = file.type.toLowerCase();
|
|
115
|
+
const fileName = file.name.toLowerCase();
|
|
160
116
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const targets = dragTargetsRef.filter((target) => rootRef && rootRef.contains(target));
|
|
167
|
-
// Make sure to remove a target present multiple times only once
|
|
168
|
-
// (Firefox may fire dragenter/dragleave multiple times on the same element)
|
|
169
|
-
const targetIdx = targets.indexOf(event.target);
|
|
170
|
-
if (targetIdx !== -1) {
|
|
171
|
-
targets.splice(targetIdx, 1);
|
|
172
|
-
}
|
|
173
|
-
dragTargetsRef = targets;
|
|
174
|
-
if (targets.length > 0) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
117
|
+
const isAcceptable = acceptedTypes.some((pattern) => {
|
|
118
|
+
// check extension like .mp4
|
|
119
|
+
if (fileType.startsWith('.')) {
|
|
120
|
+
return fileName.endsWith(pattern);
|
|
121
|
+
}
|
|
177
122
|
|
|
178
|
-
|
|
179
|
-
|
|
123
|
+
// if pattern has wild card like video/*
|
|
124
|
+
if (pattern.endsWith('/*')) {
|
|
125
|
+
const baseType = pattern.slice(0, pattern.indexOf('/*'));
|
|
126
|
+
return fileType.startsWith(baseType + '/');
|
|
127
|
+
}
|
|
180
128
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}
|
|
129
|
+
// otherwise it must be a specific type like video/mp4
|
|
130
|
+
return fileType === pattern;
|
|
131
|
+
});
|
|
187
132
|
|
|
188
|
-
|
|
189
|
-
event.preventDefault();
|
|
190
|
-
stopPropagation(event);
|
|
191
|
-
|
|
192
|
-
dragTargetsRef = [];
|
|
193
|
-
|
|
194
|
-
if (isEvtWithFiles(event)) {
|
|
195
|
-
dispatch('filedropped', {
|
|
196
|
-
event
|
|
197
|
-
});
|
|
198
|
-
Promise.resolve(getFilesFromEvent(event)).then((files) => {
|
|
199
|
-
if (isPropagationStopped(event) && !noDragEventsBubbling) {
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const acceptedFiles = [];
|
|
204
|
-
const fileRejections = [];
|
|
205
|
-
|
|
206
|
-
files.forEach((file) => {
|
|
207
|
-
const [accepted, acceptError] = fileAccepted(file, accept);
|
|
208
|
-
const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize);
|
|
209
|
-
if (accepted && sizeMatch) {
|
|
210
|
-
acceptedFiles.push(file);
|
|
211
|
-
} else {
|
|
212
|
-
const errors = [acceptError, sizeError].filter((e) => e);
|
|
213
|
-
fileRejections.push({ file, errors });
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
if (!multiple && acceptedFiles.length > 1) {
|
|
218
|
-
// Reject everything and empty accepted files
|
|
219
|
-
acceptedFiles.forEach((file) => {
|
|
220
|
-
fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] });
|
|
221
|
-
});
|
|
222
|
-
acceptedFiles.splice(0);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Files dropped keep input in sync
|
|
226
|
-
if (event.dataTransfer) {
|
|
227
|
-
inputElement.files = event.dataTransfer.files;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
state.acceptedFiles = acceptedFiles;
|
|
231
|
-
state.fileRejections = fileRejections;
|
|
232
|
-
|
|
233
|
-
dispatch('drop', {
|
|
234
|
-
acceptedFiles,
|
|
235
|
-
fileRejections,
|
|
236
|
-
event
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
if (fileRejections.length > 0) {
|
|
240
|
-
dispatch('droprejected', {
|
|
241
|
-
fileRejections,
|
|
242
|
-
event
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (acceptedFiles.length > 0) {
|
|
247
|
-
dispatch('dropaccepted', {
|
|
248
|
-
acceptedFiles,
|
|
249
|
-
event
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
resetState();
|
|
255
|
-
}
|
|
133
|
+
if (!isAcceptable) return 'File type not allowed';
|
|
256
134
|
|
|
257
|
-
|
|
135
|
+
return undefined;
|
|
136
|
+
};
|
|
258
137
|
|
|
259
|
-
|
|
138
|
+
const upload = async (uploadFiles: File[]) => {
|
|
139
|
+
uploading = true;
|
|
260
140
|
|
|
261
|
-
|
|
141
|
+
const validFiles: File[] = [];
|
|
262
142
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
: "Drag 'n' drop a file here, or click to select a file");
|
|
143
|
+
for (let i = 0; i < uploadFiles.length; i++) {
|
|
144
|
+
const file = uploadFiles[i];
|
|
266
145
|
|
|
267
|
-
|
|
268
|
-
if (noDragEventsBubbling) {
|
|
269
|
-
event.stopPropagation();
|
|
270
|
-
}
|
|
271
|
-
}
|
|
146
|
+
const rejectedReason = shouldAcceptFile(file, (fileCount ?? 0) + i + 1);
|
|
272
147
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
}
|
|
148
|
+
if (rejectedReason) {
|
|
149
|
+
onFileRejected?.({ file, reason: rejectedReason });
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
279
152
|
|
|
280
|
-
|
|
281
|
-
function onDocumentDrop(event) {
|
|
282
|
-
if (!preventDropOnDocument) {
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
if (rootRef && rootRef.contains(event.target)) {
|
|
286
|
-
// If we intercepted an event for our instance, let it propagate down to the instance's onDrop handler
|
|
287
|
-
return;
|
|
153
|
+
validFiles.push(file);
|
|
288
154
|
}
|
|
289
|
-
event.preventDefault();
|
|
290
|
-
dragTargetsRef = [];
|
|
291
|
-
}
|
|
292
155
|
|
|
293
|
-
|
|
294
|
-
function onWindowFocus() {
|
|
295
|
-
// Execute the timeout only if the file dialog is opened in the browser
|
|
296
|
-
if (state.isFileDialogActive) {
|
|
297
|
-
setTimeout(() => {
|
|
298
|
-
if (inputElement) {
|
|
299
|
-
const { files } = inputElement;
|
|
300
|
-
|
|
301
|
-
if (!files.length) {
|
|
302
|
-
state.isFileDialogActive = false;
|
|
303
|
-
dispatch('filedialogcancel');
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}, 300);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
156
|
+
await onUpload(validFiles);
|
|
309
157
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
inputElement = null;
|
|
313
|
-
});
|
|
158
|
+
uploading = false;
|
|
159
|
+
};
|
|
314
160
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
161
|
+
const canUploadFiles = $derived(
|
|
162
|
+
!disabled &&
|
|
163
|
+
!uploading &&
|
|
164
|
+
!(maxFiles !== undefined && fileCount !== undefined && fileCount >= maxFiles)
|
|
165
|
+
);
|
|
318
166
|
</script>
|
|
319
167
|
|
|
320
|
-
<
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
on:keydown={composeKeyboardHandler(onKeyDownCb)}
|
|
330
|
-
on:focus={composeKeyboardHandler(onFocusCb)}
|
|
331
|
-
on:blur={composeKeyboardHandler(onBlurCb)}
|
|
332
|
-
on:click={composeHandler(onClickCb)}
|
|
333
|
-
on:dragenter={composeDragHandler(onDragEnterCb)}
|
|
334
|
-
on:dragover={composeDragHandler(onDragOverCb)}
|
|
335
|
-
on:dragleave={composeDragHandler(onDragLeaveCb)}
|
|
336
|
-
on:drop={composeDragHandler(onDropCb)}
|
|
337
|
-
{...$$restProps}
|
|
168
|
+
<label
|
|
169
|
+
ondragover={(e) => e.preventDefault()}
|
|
170
|
+
ondrop={drop}
|
|
171
|
+
for={id}
|
|
172
|
+
aria-disabled={!canUploadFiles}
|
|
173
|
+
class={cn(
|
|
174
|
+
'flex h-48 w-full place-items-center justify-center rounded-lg border-2 border-dashed border-border p-6 transition-all hover:cursor-pointer hover:bg-accent/25 aria-disabled:opacity-50 aria-disabled:hover:cursor-not-allowed',
|
|
175
|
+
className
|
|
176
|
+
)}
|
|
338
177
|
>
|
|
178
|
+
{#if children}
|
|
179
|
+
{@render children()}
|
|
180
|
+
{:else}
|
|
181
|
+
<div class="flex flex-col place-items-center justify-center gap-2">
|
|
182
|
+
<div
|
|
183
|
+
class="flex size-14 place-items-center justify-center rounded-full border border-dashed border-border text-muted-foreground"
|
|
184
|
+
>
|
|
185
|
+
<Upload class="size-7" />
|
|
186
|
+
<!-- <Icon icon="upload" class="size-7" /> -->
|
|
187
|
+
</div>
|
|
188
|
+
<div class="flex flex-col gap-0.5 text-center">
|
|
189
|
+
<span class="font-medium text-muted-foreground">
|
|
190
|
+
Drag 'n' drop files here, or click to select files
|
|
191
|
+
</span>
|
|
192
|
+
{#if maxFiles || maxFileSize}
|
|
193
|
+
<span class="text-sm text-muted-foreground/75">
|
|
194
|
+
{#if maxFiles}
|
|
195
|
+
<span>You can upload {maxFiles} files</span>
|
|
196
|
+
{/if}
|
|
197
|
+
{#if maxFiles && maxFileSize}
|
|
198
|
+
<span>(up to {displaySize(maxFileSize)} each)</span>
|
|
199
|
+
{/if}
|
|
200
|
+
{#if maxFileSize && !maxFiles}
|
|
201
|
+
<span>Maximum size {displaySize(maxFileSize)}</span>
|
|
202
|
+
{/if}
|
|
203
|
+
</span>
|
|
204
|
+
{/if}
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
{/if}
|
|
339
208
|
<input
|
|
340
|
-
|
|
341
|
-
{
|
|
342
|
-
{
|
|
209
|
+
{...rest}
|
|
210
|
+
disabled={!canUploadFiles}
|
|
211
|
+
{id}
|
|
212
|
+
{accept}
|
|
213
|
+
multiple={maxFiles === undefined || maxFiles - (fileCount ?? 0) > 1}
|
|
343
214
|
type="file"
|
|
344
|
-
{
|
|
345
|
-
|
|
346
|
-
tabindex="-1"
|
|
347
|
-
on:change={onDropCb}
|
|
348
|
-
on:click={onInputElementClick}
|
|
349
|
-
bind:this={inputElement}
|
|
350
|
-
style="display: none;"
|
|
215
|
+
onchange={change}
|
|
216
|
+
class="hidden"
|
|
351
217
|
/>
|
|
352
|
-
|
|
353
|
-
<p>{defaultPlaceholderString}</p>
|
|
354
|
-
</slot>
|
|
355
|
-
</div>
|
|
356
|
-
|
|
357
|
-
<style>
|
|
358
|
-
.dropzone {
|
|
359
|
-
flex: 1;
|
|
360
|
-
display: flex;
|
|
361
|
-
flex-direction: column;
|
|
362
|
-
align-items: center;
|
|
363
|
-
padding: 20px;
|
|
364
|
-
border-width: 2px;
|
|
365
|
-
border-radius: 2px;
|
|
366
|
-
border-color: #eeeeee;
|
|
367
|
-
border-style: dashed;
|
|
368
|
-
background-color: #fafafa;
|
|
369
|
-
color: #bdbdbd;
|
|
370
|
-
outline: none;
|
|
371
|
-
transition: border 0.24s ease-in-out;
|
|
372
|
-
}
|
|
373
|
-
.dropzone:focus {
|
|
374
|
-
border-color: #2196f3;
|
|
375
|
-
}
|
|
376
|
-
</style>
|
|
218
|
+
</label>
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
|
+
export type FileRejectedReason = 'Maximum file size exceeded' | 'File type not allowed' | 'Maximum files uploaded';
|
|
4
|
+
export interface FileDropZoneProps extends Omit<HTMLInputAttributes, 'multiple'> {
|
|
5
|
+
/** Called with the uploaded files when the user drops or clicks and selects their files.
|
|
6
|
+
*
|
|
7
|
+
* @param files
|
|
8
|
+
*/
|
|
9
|
+
onUpload: (files: File[]) => Promise<void>;
|
|
10
|
+
/** The maximum amount files allowed to be uploaded */
|
|
11
|
+
maxFiles?: number;
|
|
12
|
+
fileCount?: number;
|
|
13
|
+
/** The maximum size of a file in bytes */
|
|
14
|
+
maxFileSize?: number;
|
|
15
|
+
children?: Snippet<[]>;
|
|
16
|
+
/** Called when a file does not meet the upload criteria (size, or type) */
|
|
17
|
+
onFileRejected?: (opts: {
|
|
18
|
+
reason: FileRejectedReason;
|
|
19
|
+
file: File;
|
|
20
|
+
}) => void;
|
|
21
|
+
/** Takes a comma separated list of one or more file types.
|
|
22
|
+
*
|
|
23
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept)
|
|
24
|
+
*
|
|
25
|
+
* ### Usage
|
|
26
|
+
* ```svelte
|
|
27
|
+
* <FileDropZone
|
|
28
|
+
* accept=".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
29
|
+
* />
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* ### Common Values
|
|
33
|
+
* ```svelte
|
|
34
|
+
* <FileDropZone accept="audio/*"/>
|
|
35
|
+
* <FileDropZone accept="image/*"/>
|
|
36
|
+
* <FileDropZone accept="video/*"/>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
accept?: string;
|
|
13
40
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} ? Props extends Record<string, never> ? any : {
|
|
17
|
-
children?: any;
|
|
18
|
-
} : {});
|
|
19
|
-
declare const Dropzone: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<Partial<{
|
|
20
|
-
accept: string | Array<string>;
|
|
21
|
-
disabled: boolean;
|
|
22
|
-
maxSize: number;
|
|
23
|
-
minSize: number;
|
|
24
|
-
multiple: boolean;
|
|
25
|
-
preventDropOnDocument: boolean;
|
|
26
|
-
noClick: boolean;
|
|
27
|
-
noKeyboard: boolean;
|
|
28
|
-
noDrag: boolean;
|
|
29
|
-
noDragEventsBubbling: boolean;
|
|
30
|
-
containerClasses: string;
|
|
31
|
-
containerStyles: string;
|
|
32
|
-
disableDefaultStyles: boolean;
|
|
33
|
-
name: string;
|
|
34
|
-
required: boolean;
|
|
35
|
-
}>, {
|
|
36
|
-
default: {};
|
|
37
|
-
}>, {
|
|
38
|
-
[evt: string]: CustomEvent<any>;
|
|
39
|
-
}, {
|
|
40
|
-
default: {};
|
|
41
|
-
}, {}, "">;
|
|
42
|
-
type Dropzone = InstanceType<typeof Dropzone>;
|
|
41
|
+
declare const Dropzone: import("svelte").Component<FileDropZoneProps, {}, "">;
|
|
42
|
+
type Dropzone = ReturnType<typeof Dropzone>;
|
|
43
43
|
export default Dropzone;
|