Skip to content

fix: uui-file-dropzone does not follow the inferred interface for dropped files #478

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 12, 2023
81 changes: 32 additions & 49 deletions packages/uui-file-dropzone/lib/uui-file-dropzone.element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,18 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
return !dtItem.type ? dtItem.webkitGetAsEntry().isDirectory : false;
}

private async _getAllFileEntries(dataTransferItemList: DataTransferItemList) {
const fileEntries: FileSystemFileEntry[] = [];
private async _getAllFileEntries(
dataTransferItemList: DataTransferItemList
): Promise<File[]> {
const fileEntries: File[] = [];
// Use BFS to traverse entire directory/file structure
const queue = [];
for (let i = 0; i < dataTransferItemList.length; i++) {
queue.push(dataTransferItemList[i].webkitGetAsEntry());
}
const queue = [...dataTransferItemList];

const acceptList: string[] = [];
const wildcards: string[] = [];

// if the accept filer is set
if (this.accept) {
const acceptList: string[] = [];
const wildcards: string[] = [];

// Create the arrays defined above
this.accept.split(',').forEach(item => {
if (item.includes('*')) {
Expand All @@ -127,36 +126,23 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
acceptList.push(item.trim().toLowerCase());
}
});
}

while (queue.length > 0) {
const entry: FileSystemFileEntry = queue.shift()!;
if (
entry.isFile &&
(await this._isAccepted(acceptList, wildcards, entry))
) {
fileEntries.push(entry);
} else if (entry.isDirectory) {
fileEntries.push(entry);
queue.push(
...(await this._readAllDirectoryEntries(
(entry as any).createReader()
))
);
}
}
} else {
while (queue.length > 0) {
const entry: FileSystemFileEntry = queue.shift()!;
if (entry.isFile) {
fileEntries.push(entry);
} else if (entry.isDirectory) {
fileEntries.push(entry);
queue.push(
...(await this._readAllDirectoryEntries(
(entry as any).createReader()
))
);
while (queue.length > 0) {
const entry = queue.shift()!;

if (entry.kind === 'file') {
const file = entry.getAsFile();
if (!file) continue;
if (this._isAccepted(acceptList, wildcards, file)) {
fileEntries.push(file);
}
} else if (entry.kind === 'directory') {
if ('webkitGetAsEntry' in entry === false) continue;
const directory = entry.webkitGetAsEntry()! as FileSystemDirectoryEntry;
queue.push(
...(await this._readAllDirectoryEntries(directory.createReader()))
);
}
}

Expand Down Expand Up @@ -189,27 +175,22 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
}
}

private async _getFile(fileEntry: FileSystemFileEntry): Promise<File> {
return await new Promise<File>((resolve, reject) =>
fileEntry.file(resolve, reject)
);
}
private _isAccepted(acceptList: string[], wildcards: string[], file: File) {
if (acceptList.length === 0 && wildcards.length === 0) {
return true;
}

private async _isAccepted(
acceptList: string[],
wildcards: string[],
entry: FileSystemFileEntry
) {
const file = await this._getFile(entry);
const fileType = file.type.toLowerCase();
const fileExtension = '.' + file.name.split('.')[1].toLowerCase();

if (acceptList.includes(fileExtension)) {
return true;
}

if (acceptList.includes(fileType)) {
return true;
}

if (wildcards.some(wildcard => fileType.startsWith(wildcard))) {
return true;
}
Expand All @@ -226,7 +207,7 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
if (items) {
let result = await this._getAllFileEntries(items);

if (this.multiple === false && result.length > 0) {
if (this.multiple === false && result.length) {
result = [result[0]];
}

Expand All @@ -243,6 +224,7 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
}

private _onDragEnter(e: DragEvent) {
// TODO: make visual indication of wether the file is acceptable or not. If not we need to make a more negative/disabled visual look.
this._dropzone.classList.add('hover');
e.preventDefault();
}
Expand Down Expand Up @@ -275,6 +257,7 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
?multiple=${this.multiple}
@change=${this._onFileInputChange}
aria-label="${this.label}" />
<slot></slot>
</div>
`;
}
Expand Down
25 changes: 16 additions & 9 deletions packages/uui-file-dropzone/lib/uui-file-dropzone.story.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { StoryFn } from '@storybook/web-components';
import { Meta, StoryFn } from '@storybook/web-components';
import { html } from 'lit';
import { UUIFileDropzoneEvent } from './UUIFileDropzoneEvents';
import { UUIFileDropzoneElement } from './uui-file-dropzone.element';
import type { UUIFileDropzoneEvent } from './UUIFileDropzoneEvents';
import type { UUIFileDropzoneElement } from './uui-file-dropzone.element';

import '@umbraco-ui/uui-symbol-file-dropzone/lib';
import '.';
import './uui-file-dropzone.element';
import '@umbraco-ui/uui-button/lib/';

export default {
id: 'uui-file-dropzone',
Expand All @@ -17,7 +18,7 @@ export default {
</div>
${Story()}`,
],
};
} as Meta<UUIFileDropzoneElement>;

const handleFileChange = (e: UUIFileDropzoneEvent) =>
console.log('event.detail: ', e.detail);
Expand All @@ -33,14 +34,17 @@ export const AAAOverview: StoryFn = props => {
};
AAAOverview.storyName = 'Overview';

export const Multiple: StoryFn = () =>
export const Multiple: StoryFn = props =>
html`
<uui-file-dropzone
multiple
.multiple=${props.multiple}
@file-change=${handleFileChange}
label="Drop files here"></uui-file-dropzone>
`;

Multiple.args = {
multiple: true,
};
Multiple.parameters = {
docs: {
description: {
Expand All @@ -50,14 +54,17 @@ Multiple.parameters = {
},
};

export const Accept: StoryFn = () =>
export const Accept: StoryFn = props =>
html`
<uui-file-dropzone
accept="image/*"
.accept=${props.accept}
@file-change=${handleFileChange}
label="Drop files here"></uui-file-dropzone>
`;

Accept.args = {
accept: 'image/*',
};
Accept.parameters = {
docs: {
description: {
Expand Down