From 3e5b30ff5aa92457116a1bf3ed42f39aa67ca45c Mon Sep 17 00:00:00 2001 From: Lightling <contact@lightling.xyz> Date: Sun, 23 Mar 2025 16:04:31 -0400 Subject: [PATCH] image editing from editor modal --- src/components/InputImage.vue | 31 +++++++++++++++++++++------- src/views/Editor/DataEditor.vue | 1 + src/views/Editor/DataEditorModal.vue | 18 +++++++++++++++- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/components/InputImage.vue b/src/components/InputImage.vue index b60b223..bde8bdc 100644 --- a/src/components/InputImage.vue +++ b/src/components/InputImage.vue @@ -19,10 +19,13 @@ const store = useAppStore() const props = defineProps<{ src: string + asModal?: boolean }>() const emit = defineEmits<{ (e: 'saved', val: string): void (e: 'canceled'): void + (e: 'removed'): void + (e: 'changed', val: string): void }>() const filePath = ref(store.currentInventory.filePath.replace('.json', '/')) const currentSrc = ref('') @@ -31,7 +34,6 @@ const renderedSrc = computed(() => { ? currentSrc.value : props.src }) -const currentBlob = ref<Blob>() const onBrowseForImage = async (e: Event) => { e.preventDefault() @@ -47,9 +49,12 @@ const onBrowseForImage = async (e: Event) => { method: 'GET', }) let blob = await res.blob() - if (blob.type.includes('image')){ - currentSrc.value = URL.createObjectURL(blob) - currentBlob.value = blob + if (blob.type.includes('image')) { + const url = URL.createObjectURL(blob) + convertToDataUrlViaCanvas(url, (val) => { + emit('changed', val) + currentSrc.value = val + }, blob.type) } } } @@ -75,11 +80,15 @@ const onCancel = (e: Event) => { emit('canceled') } +const onRemove = (e: Event) => { + e.preventDefault() + currentSrc.value = '' + emit('removed') +} + const onSave = async (e: Event) => { e.preventDefault() - convertToDataUrlViaCanvas(currentSrc.value, (val) => { - emit('saved', val) - }, currentBlob.value!.type) + emit('saved', currentSrc.value) } </script> @@ -101,16 +110,24 @@ const onSave = async (e: Event) => { ) .footer Button( + v-if='asModal' label='Save' @click='onSave' :disabled='!currentSrc' ) + Button( + v-else + label='Remove' + @click='onRemove' + :disabled='!renderedSrc && !currentSrc' + ) Button( label='Replace' @click='onBrowseForImage' :disabled='!renderedSrc && !currentSrc' ) Button( + v-if='asModal' label='Cancel' @click='onCancel' ) diff --git a/src/views/Editor/DataEditor.vue b/src/views/Editor/DataEditor.vue index 7b13e0d..8b79a67 100644 --- a/src/views/Editor/DataEditor.vue +++ b/src/views/Editor/DataEditor.vue @@ -151,6 +151,7 @@ Dialog( ) InputImage( :src='editingImageSrc' + :asModal='true' @saved='onSaveImage' @canceled='onCancelImage' ) diff --git a/src/views/Editor/DataEditorModal.vue b/src/views/Editor/DataEditorModal.vue index b2434fe..34af0a6 100644 --- a/src/views/Editor/DataEditorModal.vue +++ b/src/views/Editor/DataEditorModal.vue @@ -16,6 +16,7 @@ import Select from 'primevue/select' import { useAppStore } from 'src/store' import type { EditingRow } from 'src/types/editing' import type { Row as DataRow, Column as DataColumn } from 'src/types/data' +import InputImage from 'src/components/InputImage.vue' const store = useAppStore() @@ -42,6 +43,17 @@ const onSave = async (e: Event) => { emit('saved', JSON.parse(JSON.stringify(editing.value || {})), props.row) visible.value = false } + +const onImageChanged = (field: string, image: string) => { + editing.value[field] = { + ...editing.value[field], + src: image, + } +} + +const onImageRemoved = (field: string) => { + editing.value[field] = null +} </script> <template lang="pug"> @@ -60,7 +72,11 @@ Dialog( InputGroupAddon( v-if='field.type === "image"' ) - p Image editing in data modal not yet supported + InputImage( + :src='editing[field.name]?.src' + @changed='(e) => onImageChanged(field.name, e)' + @removed='(e) => onImageRemoved(field.name)' + ) InputGroupAddon( v-else-if='field.type === "toggle"' )