Last active
          July 17, 2021 14:17 
        
      - 
      
- 
        Save wobsoriano/7911558ba28bdcb7336dc5fb71e73a2c to your computer and use it in GitHub Desktop. 
    Vue 3 File Picker composable
  
        
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | 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')); | |
| } | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | 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