feat(c-file-upload): paste image

Property paste-image to allow pasting an image directly from clipboard
This commit is contained in:
sharevb 2024-03-03 09:28:30 +01:00 committed by ShareVB
parent 6d619603f9
commit dbd4730256

View file

@ -5,10 +5,12 @@ const props = withDefaults(defineProps<{
multiple?: boolean multiple?: boolean
accept?: string accept?: string
title?: string title?: string
pasteImage?: boolean
}>(), { }>(), {
multiple: false, multiple: false,
accept: undefined, accept: undefined,
title: 'Drag and drop files here, or click to select files', title: 'Drag and drop files here, or click to select files',
pasteImage: false,
}); });
const emit = defineEmits<{ const emit = defineEmits<{
@ -16,11 +18,31 @@ const emit = defineEmits<{
(event: 'fileUpload', file: File): void (event: 'fileUpload', file: File): void
}>(); }>();
const { multiple } = toRefs(props); const { multiple, pasteImage } = toRefs(props);
const isOverDropZone = ref(false); 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 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() { function triggerFileInput() {
fileInput.value?.click(); fileInput.value?.click();
@ -39,7 +61,30 @@ function handleDrop(event: DragEvent) {
handleUpload(files); 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)) { if (_.isNil(files) || _.isEmpty(files)) {
return; return;
} }
@ -49,6 +94,7 @@ function handleUpload(files: FileList | null | undefined) {
return; return;
} }
await handlePreview(files[0]);
emit('fileUpload', files[0]); emit('fileUpload', files[0]);
} }
</script> </script>
@ -60,6 +106,7 @@ function handleUpload(files: FileList | null | undefined) {
'border-primary border-opacity-100': isOverDropZone, 'border-primary border-opacity-100': isOverDropZone,
}" }"
@click="triggerFileInput" @click="triggerFileInput"
@paste.prevent="onPasteImage"
@drop.prevent="handleDrop" @drop.prevent="handleDrop"
@dragover.prevent @dragover.prevent
@dragenter="isOverDropZone = true" @dragenter="isOverDropZone = true"
@ -73,6 +120,7 @@ function handleUpload(files: FileList | null | undefined) {
:accept="accept" :accept="accept"
@change="handleFileInput" @change="handleFileInput"
> >
<slot> <slot>
<span op-70> <span op-70>
{{ title }} {{ title }}
@ -90,6 +138,22 @@ function handleUpload(files: FileList | null | undefined) {
<c-button> <c-button>
Browse files Browse files
</c-button> </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> </slot>
<div mt-2>
<img ref="imgPreview" width="150">
</div>
</div> </div>
</template> </template>