Skip to content

Commit

Permalink
Improve UX of dynamic image input
Browse files Browse the repository at this point in the history
  • Loading branch information
mschuettlerTNG committed Dec 17, 2024
1 parent 36ee53d commit 24cc605
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 51 deletions.
52 changes: 3 additions & 49 deletions WebUI/src/components/SettingsImageComfyDynamic.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
<template>
<div v-for="input, i in imageGeneration.comfyInputs" class="flex flex-col gap-2">
<div v-for="input, i in imageGeneration.comfyInputs" class="flex flex-col gap-2 py-2">
<p>{{ input.label }}</p>

<!-- Number -->
<slide-bar v-if="input.type === 'number'" v-model:current="(input.current.value as number)" :min="input.min"
:max="input.max" :step="input.step"></slide-bar>

<!-- Image -->
<img ref="imgDropZones" v-if="input.type === 'image'" :src="(input.current.value as string)" alt="Image" class="w-64 h-64 object-scale-down self-center"></img>
<Input v-if="input.type === 'image'" accept="image/jpeg,image/png,image/webp" id="picture" type="file"
v-on:change="(e: Event) => handleFilesEvent(input.current as Ref<string, string>)(e)"></Input>
<LoadImage :id="`${input.nodeTitle}.${input.nodeInput}`" v-if="input.type === 'image'" :image-url-ref="(input.current as WritableComputedRef<string>)"></LoadImage>

<!-- String -->
<Input v-if="input.type === 'string'" type="text" v-model="(input.current.value as string)"></Input>
Expand All @@ -25,53 +23,9 @@
<script setup lang="ts">
import { useImageGeneration } from "@/assets/js/store/imageGeneration";
import { Input } from '../components/ui/input'
import { LoadImage } from '../components/ui/loadImage'
import SlideBar from "../components/SlideBar.vue";
import { useDropZone } from '@vueuse/core';
const imageGeneration = useImageGeneration();
const imgDropZones = useTemplateRef('imgDropZones')
const { isOverDropZone } = useDropZone(imgDropZones.value, {
onDrop: (e) => console.log('Dropped!', e),
// specify the types of data to be received.
dataTypes: ['image/jpeg'],
// control multi-file drop
multiple: true,
// whether to prevent default behavior for unhandled events
preventDefaultForUnhandled: false,
})
onMounted(() => console.log('imgDropZones', imgDropZones.value))
const handleFilesEvent = (inputCurrent: Ref<string, string>) => (event: Event) => {
if (!event.target || !(event.target instanceof HTMLInputElement) || !event.target.files) {
return;
}
const files = event.target.files;
processFiles([...files], inputCurrent);
}
function processFiles(files: File[] | null, inputCurrent: Ref<string, string>) {
if (!files) {
return;
}
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (!file.type.startsWith("image/")) {
continue;
}
const reader = new FileReader();
reader.onload = (e) => {
if (!e.target || !(e.target instanceof FileReader) || !e.target.result || typeof e.target.result !== "string") {
console.error("Failed to read file");
return;
}
inputCurrent.value = e.target.result;
};
reader.readAsDataURL(file);
}
}
</script>
6 changes: 4 additions & 2 deletions WebUI/src/components/SettingsImageGeneration.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</div>
<div class="flex items-center gap-5">
<p>{{ languages.SETTINGS_MODEL_IMAGE_PREVIEW }}</p>
<button v-show=true class="v-checkbox-control flex-none w-5 h-5"
<button v-sh=true class="v-checkbox-control flex-none w-5 h-5"
:class="{ 'v-checkbox-checked': imageGeneration.imagePreview }"
@click="() => imageGeneration.imagePreview = !imageGeneration.imagePreview">
</button>
Expand Down Expand Up @@ -123,7 +123,9 @@
</div>
</div>
<ComfyDynamic></ComfyDynamic>
<button class="mt-4" @click="imageGeneration.resetActiveWorkflowSettings"><div class="svg-icon i-refresh">Reset</div>Load workflow defaults</button>
<div class="border-t border-color-spilter items-center flex-wrap grid grid-cols-1 gap-2">
<button class="mt-4" @click="imageGeneration.resetActiveWorkflowSettings"><div class="svg-icon i-refresh">Reset</div>Load workflow defaults</button>
</div>
</div>
</template>

Expand Down
69 changes: 69 additions & 0 deletions WebUI/src/components/ui/loadImage/LoadImage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
import { useDropZone } from '@vueuse/core';
const props = defineProps<{
imageUrlRef: WritableComputedRef<string>
defaultValue?: string | number
modelValue?: string | number
class?: HTMLAttributes['class']
id: string
}>()
const acceptedImageTypes = ['image/jpeg', 'image/png', 'image/webp']
const imgDropZone = useTemplateRef('imgDropZone')
const { isOverDropZone } = useDropZone(imgDropZone, {
onDrop: (files) => processFiles(files, props.imageUrlRef),
dataTypes: acceptedImageTypes,
multiple: false,
preventDefaultForUnhandled: false,
})
const handleFilesEvent = (inputCurrent: Ref<string, string>) => (event: Event) => {
if (!event.target || !(event.target instanceof HTMLInputElement) || !event.target.files) {
return;
}
const files = event.target.files;
processFiles([...files], inputCurrent);
}
function processFiles(files: File[] | null, inputCurrent: Ref<string, string>) {
if (!files) {
return;
}
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (!file.type.startsWith("image/")) {
continue;
}
const reader = new FileReader();
reader.onload = (e) => {
if (!e.target || !(e.target instanceof FileReader) || !e.target.result || typeof e.target.result !== "string") {
console.error("Failed to read file");
return;
}
inputCurrent.value = e.target.result;
};
reader.readAsDataURL(file);
}
}
</script>

<template>
<div ref="imgDropZone" class="flex justify-center relative">
<div v-show="isOverDropZone" class="bg-black/70 absolute inset-0 flex items-center justify-center text-white text-lg">Load Image</div>
<img :src="(imageUrlRef.value as string)" alt="Image" class="w-64 py-4 object-scale-down"></img>
</div>
<div class="flex justify-center">
<input :id="id" :accept="acceptedImageTypes.join(',')" type="file" class="hidden"
v-on:change="(e: Event) => handleFilesEvent(imageUrlRef as Ref<string, string>)(e)"
>
<label :for="id" :class="cn('text-base bg-color-active py-1 px-6 rounded hover:opacity-90 hover:cursor-pointer disabled:cursor-not-allowed disabled:opacity-50 ', props.class)">Load Image</label>
</div>

</template>
1 change: 1 addition & 0 deletions WebUI/src/components/ui/loadImage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as LoadImage } from './LoadImage.vue'

0 comments on commit 24cc605

Please sign in to comment.