refactor(settings): replace file input with browser-fs-access
This commit is contained in:
parent
645da2f945
commit
23c7e68755
|
@ -1,6 +1,9 @@
|
|||
<script lang="ts" setup>
|
||||
import { fileOpen } from 'browser-fs-access'
|
||||
import type { FileWithHandle } from 'browser-fs-access'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue?: File
|
||||
modelValue?: FileWithHandle
|
||||
/** The image src before change */
|
||||
original?: string
|
||||
/** Allowed file types */
|
||||
|
@ -15,70 +18,56 @@ const props = withDefaults(defineProps<{
|
|||
allowedFileTypes: () => ['image/jpeg', 'image/png'],
|
||||
allowedFileSize: 1024 * 1024 * 5, // 5 MB
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(event: 'update:modelValue', value: File): void
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: FileWithHandle): void
|
||||
(event: 'pick', value: FileWithHandle): void
|
||||
(event: 'error', code: number, message: string): void
|
||||
}>()
|
||||
|
||||
const vmFile = useVModel(props, 'modelValue', emits, { passive: true })
|
||||
const file = useVModel(props, 'modelValue', emit, { passive: true })
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const elInput = ref<HTMLInputElement>()
|
||||
|
||||
function clearInput() {
|
||||
if (elInput.value)
|
||||
elInput.value.value = ''
|
||||
}
|
||||
|
||||
function selectImage(e: Event) {
|
||||
const target = e.target as HTMLInputElement
|
||||
const image = target.files?.[0]
|
||||
if (!image) {
|
||||
vmFile.value = image
|
||||
}
|
||||
else if (!props.allowedFileTypes.includes(image.type)) {
|
||||
emits('error', 1, t('error.unsupported_file_format'))
|
||||
clearInput()
|
||||
}
|
||||
else if (image.size > props.allowedFileSize) {
|
||||
emits('error', 2, t('error.file_size_cannot_exceed_n_mb', [5]))
|
||||
clearInput()
|
||||
}
|
||||
else {
|
||||
vmFile.value = image
|
||||
clearInput()
|
||||
}
|
||||
}
|
||||
|
||||
const defaultImage = computed(() => props.original || '')
|
||||
/** Preview of selected images */
|
||||
const previewImage = ref('')
|
||||
/** The current images on display */
|
||||
const imageSrc = computed<string>(() => previewImage.value || defaultImage.value)
|
||||
|
||||
// Update the preview image when the input file change
|
||||
watch(vmFile, (image, _, onCleanup) => {
|
||||
const pickImage = async () => {
|
||||
const image = await fileOpen({
|
||||
description: 'Image',
|
||||
mimeTypes: props.allowedFileTypes,
|
||||
})
|
||||
|
||||
if (!props.allowedFileTypes.includes(image.type)) {
|
||||
emit('error', 1, t('error.unsupported_file_format'))
|
||||
return
|
||||
}
|
||||
else if (image.size > props.allowedFileSize) {
|
||||
emit('error', 2, t('error.file_size_cannot_exceed_n_mb', [5]))
|
||||
return
|
||||
}
|
||||
|
||||
file.value = image
|
||||
emit('pick', file.value)
|
||||
}
|
||||
|
||||
watch(file, (image, _, onCleanup) => {
|
||||
let expired = false
|
||||
onCleanup(() => expired = true)
|
||||
|
||||
if (image) {
|
||||
if (!image) {
|
||||
previewImage.value = ''
|
||||
return
|
||||
}
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(image)
|
||||
reader.onload = (e) => {
|
||||
reader.onload = (evt) => {
|
||||
if (expired)
|
||||
return
|
||||
previewImage.value = e.target?.result as string
|
||||
previewImage.value = evt.target?.result as string
|
||||
}
|
||||
}
|
||||
else {
|
||||
previewImage.value = ''
|
||||
clearInput()
|
||||
}
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
clearInput,
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -89,6 +78,7 @@ defineExpose({
|
|||
flex justify-center items-center
|
||||
cursor-pointer
|
||||
of-hidden
|
||||
@click="pickImage"
|
||||
>
|
||||
<img
|
||||
v-if="imageSrc"
|
||||
|
@ -110,12 +100,5 @@ defineExpose({
|
|||
>
|
||||
<div class="i-ri:loader-4-line animate-spin animate-duration-[2.5s]" text-4xl />
|
||||
</div>
|
||||
<input
|
||||
ref="elInput"
|
||||
type="file"
|
||||
absolute opacity-0 inset-0 z--1
|
||||
:accept="allowedFileTypes.join(',')"
|
||||
@change="selectImage"
|
||||
>
|
||||
</label>
|
||||
</template>
|
||||
|
|
|
@ -70,7 +70,6 @@ const { submit, submitting } = submitter(async ({ dirtyFields }) => {
|
|||
<div of-hidden bg="gray-500/20" aspect="3">
|
||||
<CommonInputImage
|
||||
v-if="isHydrated"
|
||||
ref="elInputImage"
|
||||
v-model="form.header"
|
||||
:original="onlineSrc.header"
|
||||
w-full h-full
|
||||
|
|
Loading…
Reference in a new issue