Skip to content

Instantly share code, notes, and snippets.

@wobsoriano
Last active July 17, 2021 14:17
Show Gist options
  • Save wobsoriano/7911558ba28bdcb7336dc5fb71e73a2c to your computer and use it in GitHub Desktop.
Save wobsoriano/7911558ba28bdcb7336dc5fb71e73a2c to your computer and use it in GitHub Desktop.
Vue 3 File Picker composable
export default function openFileDialog(
accept: string,
multiple: boolean,
callback: (arg: Event) => void
) {
// this function must be called from a user
// activation event (ie an onclick event)
// Create an input element
const inputElement = document.createElement('input');
// Set its type to file
inputElement.type = 'file';
// Set accept to the file types you want the user to select.
// Include both the file extension and the mime type
inputElement.accept = accept;
// Accept multiple files
inputElement.multiple = multiple;
// set onchange event to call callback when user has selected file
inputElement.addEventListener('change', callback);
// dispatch a click event to open the file dialog
inputElement.dispatchEvent(new MouseEvent('click'));
}
import { ref, watch } from 'vue';
import { FileWithPath, fromEvent } from 'file-selector';
import openFileDialog from '~/utils/open-file-dialog';
export type ReadType = 'Text' | 'BinaryString' | 'ArrayBuffer' | 'DataURL';
export type ReaderMethod = keyof FileReader;
export interface UseFilePickerConfig {
multiple?: boolean;
accept?: string | string[];
readAs?: ReadType;
}
export interface FileContent {
lastModified: number;
name: string;
content: string;
}
export const useFilePicker = ({
multiple = false,
accept = '*',
readAs = 'Text',
}: UseFilePickerConfig) => {
const files = ref<FileWithPath[]>([]);
const filesContent = ref<FileContent[]>([]);
const fileErrors = ref<DOMException[]>([]);
const isLoading = ref(false);
const openFileSelector = () => {
const fileExtensions = accept instanceof Array ? accept.join(',') : accept;
openFileDialog(fileExtensions, multiple, async (evt) => {
clear();
files.value = (await fromEvent(evt)) as FileWithPath[];
});
};
const clear = () => {
files.value = [];
filesContent.value = [];
fileErrors.value = [];
};
watch(files, async (value) => {
if (!value.length) {
filesContent.value = [];
return;
}
isLoading.value = true;
const fileParsingPromises = value.map(
(file: FileWithPath) =>
new Promise(
(
resolve: (fileContent: FileContent) => void,
reject: (reason: DOMException | null) => void
) => {
const reader = new FileReader();
const readStrategy = reader[
`readAs${readAs}` as ReaderMethod
] as typeof reader.readAsText;
readStrategy.call(reader, file);
reader.onload = () => {
resolve({
content: reader.result,
name: file.name,
lastModified: file.lastModified,
} as FileContent);
};
reader.onerror = () => {
reject(reader.error);
};
}
)
);
try {
filesContent.value = await Promise.all(fileParsingPromises);
} catch (e) {
fileErrors.value = e;
} finally {
isLoading.value = false;
}
});
return {
isLoading,
openFileSelector,
filesContent,
files,
clear,
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment