feat(c-file-upload): paste image
Property paste-image to allow pasting an image directly from clipboard
This commit is contained in:
		
							parent
							
								
									6d619603f9
								
							
						
					
					
						commit
						dbd4730256
					
				| @ -5,10 +5,12 @@ const props = withDefaults(defineProps<{ | ||||
|   multiple?: boolean | ||||
|   accept?: string | ||||
|   title?: string | ||||
|   pasteImage?: boolean | ||||
| }>(), { | ||||
|   multiple: false, | ||||
|   accept: undefined, | ||||
|   title: 'Drag and drop files here, or click to select files', | ||||
|   pasteImage: false, | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
| @ -16,11 +18,31 @@ const emit = defineEmits<{ | ||||
|   (event: 'fileUpload', file: File): void | ||||
| }>(); | ||||
| 
 | ||||
| const { multiple } = toRefs(props); | ||||
| const { multiple, pasteImage } = toRefs(props); | ||||
| 
 | ||||
| const isOverDropZone = ref(false); | ||||
| 
 | ||||
| function toBase64(file: File) { | ||||
|   return new Promise<string>((resolve, reject) => { | ||||
|     const reader = new FileReader(); | ||||
|     reader.readAsDataURL(file); | ||||
|     reader.onload = () => resolve(reader.result?.toString() ?? ''); | ||||
|     reader.onerror = error => reject(error); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| const fileInput = ref<HTMLInputElement | null>(null); | ||||
| const imgPreview = ref<HTMLImageElement | null>(null); | ||||
| async function handlePreview(image: File) { | ||||
|   if (imgPreview.value) { | ||||
|     imgPreview.value.src = await toBase64(image); | ||||
|   } | ||||
| } | ||||
| function clearPreview() { | ||||
|   if (imgPreview.value) { | ||||
|     imgPreview.value.src = ''; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function triggerFileInput() { | ||||
|   fileInput.value?.click(); | ||||
| @ -39,7 +61,30 @@ function handleDrop(event: DragEvent) { | ||||
|   handleUpload(files); | ||||
| } | ||||
| 
 | ||||
| function handleUpload(files: FileList | null | undefined) { | ||||
| async function onPasteImage(evt: ClipboardEvent) { | ||||
|   if (!pasteImage.value) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   const items = evt.clipboardData?.items; | ||||
|   if (!items) { | ||||
|     return false; | ||||
|   } | ||||
|   for (let i = 0; i < items.length; i++) { | ||||
|     if (items[i].type.includes('image')) { | ||||
|       const imageFile = items[i].getAsFile(); | ||||
|       if (imageFile) { | ||||
|         await handlePreview(imageFile); | ||||
|         emit('fileUpload', imageFile); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| async function handleUpload(files: FileList | null | undefined) { | ||||
|   clearPreview(); | ||||
| 
 | ||||
|   if (_.isNil(files) || _.isEmpty(files)) { | ||||
|     return; | ||||
|   } | ||||
| @ -49,6 +94,7 @@ function handleUpload(files: FileList | null | undefined) { | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   await handlePreview(files[0]); | ||||
|   emit('fileUpload', files[0]); | ||||
| } | ||||
| </script> | ||||
| @ -60,6 +106,7 @@ function handleUpload(files: FileList | null | undefined) { | ||||
|       'border-primary border-opacity-100': isOverDropZone, | ||||
|     }" | ||||
|     @click="triggerFileInput" | ||||
|     @paste.prevent="onPasteImage" | ||||
|     @drop.prevent="handleDrop" | ||||
|     @dragover.prevent | ||||
|     @dragenter="isOverDropZone = true" | ||||
| @ -73,6 +120,7 @@ function handleUpload(files: FileList | null | undefined) { | ||||
|       :accept="accept" | ||||
|       @change="handleFileInput" | ||||
|     > | ||||
| 
 | ||||
|     <slot> | ||||
|       <span op-70> | ||||
|         {{ title }} | ||||
| @ -90,6 +138,22 @@ function handleUpload(files: FileList | null | undefined) { | ||||
|       <c-button> | ||||
|         Browse files | ||||
|       </c-button> | ||||
| 
 | ||||
|       <div v-if="pasteImage"> | ||||
|         <!-- separator --> | ||||
|         <div my-4 w-full flex items-center justify-center op-70> | ||||
|           <div class="h-1px max-w-100px flex-1 bg-gray-300 op-50" /> | ||||
|           <div class="mx-2 text-gray-400"> | ||||
|             or | ||||
|           </div> | ||||
|           <div class="h-1px max-w-100px flex-1 bg-gray-300 op-50" /> | ||||
|         </div> | ||||
| 
 | ||||
|         <p>Paste an image from clipboard</p> | ||||
|       </div> | ||||
|     </slot> | ||||
|     <div mt-2> | ||||
|       <img ref="imgPreview" width="150"> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user