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"'
       )