@functionalcms/svelte-components 2.30.0 → 2.33.1
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/css/functional.css +1 -2220
- package/css/functional.css.map +1 -1
- package/dist/components/files/Dropzone.svelte +366 -0
- package/dist/components/files/Dropzone.svelte.d.ts +82 -0
- package/dist/components/files/attr-accept.d.ts +1 -0
- package/dist/components/files/attr-accept.js +22 -0
- package/dist/components/files/file-selector.d.ts +15 -0
- package/dist/components/files/file-selector.js +167 -0
- package/dist/components/files/file.d.ts +10 -0
- package/dist/components/files/file.js +1258 -0
- package/dist/components/files/utils.d.ts +47 -0
- package/dist/components/files/utils.js +146 -0
- package/dist/components/layouts/DefaultLayout.svelte +1 -1
- package/dist/components/menu/ColumnMenu.svelte +3 -3
- package/dist/components/menu/FlatMenu.svelte +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +2 -2
package/css/functional.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sourceRoot":"","sources":["../
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["../src/lib/css/common.scss","../src/lib/css/opinions.scss","../src/lib/css/menu.scss","../src/lib/css/logo.scss","../src/lib/css/displays.scss","../node_modules/@splidejs/svelte-splide/css/splide.min.css"],"names":[],"mappings":"AAAA,aACI,sDACA,+CACA,8CACA,6CACA,gDACA,0CACA,oDACA,8CACA,6CACA,+CACA,8CACA,wCACA,mCACA,kCACA,0CACA,2CACA,yCACA,2CACA,iDACA,6CACA,4CACA,2CACA,sCACA,8CACA,6CACA,4CACA,6CACA,uCACA,6EACA,4EAGJ,aACI,qDACA,8CACA,6CACA,4CACA,4CACA,yCACA,mDACA,6CACA,4CACA,8CACA,6CACA,uCACA,qCACA,oCACA,wDACA,6DACA,uDACA,yDACA,+DACA,4CACA,2CACA,0CACA,qCACA,6CACA,4CACA,2CACA,4CACA,sCACA,2EACA,0EAGJ,MACI,mBACA,sFACA,wEACA,sEACA,oEACA,0EACA,8DACA,sEACA,oEACA,wEACA,sEACA,0DACA,sDACA,oDACA,8DACA,sEACA,4DACA,gEACA,4EACA,sDACA,kEACA,gEACA,oEACA,mEACA,iEACA,sEACA,oEACA,kEACA,oEACA,yDAGJ,mCACI,MACI,kBACA,qFACA,uEACA,qEACA,mEACA,yEACA,6DACA,qEACA,mEACA,uEACA,qEACA,yDACA,qDACA,mDACA,6DACA,qEACA,2DACA,+DACA,2EACA,qDACA,iEACA,+DACA,mEACA,kEACA,gEACA,qEACA,mEACA,iEACA,mEACA,yDAIR,qBACI,mBACA,sFACA,wEACA,sEACA,oEACA,0EACA,8DACA,sEACA,oEACA,wEACA,sEACA,0DACA,sDACA,oDACA,8DACA,sEACA,4DACA,gEACA,4EACA,sDACA,kEACA,gEACA,oEACA,mEACA,iEACA,sEACA,oEACA,kEACA,oEACA,yDAGJ,oBACI,kBACA,qFACA,uEACA,qEACA,mEACA,yEACA,6DACA,qEACA,mEACA,uEACA,qEACA,yDACA,qDACA,mDACA,6DACA,qEACA,2DACA,+DACA,2EACA,qDACA,iEACA,+DACA,mEACA,kEACA,gEACA,qEACA,mEACA,iEACA,mEACA,wDAGJ,aACI,mDACA,6CACA,2CACA,sDAGJ,aACI,iBACA,mBACA,iBACA,mBACA,iBACA,mBACA,oBACA,i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","file":"functional.css"}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { fromEvent } from "./file-selector.js";
|
|
3
|
+
import {
|
|
4
|
+
fileAccepted,
|
|
5
|
+
fileMatchSize,
|
|
6
|
+
isEvtWithFiles,
|
|
7
|
+
isIeOrEdge,
|
|
8
|
+
isPropagationStopped,
|
|
9
|
+
TOO_MANY_FILES_REJECTION
|
|
10
|
+
} from "./utils.js";
|
|
11
|
+
import { onMount, onDestroy, createEventDispatcher } from "svelte";
|
|
12
|
+
|
|
13
|
+
//props
|
|
14
|
+
/**
|
|
15
|
+
* Set accepted file types.
|
|
16
|
+
* See https://github.com/okonet/attr-accept for more information.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* @type {string | Array<string>}
|
|
20
|
+
*/
|
|
21
|
+
export let accept = undefined;
|
|
22
|
+
export let disabled = false;
|
|
23
|
+
export let getFilesFromEvent = fromEvent;
|
|
24
|
+
export let maxSize = Infinity;
|
|
25
|
+
export let minSize = 0;
|
|
26
|
+
export let multiple = true;
|
|
27
|
+
export let preventDropOnDocument = true;
|
|
28
|
+
export let noClick = false;
|
|
29
|
+
export let noKeyboard = false;
|
|
30
|
+
export let noDrag = false;
|
|
31
|
+
export let noDragEventsBubbling = false;
|
|
32
|
+
export let containerClasses = "";
|
|
33
|
+
export let containerStyles = "";
|
|
34
|
+
export let disableDefaultStyles = false;
|
|
35
|
+
export let name = "";
|
|
36
|
+
export let inputElement = undefined;
|
|
37
|
+
export let required = false;
|
|
38
|
+
const dispatch = createEventDispatcher();
|
|
39
|
+
|
|
40
|
+
//state
|
|
41
|
+
|
|
42
|
+
let state = {
|
|
43
|
+
isFocused: false,
|
|
44
|
+
isFileDialogActive: false,
|
|
45
|
+
isDragActive: false,
|
|
46
|
+
isDragAccept: false,
|
|
47
|
+
isDragReject: false,
|
|
48
|
+
draggedFiles: [],
|
|
49
|
+
acceptedFiles: [],
|
|
50
|
+
fileRejections: []
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
let rootRef;
|
|
54
|
+
|
|
55
|
+
function resetState() {
|
|
56
|
+
state.isFileDialogActive = false;
|
|
57
|
+
state.isDragActive = false;
|
|
58
|
+
state.draggedFiles = [];
|
|
59
|
+
state.acceptedFiles = [];
|
|
60
|
+
state.fileRejections = [];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Fn for opening the file dialog programmatically
|
|
64
|
+
function openFileDialog() {
|
|
65
|
+
if (inputElement) {
|
|
66
|
+
inputElement.value = null; // TODO check if null needs to be set
|
|
67
|
+
state.isFileDialogActive = true;
|
|
68
|
+
inputElement.click();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
|
|
73
|
+
function onKeyDownCb(event) {
|
|
74
|
+
// Ignore keyboard events bubbling up the DOM tree
|
|
75
|
+
if (!rootRef || !rootRef.isEqualNode(event.target)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (event.keyCode === 32 || event.keyCode === 13) {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
openFileDialog();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Update focus state for the dropzone
|
|
86
|
+
function onFocusCb() {
|
|
87
|
+
state.isFocused = true;
|
|
88
|
+
}
|
|
89
|
+
function onBlurCb() {
|
|
90
|
+
state.isFocused = false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Cb to open the file dialog when click occurs on the dropzone
|
|
94
|
+
function onClickCb() {
|
|
95
|
+
if (noClick) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout()
|
|
100
|
+
// to ensure React can handle state changes
|
|
101
|
+
// See: https://github.com/react-dropzone/react-dropzone/issues/450
|
|
102
|
+
if (isIeOrEdge()) {
|
|
103
|
+
setTimeout(openFileDialog, 0);
|
|
104
|
+
} else {
|
|
105
|
+
openFileDialog();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function onDragEnterCb(event) {
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
stopPropagation(event);
|
|
112
|
+
|
|
113
|
+
dragTargetsRef = [...dragTargetsRef, event.target];
|
|
114
|
+
|
|
115
|
+
if (isEvtWithFiles(event)) {
|
|
116
|
+
Promise.resolve(getFilesFromEvent(event)).then(draggedFiles => {
|
|
117
|
+
if (isPropagationStopped(event) && !noDragEventsBubbling) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
state.draggedFiles = draggedFiles;
|
|
122
|
+
state.isDragActive = true;
|
|
123
|
+
|
|
124
|
+
dispatch("dragenter", {
|
|
125
|
+
dragEvent: event
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function onDragOverCb(event) {
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
stopPropagation(event);
|
|
134
|
+
|
|
135
|
+
if (event.dataTransfer) {
|
|
136
|
+
try {
|
|
137
|
+
event.dataTransfer.dropEffect = "copy";
|
|
138
|
+
} catch {} /* eslint-disable-line no-empty */
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (isEvtWithFiles(event)) {
|
|
142
|
+
dispatch("dragover", {
|
|
143
|
+
dragEvent: event
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function onDragLeaveCb(event) {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
stopPropagation(event);
|
|
153
|
+
|
|
154
|
+
// Only deactivate once the dropzone and all children have been left
|
|
155
|
+
const targets = dragTargetsRef.filter(target => rootRef && rootRef.contains(target));
|
|
156
|
+
// Make sure to remove a target present multiple times only once
|
|
157
|
+
// (Firefox may fire dragenter/dragleave multiple times on the same element)
|
|
158
|
+
const targetIdx = targets.indexOf(event.target);
|
|
159
|
+
if (targetIdx !== -1) {
|
|
160
|
+
targets.splice(targetIdx, 1);
|
|
161
|
+
}
|
|
162
|
+
dragTargetsRef = targets;
|
|
163
|
+
if (targets.length > 0) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
state.isDragActive = false;
|
|
168
|
+
state.draggedFiles = [];
|
|
169
|
+
|
|
170
|
+
if (isEvtWithFiles(event)) {
|
|
171
|
+
dispatch("dragleave", {
|
|
172
|
+
dragEvent: event
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function onDropCb(event) {
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
stopPropagation(event);
|
|
180
|
+
|
|
181
|
+
dragTargetsRef = [];
|
|
182
|
+
|
|
183
|
+
if (isEvtWithFiles(event)) {
|
|
184
|
+
dispatch("filedropped", {
|
|
185
|
+
event
|
|
186
|
+
});
|
|
187
|
+
Promise.resolve(getFilesFromEvent(event)).then(files => {
|
|
188
|
+
if (isPropagationStopped(event) && !noDragEventsBubbling) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const acceptedFiles = [];
|
|
193
|
+
const fileRejections = [];
|
|
194
|
+
|
|
195
|
+
files.forEach(file => {
|
|
196
|
+
const [accepted, acceptError] = fileAccepted(file, accept);
|
|
197
|
+
const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize);
|
|
198
|
+
if (accepted && sizeMatch) {
|
|
199
|
+
acceptedFiles.push(file);
|
|
200
|
+
} else {
|
|
201
|
+
const errors = [acceptError, sizeError].filter(e => e);
|
|
202
|
+
fileRejections.push({ file, errors });
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (!multiple && acceptedFiles.length > 1) {
|
|
207
|
+
// Reject everything and empty accepted files
|
|
208
|
+
acceptedFiles.forEach(file => {
|
|
209
|
+
fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] });
|
|
210
|
+
});
|
|
211
|
+
acceptedFiles.splice(0);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Files dropped keep input in sync
|
|
215
|
+
if (event.dataTransfer) {
|
|
216
|
+
inputElement.files = event.dataTransfer.files;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
state.acceptedFiles = acceptedFiles;
|
|
220
|
+
state.fileRejections = fileRejections;
|
|
221
|
+
|
|
222
|
+
dispatch("drop", {
|
|
223
|
+
acceptedFiles,
|
|
224
|
+
fileRejections,
|
|
225
|
+
event
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
if (fileRejections.length > 0) {
|
|
229
|
+
dispatch("droprejected", {
|
|
230
|
+
fileRejections,
|
|
231
|
+
event
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (acceptedFiles.length > 0) {
|
|
236
|
+
dispatch("dropaccepted", {
|
|
237
|
+
acceptedFiles,
|
|
238
|
+
event
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
resetState();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
$: composeHandler = fn => (disabled ? null : fn);
|
|
247
|
+
|
|
248
|
+
$: composeKeyboardHandler = fn => (noKeyboard ? null : composeHandler(fn));
|
|
249
|
+
|
|
250
|
+
$: composeDragHandler = fn => (noDrag ? null : composeHandler(fn));
|
|
251
|
+
|
|
252
|
+
$: defaultPlaceholderString = multiple
|
|
253
|
+
? "Drag 'n' drop some files here, or click to select files"
|
|
254
|
+
: "Drag 'n' drop a file here, or click to select a file";
|
|
255
|
+
|
|
256
|
+
function stopPropagation(event) {
|
|
257
|
+
if (noDragEventsBubbling) {
|
|
258
|
+
event.stopPropagation();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// allow the entire document to be a drag target
|
|
263
|
+
function onDocumentDragOver(event) {
|
|
264
|
+
if (preventDropOnDocument) {
|
|
265
|
+
event.preventDefault();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
let dragTargetsRef = [];
|
|
270
|
+
function onDocumentDrop(event) {
|
|
271
|
+
if (!preventDropOnDocument) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (rootRef && rootRef.contains(event.target)) {
|
|
275
|
+
// If we intercepted an event for our instance, let it propagate down to the instance's onDrop handler
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
event.preventDefault();
|
|
279
|
+
dragTargetsRef = [];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Update file dialog active state when the window is focused on
|
|
283
|
+
function onWindowFocus() {
|
|
284
|
+
// Execute the timeout only if the file dialog is opened in the browser
|
|
285
|
+
if (state.isFileDialogActive) {
|
|
286
|
+
setTimeout(() => {
|
|
287
|
+
if (inputElement) {
|
|
288
|
+
const { files } = inputElement;
|
|
289
|
+
|
|
290
|
+
if (!files.length) {
|
|
291
|
+
state.isFileDialogActive = false;
|
|
292
|
+
dispatch("filedialogcancel");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}, 300);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
onDestroy(() => {
|
|
300
|
+
// This is critical for canceling the timeout behaviour on `onWindowFocus()`
|
|
301
|
+
inputElement = null;
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
function onInputElementClick(event) {
|
|
305
|
+
event.stopPropagation();
|
|
306
|
+
}
|
|
307
|
+
</script>
|
|
308
|
+
|
|
309
|
+
<svelte:window on:focus={onWindowFocus} on:dragover={onDocumentDragOver} on:drop={onDocumentDrop} />
|
|
310
|
+
|
|
311
|
+
<div
|
|
312
|
+
bind:this={rootRef}
|
|
313
|
+
tabindex="0"
|
|
314
|
+
role="button"
|
|
315
|
+
class="{disableDefaultStyles ? '' : 'dropzone'}
|
|
316
|
+
{containerClasses}"
|
|
317
|
+
style={containerStyles}
|
|
318
|
+
on:keydown={composeKeyboardHandler(onKeyDownCb)}
|
|
319
|
+
on:focus={composeKeyboardHandler(onFocusCb)}
|
|
320
|
+
on:blur={composeKeyboardHandler(onBlurCb)}
|
|
321
|
+
on:click={composeHandler(onClickCb)}
|
|
322
|
+
on:dragenter={composeDragHandler(onDragEnterCb)}
|
|
323
|
+
on:dragover={composeDragHandler(onDragOverCb)}
|
|
324
|
+
on:dragleave={composeDragHandler(onDragLeaveCb)}
|
|
325
|
+
on:drop={composeDragHandler(onDropCb)}
|
|
326
|
+
{...$$restProps}
|
|
327
|
+
>
|
|
328
|
+
<input
|
|
329
|
+
accept={accept?.toString()}
|
|
330
|
+
{multiple}
|
|
331
|
+
{required}
|
|
332
|
+
type="file"
|
|
333
|
+
{name}
|
|
334
|
+
autocomplete="off"
|
|
335
|
+
tabindex="-1"
|
|
336
|
+
on:change={onDropCb}
|
|
337
|
+
on:click={onInputElementClick}
|
|
338
|
+
bind:this={inputElement}
|
|
339
|
+
style="display: none;"
|
|
340
|
+
/>
|
|
341
|
+
<slot>
|
|
342
|
+
<p>{defaultPlaceholderString}</p>
|
|
343
|
+
</slot>
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
<style>
|
|
347
|
+
.dropzone {
|
|
348
|
+
flex: 1;
|
|
349
|
+
display: flex;
|
|
350
|
+
flex-direction: column;
|
|
351
|
+
align-items: center;
|
|
352
|
+
padding: 20px;
|
|
353
|
+
border-width: 2px;
|
|
354
|
+
border-radius: 2px;
|
|
355
|
+
border-color: #eeeeee;
|
|
356
|
+
border-style: dashed;
|
|
357
|
+
background-color: #fafafa;
|
|
358
|
+
color: #bdbdbd;
|
|
359
|
+
outline: none;
|
|
360
|
+
transition: border 0.24s ease-in-out;
|
|
361
|
+
}
|
|
362
|
+
.dropzone:focus {
|
|
363
|
+
border-color: #2196f3;
|
|
364
|
+
}
|
|
365
|
+
</style>
|
|
366
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** @typedef {typeof __propDef.props} DropzoneProps */
|
|
2
|
+
/** @typedef {typeof __propDef.events} DropzoneEvents */
|
|
3
|
+
/** @typedef {typeof __propDef.slots} DropzoneSlots */
|
|
4
|
+
export default class Dropzone extends SvelteComponent<{
|
|
5
|
+
[x: string]: any;
|
|
6
|
+
disabled?: boolean | undefined;
|
|
7
|
+
name?: string | undefined;
|
|
8
|
+
accept?: string | string[] | undefined;
|
|
9
|
+
minSize?: number | undefined;
|
|
10
|
+
maxSize?: number | undefined;
|
|
11
|
+
multiple?: boolean | undefined;
|
|
12
|
+
getFilesFromEvent?: typeof fromEvent | undefined;
|
|
13
|
+
preventDropOnDocument?: boolean | undefined;
|
|
14
|
+
noClick?: boolean | undefined;
|
|
15
|
+
noKeyboard?: boolean | undefined;
|
|
16
|
+
noDrag?: boolean | undefined;
|
|
17
|
+
noDragEventsBubbling?: boolean | undefined;
|
|
18
|
+
containerClasses?: string | undefined;
|
|
19
|
+
containerStyles?: string | undefined;
|
|
20
|
+
disableDefaultStyles?: boolean | undefined;
|
|
21
|
+
inputElement?: any;
|
|
22
|
+
required?: boolean | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
dragenter: CustomEvent<any>;
|
|
25
|
+
dragover: CustomEvent<any>;
|
|
26
|
+
dragleave: CustomEvent<any>;
|
|
27
|
+
filedropped: CustomEvent<any>;
|
|
28
|
+
drop: CustomEvent<any>;
|
|
29
|
+
droprejected: CustomEvent<any>;
|
|
30
|
+
dropaccepted: CustomEvent<any>;
|
|
31
|
+
filedialogcancel: CustomEvent<any>;
|
|
32
|
+
} & {
|
|
33
|
+
[evt: string]: CustomEvent<any>;
|
|
34
|
+
}, {
|
|
35
|
+
default: {};
|
|
36
|
+
}> {
|
|
37
|
+
}
|
|
38
|
+
export type DropzoneProps = typeof __propDef.props;
|
|
39
|
+
export type DropzoneEvents = typeof __propDef.events;
|
|
40
|
+
export type DropzoneSlots = typeof __propDef.slots;
|
|
41
|
+
import { fromEvent } from "./file-selector.js";
|
|
42
|
+
import { SvelteComponent } from "svelte";
|
|
43
|
+
declare const __propDef: {
|
|
44
|
+
props: {
|
|
45
|
+
[x: string]: any;
|
|
46
|
+
disabled?: boolean | undefined;
|
|
47
|
+
name?: string | undefined;
|
|
48
|
+
accept?: string | string[] | undefined;
|
|
49
|
+
minSize?: number | undefined;
|
|
50
|
+
maxSize?: number | undefined;
|
|
51
|
+
multiple?: boolean | undefined;
|
|
52
|
+
getFilesFromEvent?: typeof fromEvent | undefined;
|
|
53
|
+
preventDropOnDocument?: boolean | undefined;
|
|
54
|
+
noClick?: boolean | undefined;
|
|
55
|
+
noKeyboard?: boolean | undefined;
|
|
56
|
+
noDrag?: boolean | undefined;
|
|
57
|
+
noDragEventsBubbling?: boolean | undefined;
|
|
58
|
+
containerClasses?: string | undefined;
|
|
59
|
+
containerStyles?: string | undefined;
|
|
60
|
+
disableDefaultStyles?: boolean | undefined;
|
|
61
|
+
inputElement?: any;
|
|
62
|
+
required?: boolean | undefined;
|
|
63
|
+
};
|
|
64
|
+
events: {
|
|
65
|
+
dragenter: CustomEvent<any>;
|
|
66
|
+
dragover: CustomEvent<any>;
|
|
67
|
+
dragleave: CustomEvent<any>;
|
|
68
|
+
filedropped: CustomEvent<any>;
|
|
69
|
+
drop: CustomEvent<any>;
|
|
70
|
+
droprejected: CustomEvent<any>;
|
|
71
|
+
dropaccepted: CustomEvent<any>;
|
|
72
|
+
filedialogcancel: CustomEvent<any>;
|
|
73
|
+
} & {
|
|
74
|
+
[evt: string]: CustomEvent<any>;
|
|
75
|
+
};
|
|
76
|
+
slots: {
|
|
77
|
+
default: {};
|
|
78
|
+
};
|
|
79
|
+
exports?: undefined;
|
|
80
|
+
bindings?: undefined;
|
|
81
|
+
};
|
|
82
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function _default(file: any, acceptedFiles: any): any;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export default function(file, acceptedFiles) {
|
|
2
|
+
if (file && acceptedFiles) {
|
|
3
|
+
const acceptedFilesArray = Array.isArray(acceptedFiles)
|
|
4
|
+
? acceptedFiles
|
|
5
|
+
: acceptedFiles.split(",");
|
|
6
|
+
const fileName = file.name || "";
|
|
7
|
+
const mimeType = (file.type || "").toLowerCase();
|
|
8
|
+
const baseMimeType = mimeType.replace(/\/.*$/, "");
|
|
9
|
+
|
|
10
|
+
return acceptedFilesArray.some((type) => {
|
|
11
|
+
const validType = type.trim().toLowerCase();
|
|
12
|
+
if (validType.charAt(0) === ".") {
|
|
13
|
+
return fileName.toLowerCase().endsWith(validType);
|
|
14
|
+
} else if (validType.endsWith("/*")) {
|
|
15
|
+
// This is something like a image/* mime type
|
|
16
|
+
return baseMimeType === validType.replace(/\/.*$/, "");
|
|
17
|
+
}
|
|
18
|
+
return mimeType === validType;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copied from https://github.com/react-dropzone/file-selector/tree/master
|
|
3
|
+
*/
|
|
4
|
+
import { FileWithPath } from './file.js';
|
|
5
|
+
/**
|
|
6
|
+
* Convert a DragEvent's DataTrasfer object to a list of File objects
|
|
7
|
+
* NOTE: If some of the items are folders,
|
|
8
|
+
* everything will be flattened and placed in the same list but the paths will be kept as a {path} property.
|
|
9
|
+
*
|
|
10
|
+
* EXPERIMENTAL: A list of https://developer.mozilla.org/en-US/docs/Web/API/FileSystemHandle objects can also be passed as an arg
|
|
11
|
+
* and a list of File objects will be returned.
|
|
12
|
+
*
|
|
13
|
+
* @param evt
|
|
14
|
+
*/
|
|
15
|
+
export declare function fromEvent(evt: Event | any): Promise<(FileWithPath | DataTransferItem)[]>;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copied from https://github.com/react-dropzone/file-selector/tree/master
|
|
3
|
+
*/
|
|
4
|
+
import { FileWithPath, toFileWithPath } from './file.js';
|
|
5
|
+
const FILES_TO_IGNORE = [
|
|
6
|
+
// Thumbnail cache files for macOS and Windows
|
|
7
|
+
'.DS_Store', // macOs
|
|
8
|
+
'Thumbs.db' // Windows
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Convert a DragEvent's DataTrasfer object to a list of File objects
|
|
12
|
+
* NOTE: If some of the items are folders,
|
|
13
|
+
* everything will be flattened and placed in the same list but the paths will be kept as a {path} property.
|
|
14
|
+
*
|
|
15
|
+
* EXPERIMENTAL: A list of https://developer.mozilla.org/en-US/docs/Web/API/FileSystemHandle objects can also be passed as an arg
|
|
16
|
+
* and a list of File objects will be returned.
|
|
17
|
+
*
|
|
18
|
+
* @param evt
|
|
19
|
+
*/
|
|
20
|
+
export async function fromEvent(evt) {
|
|
21
|
+
if (isObject(evt) && isDataTransfer(evt.dataTransfer)) {
|
|
22
|
+
return getDataTransferFiles(evt.dataTransfer, evt.type);
|
|
23
|
+
}
|
|
24
|
+
else if (isChangeEvt(evt)) {
|
|
25
|
+
return getInputFiles(evt);
|
|
26
|
+
}
|
|
27
|
+
else if (Array.isArray(evt) && evt.every(item => 'getFile' in item && typeof item.getFile === 'function')) {
|
|
28
|
+
return getFsHandleFiles(evt);
|
|
29
|
+
}
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
function isDataTransfer(value) {
|
|
33
|
+
return isObject(value);
|
|
34
|
+
}
|
|
35
|
+
function isChangeEvt(value) {
|
|
36
|
+
return isObject(value) && isObject(value.target);
|
|
37
|
+
}
|
|
38
|
+
function isObject(v) {
|
|
39
|
+
return typeof v === 'object' && v !== null;
|
|
40
|
+
}
|
|
41
|
+
function getInputFiles(evt) {
|
|
42
|
+
return fromList(evt.target.files).map(file => toFileWithPath(file));
|
|
43
|
+
}
|
|
44
|
+
// Ee expect each handle to be https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle
|
|
45
|
+
async function getFsHandleFiles(handles) {
|
|
46
|
+
const files = await Promise.all(handles.map(h => h.getFile()));
|
|
47
|
+
return files.map(file => toFileWithPath(file));
|
|
48
|
+
}
|
|
49
|
+
async function getDataTransferFiles(dt, type) {
|
|
50
|
+
// IE11 does not support dataTransfer.items
|
|
51
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/items#Browser_compatibility
|
|
52
|
+
if (dt.items) {
|
|
53
|
+
const items = fromList(dt.items)
|
|
54
|
+
.filter(item => item.kind === 'file');
|
|
55
|
+
// According to https://html.spec.whatwg.org/multipage/dnd.html#dndevents,
|
|
56
|
+
// only 'dragstart' and 'drop' has access to the data (source node)
|
|
57
|
+
if (type !== 'drop') {
|
|
58
|
+
return items;
|
|
59
|
+
}
|
|
60
|
+
const files = await Promise.all(items.map(toFilePromises));
|
|
61
|
+
return noIgnoredFiles(flatten(files));
|
|
62
|
+
}
|
|
63
|
+
return noIgnoredFiles(fromList(dt.files)
|
|
64
|
+
.map(file => toFileWithPath(file)));
|
|
65
|
+
}
|
|
66
|
+
function noIgnoredFiles(files) {
|
|
67
|
+
return files.filter(file => FILES_TO_IGNORE.indexOf(file.name) === -1);
|
|
68
|
+
}
|
|
69
|
+
// IE11 does not support Array.from()
|
|
70
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility
|
|
71
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/FileList
|
|
72
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItemList
|
|
73
|
+
function fromList(items) {
|
|
74
|
+
if (items === null) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const files = [];
|
|
78
|
+
// tslint:disable: prefer-for-of
|
|
79
|
+
for (let i = 0; i < items.length; i++) {
|
|
80
|
+
const file = items[i];
|
|
81
|
+
files.push(file);
|
|
82
|
+
}
|
|
83
|
+
return files;
|
|
84
|
+
}
|
|
85
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem
|
|
86
|
+
function toFilePromises(item) {
|
|
87
|
+
if (typeof item.webkitGetAsEntry !== 'function') {
|
|
88
|
+
return fromDataTransferItem(item);
|
|
89
|
+
}
|
|
90
|
+
const entry = item.webkitGetAsEntry();
|
|
91
|
+
// Safari supports dropping an image node from a different window and can be retrieved using
|
|
92
|
+
// the DataTransferItem.getAsFile() API
|
|
93
|
+
// NOTE: FileSystemEntry.file() throws if trying to get the file
|
|
94
|
+
if (entry && entry.isDirectory) {
|
|
95
|
+
return fromDirEntry(entry);
|
|
96
|
+
}
|
|
97
|
+
return fromDataTransferItem(item, entry);
|
|
98
|
+
}
|
|
99
|
+
function flatten(items) {
|
|
100
|
+
return items.reduce((acc, files) => [
|
|
101
|
+
...acc,
|
|
102
|
+
...(Array.isArray(files) ? flatten(files) : [files])
|
|
103
|
+
], []);
|
|
104
|
+
}
|
|
105
|
+
function fromDataTransferItem(item, entry) {
|
|
106
|
+
if (typeof item.getAsFileSystemHandle === 'function') {
|
|
107
|
+
return item.getAsFileSystemHandle()
|
|
108
|
+
.then(async (h) => {
|
|
109
|
+
const file = await h.getFile();
|
|
110
|
+
file.handle = h;
|
|
111
|
+
return toFileWithPath(file);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const file = item.getAsFile();
|
|
115
|
+
if (!file) {
|
|
116
|
+
return Promise.reject(`${item} is not a File`);
|
|
117
|
+
}
|
|
118
|
+
const fwp = toFileWithPath(file, entry?.fullPath ?? undefined);
|
|
119
|
+
return Promise.resolve(fwp);
|
|
120
|
+
}
|
|
121
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemEntry
|
|
122
|
+
async function fromEntry(entry) {
|
|
123
|
+
return entry.isDirectory ? fromDirEntry(entry) : fromFileEntry(entry);
|
|
124
|
+
}
|
|
125
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryEntry
|
|
126
|
+
function fromDirEntry(entry) {
|
|
127
|
+
const reader = entry.createReader();
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
const entries = [];
|
|
130
|
+
function readEntries() {
|
|
131
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryEntry/createReader
|
|
132
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryReader/readEntries
|
|
133
|
+
reader.readEntries(async (batch) => {
|
|
134
|
+
if (!batch.length) {
|
|
135
|
+
// Done reading directory
|
|
136
|
+
try {
|
|
137
|
+
const files = await Promise.all(entries);
|
|
138
|
+
resolve(files);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
reject(err);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
const items = Promise.all(batch.map(fromEntry));
|
|
146
|
+
entries.push(items);
|
|
147
|
+
// Continue reading
|
|
148
|
+
readEntries();
|
|
149
|
+
}
|
|
150
|
+
}, (err) => {
|
|
151
|
+
reject(err);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
readEntries();
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileEntry
|
|
158
|
+
async function fromFileEntry(entry) {
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
entry.file((file) => {
|
|
161
|
+
const fwp = toFileWithPath(file, entry.fullPath);
|
|
162
|
+
resolve(fwp);
|
|
163
|
+
}, (err) => {
|
|
164
|
+
reject(err);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|