Fixes for UI
This commit is contained in:
@@ -1,343 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { type WidgetLayout } from "$lib/stores/layoutState";
|
||||
import { Block } from "@gradio/atoms";
|
||||
import { TextBox } from "@gradio/form";
|
||||
import Row from "$lib/components/gradio/app/Row.svelte";
|
||||
import { get, type Writable } from "svelte/store";
|
||||
import Modal from "$lib/components/Modal.svelte";
|
||||
import { Button } from "@gradio/button";
|
||||
import type { ComfyImageEditorNode, ComfyImageLocation } from "$lib/nodes/ComfyWidgetNodes";
|
||||
import { Embed as Klecks } from "klecks";
|
||||
|
||||
import "klecks/style/style.scss";
|
||||
import ImageUpload from "$lib/components/ImageUpload.svelte";
|
||||
import { uploadImageToComfyUI, type ComfyBoxImageMetadata, comfyFileToComfyBoxMetadata, comfyBoxImageToComfyURL, comfyBoxImageToComfyFile, type ComfyUploadImageType } from "$lib/utils";
|
||||
import notify from "$lib/notify";
|
||||
import NumberInput from "$lib/components/NumberInput.svelte";
|
||||
|
||||
export let widget: WidgetLayout | null = null;
|
||||
export let isMobile: boolean = false;
|
||||
let node: ComfyImageEditorNode | null = null;
|
||||
let nodeValue: Writable<ComfyBoxImageMetadata[]> | null = null;
|
||||
let attrsChanged: Writable<number> | null = null;
|
||||
|
||||
let imgWidth: number = 0;
|
||||
let imgHeight: number = 0;
|
||||
|
||||
$: widget && setNodeValue(widget);
|
||||
|
||||
$: if ($nodeValue && $nodeValue.length > 0) {
|
||||
// TODO improve
|
||||
if (imgWidth > 0 && imgHeight > 0) {
|
||||
$nodeValue[0].width = imgWidth
|
||||
$nodeValue[0].height = imgHeight
|
||||
}
|
||||
else {
|
||||
$nodeValue[0].width = 0
|
||||
$nodeValue[0].height = 0
|
||||
}
|
||||
}
|
||||
|
||||
function setNodeValue(widget: WidgetLayout) {
|
||||
if (widget) {
|
||||
node = widget.node as ComfyImageEditorNode
|
||||
nodeValue = node.value;
|
||||
attrsChanged = widget.attrsChanged;
|
||||
status = $nodeValue && $nodeValue.length > 0 ? "uploaded" : "empty"
|
||||
}
|
||||
};
|
||||
|
||||
let editorRoot: HTMLDivElement | null = null;
|
||||
let showModal = false;
|
||||
let kl: Klecks | null = null;
|
||||
|
||||
function disposeEditor() {
|
||||
console.warn("[ImageEditorWidget] CLOSING", widget, $nodeValue)
|
||||
|
||||
if (editorRoot) {
|
||||
while (editorRoot.firstChild) {
|
||||
editorRoot.removeChild(editorRoot.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
kl = null;
|
||||
showModal = false;
|
||||
}
|
||||
|
||||
function generateBlankCanvas(width: number, height: number, fill: string = "#fff"): HTMLCanvasElement {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.save();
|
||||
ctx.fillStyle = fill,
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.restore();
|
||||
return canvas;
|
||||
}
|
||||
|
||||
async function loadImage(imageURL: string): Promise<HTMLImageElement> {
|
||||
return new Promise((resolve) => {
|
||||
const e = new Image();
|
||||
e.setAttribute('crossorigin', 'anonymous'); // Don't taint the canvas from loading files on-disk
|
||||
e.addEventListener("load", () => { resolve(e); });
|
||||
e.src = imageURL;
|
||||
return e;
|
||||
});
|
||||
}
|
||||
|
||||
async function generateImageCanvas(imageURL: string): Promise<[HTMLCanvasElement, number, number]> {
|
||||
const image = await loadImage(imageURL);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.save();
|
||||
ctx.fillStyle = "rgba(255, 255, 255, 0.0)";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(image, 0, 0);
|
||||
ctx.restore();
|
||||
return [canvas, image.width, image.height];
|
||||
}
|
||||
|
||||
const FILENAME: string = "ComfyUITemp.png";
|
||||
const SUBFOLDER: string = "ComfyBox_Editor";
|
||||
const DIRECTORY: ComfyUploadImageType = "input";
|
||||
|
||||
async function submitKlecksToComfyUI(onSuccess: () => void, onError: () => void) {
|
||||
const blob = kl.getPNG();
|
||||
|
||||
status = "uploading"
|
||||
|
||||
await uploadImageToComfyUI(blob, FILENAME, DIRECTORY, SUBFOLDER)
|
||||
.then((entry: ComfyImageLocation) => {
|
||||
const meta: ComfyBoxImageMetadata = comfyFileToComfyBoxMetadata(entry);
|
||||
$nodeValue = [meta] // TODO more than one image
|
||||
status = "uploaded"
|
||||
notify("Saved image to ComfyUI!", { type: "success" })
|
||||
onSuccess();
|
||||
})
|
||||
.catch(err => {
|
||||
notify(`Failed to upload image from editor: ${err}`, { type: "error", timeout: 10000 })
|
||||
status = "error"
|
||||
uploadError = err;
|
||||
$nodeValue = []
|
||||
onError();
|
||||
})
|
||||
}
|
||||
|
||||
let closeDialog = null;
|
||||
|
||||
async function saveAndClose() {
|
||||
console.log(closeDialog, kl)
|
||||
if (!closeDialog || !kl)
|
||||
return;
|
||||
|
||||
submitKlecksToComfyUI(() => {}, () => {});
|
||||
closeDialog()
|
||||
}
|
||||
|
||||
let blankImageWidth = 512;
|
||||
let blankImageHeight = 512;
|
||||
|
||||
async function openImageEditor() {
|
||||
if (!editorRoot)
|
||||
return;
|
||||
|
||||
showModal = true;
|
||||
|
||||
const url = `http://${location.hostname}:8188` // TODO make configurable
|
||||
|
||||
kl = new Klecks({
|
||||
embedUrl: url,
|
||||
onSubmit: submitKlecksToComfyUI,
|
||||
targetEl: editorRoot,
|
||||
warnOnPageClose: false
|
||||
});
|
||||
|
||||
console.warn("[ImageEditorWidget] OPENING", widget, $nodeValue)
|
||||
|
||||
let canvas = null;
|
||||
let width = blankImageWidth;
|
||||
let height = blankImageHeight;
|
||||
|
||||
if ($nodeValue && $nodeValue.length > 0) {
|
||||
const comfyImage = $nodeValue[0];
|
||||
const comfyURL = comfyBoxImageToComfyURL(comfyImage);
|
||||
[canvas, width, height] = await generateImageCanvas(comfyURL);
|
||||
}
|
||||
else {
|
||||
canvas = generateBlankCanvas(width, height);
|
||||
}
|
||||
|
||||
kl.openProject({
|
||||
width: width,
|
||||
height: height,
|
||||
layers: [{
|
||||
name: 'Image',
|
||||
opacity: 1,
|
||||
mixModeStr: 'source-over',
|
||||
image: canvas
|
||||
}]
|
||||
});
|
||||
|
||||
setTimeout(function () {
|
||||
kl.klApp?.out("yo");
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
let status = "empty";
|
||||
let uploadError = null;
|
||||
|
||||
function onUploading() {
|
||||
console.warn("UPLOADING!!!")
|
||||
uploadError = null;
|
||||
status = "uploading"
|
||||
}
|
||||
|
||||
function onUploaded(e: CustomEvent<ComfyImageLocation[]>) {
|
||||
console.warn("UPLOADED!!!")
|
||||
uploadError = null;
|
||||
status = "uploaded"
|
||||
$nodeValue = e.detail.map(comfyFileToComfyBoxMetadata);
|
||||
}
|
||||
|
||||
function onClear() {
|
||||
console.warn("CLEAR!!!")
|
||||
uploadError = null;
|
||||
status = "empty"
|
||||
$nodeValue = []
|
||||
}
|
||||
|
||||
function onUploadError(e: CustomEvent<any>) {
|
||||
console.warn("ERROR!!!")
|
||||
status = "error"
|
||||
uploadError = e.detail
|
||||
$nodeValue = []
|
||||
notify(`Failed to upload image to ComfyUI: ${uploadError}`, { type: "error", timeout: 10000 })
|
||||
}
|
||||
|
||||
function onChange(e: CustomEvent<ComfyImageLocation[]>) {
|
||||
}
|
||||
|
||||
let _value: ComfyImageLocation[] = []
|
||||
$: if ($nodeValue)
|
||||
_value = $nodeValue.map(comfyBoxImageToComfyFile)
|
||||
else
|
||||
_value = []
|
||||
|
||||
$: canEdit = status === "empty" || status === "uploaded";
|
||||
</script>
|
||||
|
||||
<div class="wrapper comfy-image-editor">
|
||||
{#if widget.attrs.variant === "fileUpload" || isMobile}
|
||||
<ImageUpload value={_value}
|
||||
bind:imgWidth
|
||||
bind:imgHeight
|
||||
fileCount={"single"}
|
||||
elem_classes={[]}
|
||||
style={""}
|
||||
label={widget.attrs.title}
|
||||
on:uploading={onUploading}
|
||||
on:uploaded={onUploaded}
|
||||
on:upload_error={onUploadError}
|
||||
on:clear={onClear}
|
||||
on:change={onChange}
|
||||
on:image_clicked={openImageEditor}
|
||||
/>
|
||||
{:else}
|
||||
<div class="comfy-image-editor-panel">
|
||||
<ImageUpload value={_value}
|
||||
bind:imgWidth
|
||||
bind:imgHeight
|
||||
fileCount={"single"}
|
||||
elem_classes={[]}
|
||||
style={""}
|
||||
label={widget.attrs.title}
|
||||
on:uploading={onUploading}
|
||||
on:uploaded={onUploaded}
|
||||
on:upload_error={onUploadError}
|
||||
on:clear={onClear}
|
||||
on:change={onChange}
|
||||
on:image_clicked={openImageEditor}
|
||||
/>
|
||||
<Modal bind:showModal closeOnClick={false} on:close={disposeEditor} bind:closeDialog>
|
||||
<div>
|
||||
<div id="klecks-loading-screen">
|
||||
<span id="klecks-loading-screen-text"></span>
|
||||
</div>
|
||||
<div class="image-editor-root" bind:this={editorRoot} />
|
||||
</div>
|
||||
<div slot="buttons">
|
||||
<Button variant="primary" on:click={saveAndClose}>
|
||||
Save and Close
|
||||
</Button>
|
||||
<Button variant="secondary" on:click={closeDialog}>
|
||||
Discard Edits
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
<Block>
|
||||
{#if !$nodeValue || $nodeValue.length === 0}
|
||||
<Row>
|
||||
<Row>
|
||||
<Button variant="secondary" disabled={!canEdit} on:click={openImageEditor}>
|
||||
Create Image
|
||||
</Button>
|
||||
<div>
|
||||
<TextBox show_label={false} disabled={true} value="Status: {status}"/>
|
||||
</div>
|
||||
{#if uploadError}
|
||||
<div>
|
||||
Upload error: {uploadError}
|
||||
</div>
|
||||
{/if}
|
||||
</Row>
|
||||
<Row>
|
||||
<NumberInput label={"Width"} min={64} max={2048} step={64} bind:value={blankImageWidth} />
|
||||
<NumberInput label={"Height"} min={64} max={2048} step={64} bind:value={blankImageHeight} />
|
||||
</Row>
|
||||
</Row>
|
||||
{:else}
|
||||
<Row>
|
||||
<Button variant="secondary" disabled={!canEdit} on:click={openImageEditor}>
|
||||
Edit Image
|
||||
</Button>
|
||||
<div>
|
||||
<TextBox show_label={false} disabled={true} value="Status: {status}"/>
|
||||
</div>
|
||||
{#if uploadError}
|
||||
<div>
|
||||
Upload error: {uploadError}
|
||||
</div>
|
||||
{/if}
|
||||
</Row>
|
||||
{/if}
|
||||
</Block>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.image-editor-root {
|
||||
width: 75vw;
|
||||
height: 75vh;
|
||||
overflow: hidden;
|
||||
|
||||
color: black;
|
||||
|
||||
:global(> .g-root) {
|
||||
height: calc(100% - 59px);
|
||||
}
|
||||
}
|
||||
|
||||
.comfy-image-editor {
|
||||
:global(> dialog) {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.kl-popup) {
|
||||
z-index: 999999999999;
|
||||
}
|
||||
</style>
|
||||
@@ -82,6 +82,7 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
||||
|
||||
let state = get(queueState);
|
||||
let ss = get(selectionState);
|
||||
const isRunningNode = node.id === state.runningNodeID
|
||||
|
||||
let color = null;
|
||||
let thickness = 1;
|
||||
@@ -92,16 +93,22 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
||||
if (ss.currentHoveredNodes.has(node.id)) {
|
||||
color = "lightblue";
|
||||
}
|
||||
else if (node.id === +state.runningNodeID) {
|
||||
else if (isRunningNode) {
|
||||
color = "#0f0";
|
||||
}
|
||||
|
||||
if (color) {
|
||||
this.drawNodeOutline(node, ctx, state.progress, size, fgColor, bgColor, color, thickness)
|
||||
this.drawNodeOutline(node, ctx, size, fgColor, bgColor, color, thickness)
|
||||
}
|
||||
|
||||
if (isRunningNode && state.progress) {
|
||||
ctx.fillStyle = "green";
|
||||
ctx.fillRect(0, 0, size[0] * (state.progress.value / state.progress.max), 6);
|
||||
ctx.fillStyle = bgColor;
|
||||
}
|
||||
}
|
||||
|
||||
private drawNodeOutline(node: LGraphNode, ctx: CanvasRenderingContext2D, progress?: Progress, size: Vector2, fgColor: string, bgColor: string, outlineColor: string, outlineThickness: number) {
|
||||
private drawNodeOutline(node: LGraphNode, ctx: CanvasRenderingContext2D, size: Vector2, fgColor: string, bgColor: string, outlineColor: string, outlineThickness: number) {
|
||||
const shape = node.shape || BuiltInSlotShape.ROUND_SHAPE;
|
||||
ctx.lineWidth = outlineThickness;
|
||||
ctx.globalAlpha = 0.8;
|
||||
@@ -131,12 +138,6 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
||||
ctx.stroke();
|
||||
ctx.strokeStyle = fgColor;
|
||||
ctx.globalAlpha = 1;
|
||||
|
||||
if (progress) {
|
||||
ctx.fillStyle = "green";
|
||||
ctx.fillRect(0, 0, size[0] * (progress.value / progress.max), 6);
|
||||
ctx.fillStyle = bgColor;
|
||||
}
|
||||
}
|
||||
|
||||
private alignToGrid(node: LGraphNode, ctx: CanvasRenderingContext2D) {
|
||||
|
||||
@@ -137,45 +137,22 @@ export class ImageViewer {
|
||||
}
|
||||
}
|
||||
|
||||
setupGalleryImageForLightbox(e: HTMLImageElement) {
|
||||
if (e.dataset.modded === "true")
|
||||
showLightbox(source: HTMLImageElement) {
|
||||
const initiallyZoomed = true
|
||||
this.modalZoomSet(this.modalImage, initiallyZoomed)
|
||||
|
||||
const galleryElem = source.closest<HTMLDivElement>("div.block")
|
||||
console.debug("[ImageViewer] showModal", event, source, galleryElem);
|
||||
if (!galleryElem || ImageViewer.all_gallery_buttons(galleryElem).length === 0) {
|
||||
console.error("No buttons found on gallery element!", galleryElem)
|
||||
return;
|
||||
}
|
||||
|
||||
e.dataset.modded = "true";
|
||||
e.style.cursor = 'pointer'
|
||||
e.style.userSelect = 'none'
|
||||
|
||||
var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1
|
||||
|
||||
// For Firefox, listening on click first switched to next image then shows the lightbox.
|
||||
// If you know how to fix this without switching to mousedown event, please.
|
||||
// For other browsers the event is click to make it possiblr to drag picture.
|
||||
var event = isFirefox ? 'mousedown' : 'click'
|
||||
|
||||
e.addEventListener(event, (evt) => {
|
||||
// if (!opts.js_modal_lightbox || evt.button != 0) return;
|
||||
|
||||
const initiallyZoomed = true
|
||||
this.modalZoomSet(this.modalImage, initiallyZoomed)
|
||||
evt.preventDefault()
|
||||
|
||||
const source = evt.target as HTMLImageElement;
|
||||
|
||||
const galleryElem = source.closest<HTMLDivElement>("div.block")
|
||||
console.debug("[ImageViewer] showModal", event, source, galleryElem);
|
||||
if (!galleryElem || ImageViewer.all_gallery_buttons(galleryElem).length === 0) {
|
||||
console.error("No buttons found on gallery element!", galleryElem)
|
||||
return;
|
||||
}
|
||||
|
||||
let urls = ImageViewer.get_gallery_urls(galleryElem)
|
||||
const [_currentButton, index] = ImageViewer.selected_gallery_button(galleryElem)
|
||||
console.warn("Gallery!", index, urls, galleryElem)
|
||||
|
||||
this.showModal(urls, index, galleryElem)
|
||||
evt.stopPropagation();
|
||||
}, true);
|
||||
let urls = ImageViewer.get_gallery_urls(galleryElem)
|
||||
const [_currentButton, index] = ImageViewer.selected_gallery_button(galleryElem)
|
||||
console.warn("Gallery!", index, urls, galleryElem)
|
||||
|
||||
this.showModal(urls, index, galleryElem)
|
||||
}
|
||||
|
||||
modalZoomSet(modalImage: HTMLImageElement, enable: boolean) {
|
||||
|
||||
@@ -65,9 +65,11 @@
|
||||
dateStr = formatDate(date);
|
||||
}
|
||||
|
||||
const subgraphs: string[] | null = entry.extraData?.extra_pnginfo?.comfyBoxSubgraphs;
|
||||
|
||||
let message = "Prompt";
|
||||
if (entry.extraData.subgraphs)
|
||||
message = `Prompt: ${entry.extraData.subgraphs.join(', ')}`
|
||||
if (subgraphs?.length > 0)
|
||||
message = `Prompt: ${subgraphs.join(', ')}`
|
||||
|
||||
let submessage = `Nodes: ${Object.keys(entry.prompt).length}`
|
||||
if (Object.keys(entry.outputs).length > 0) {
|
||||
@@ -156,12 +158,14 @@
|
||||
}
|
||||
|
||||
let showModal = false;
|
||||
let expandAll = false;
|
||||
let selectedPrompt = null;
|
||||
let selectedImages = [];
|
||||
function showPrompt(entry: QueueUIEntry, e: MouseEvent) {
|
||||
selectedPrompt = entry.entry.prompt;
|
||||
selectedImages = entry.images;
|
||||
showModal = true;
|
||||
expandAll = false
|
||||
}
|
||||
|
||||
$: if(!showModal)
|
||||
@@ -180,8 +184,16 @@
|
||||
<h1 style="padding-bottom: 1rem;">Prompt Details</h1>
|
||||
</div>
|
||||
{#if selectedPrompt}
|
||||
<PromptDisplay prompt={selectedPrompt} images={selectedImages} />
|
||||
<PromptDisplay prompt={selectedPrompt} images={selectedImages} {expandAll} />
|
||||
{/if}
|
||||
<div slot="buttons" let:closeDialog>
|
||||
<Button variant="secondary" on:click={closeDialog}>
|
||||
Close
|
||||
</Button>
|
||||
<Button variant="secondary" on:click={() => (expandAll = !expandAll)}>
|
||||
Expand All
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<div class="queue">
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
}
|
||||
|
||||
function doClose() {
|
||||
showModal = false;
|
||||
dialog.close();
|
||||
dispatch("close")
|
||||
}
|
||||
@@ -41,7 +42,7 @@
|
||||
<slot name="header" />
|
||||
<slot />
|
||||
<div class="button-row">
|
||||
<slot name="buttons">
|
||||
<slot name="buttons" {closeDialog}>
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<Button variant="secondary" on:click={doClose}>Close</Button>
|
||||
</slot>
|
||||
|
||||
@@ -6,16 +6,20 @@
|
||||
import { JSON as JSONIcon, Copy, Check } from "@gradio/icons";
|
||||
import Accordion from "$lib/components/gradio/app/Accordion.svelte";
|
||||
import Gallery from "$lib/components/gradio/gallery/Gallery.svelte";
|
||||
import type { Styles } from "@gradio/utils";
|
||||
import { ImageViewer } from "$lib/ImageViewer";
|
||||
import type { Styles } from "@gradio/utils";
|
||||
|
||||
const splitLength = 50;
|
||||
|
||||
export let prompt: SerializedPromptInputsAll;
|
||||
export let images: string[] = [];
|
||||
export let isMobile: boolean = false;
|
||||
export let expandAll: boolean = false;
|
||||
|
||||
let galleryStyle: Styles = {
|
||||
grid_cols: [2],
|
||||
object_fit: "cover",
|
||||
height: "var(--size-96)"
|
||||
}
|
||||
|
||||
function isInputLink(input: SerializedPromptInput): boolean {
|
||||
@@ -59,6 +63,11 @@
|
||||
copyFeedback(nodeID, inputName);
|
||||
}
|
||||
}
|
||||
|
||||
function onGalleryImageClicked(e: CustomEvent<HTMLImageElement>) {
|
||||
// TODO dialog renders over it
|
||||
// ImageViewer.instance.showLightbox(e.detail)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="prompt-display">
|
||||
@@ -70,7 +79,7 @@
|
||||
{#if filtered.length > 0}
|
||||
<div class="accordion">
|
||||
<Block padding={true}>
|
||||
<Accordion label="Node {i+1}: {classType}" open={false}>
|
||||
<Accordion label="Node {i+1}: {classType}" open={expandAll}>
|
||||
{#each filtered as [inputName, input]}
|
||||
<Block>
|
||||
<button class="copy-button" on:click={() => handleCopy(nodeID, inputName, input)}>
|
||||
@@ -121,6 +130,7 @@
|
||||
style={galleryStyle}
|
||||
root={""}
|
||||
root_url={""}
|
||||
on:clicked={onGalleryImageClicked}
|
||||
/>
|
||||
</Block>
|
||||
</div>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
select: SelectData;
|
||||
clicked: HTMLImageElement
|
||||
}>();
|
||||
|
||||
// tracks whether the value of the gallery was reset
|
||||
@@ -142,6 +143,14 @@
|
||||
|
||||
let height = 0;
|
||||
let window_height = 0;
|
||||
|
||||
let imgElem = null;
|
||||
|
||||
function onClick() {
|
||||
// selected_image = next
|
||||
if (imgElem)
|
||||
dispatch("clicked", imgElem)
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerHeight={window_height} />
|
||||
@@ -166,7 +175,8 @@
|
||||
<ModifyUpload on:clear={() => (selected_image = null)} />
|
||||
|
||||
<img
|
||||
on:click={() => (selected_image = next)}
|
||||
on:click={onClick}
|
||||
bind:this={imgElem}
|
||||
src={_value[selected_image][0].data}
|
||||
alt={_value[selected_image][1] || ""}
|
||||
title={_value[selected_image][1] || null}
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
let mobileLightbox = null;
|
||||
|
||||
function showMobileLightbox(event: Event) {
|
||||
function showMobileLightbox(source: HTMLImageElement) {
|
||||
if (!f7)
|
||||
return
|
||||
|
||||
@@ -58,16 +58,14 @@
|
||||
mobileLightbox = null;
|
||||
}
|
||||
|
||||
const source = (event.target || event.srcElement) as HTMLImageElement;
|
||||
const galleryElem = source.closest<HTMLDivElement>("div.block")
|
||||
console.debug("[ImageViewer] showModal", event, source, galleryElem);
|
||||
console.debug("[ImageViewer] showModal", source, galleryElem);
|
||||
if (!galleryElem || ImageViewer.all_gallery_buttons(galleryElem).length === 0) {
|
||||
console.error("No buttons found on gallery element!", galleryElem)
|
||||
return;
|
||||
}
|
||||
|
||||
const allGalleryButtons = ImageViewer.all_gallery_buttons(galleryElem);
|
||||
const selectedSource = source.src
|
||||
|
||||
const images = allGalleryButtons.map(button => {
|
||||
return {
|
||||
@@ -84,47 +82,18 @@
|
||||
type: 'popup',
|
||||
});
|
||||
mobileLightbox.open(selected_image)
|
||||
|
||||
event.stopPropagation()
|
||||
}
|
||||
|
||||
function setupImageForMobileLightbox(e: HTMLImageElement) {
|
||||
if (e.dataset.modded === "true")
|
||||
return;
|
||||
|
||||
e.dataset.modded = "true";
|
||||
e.style.cursor = "pointer";
|
||||
e.style.userSelect = "none";
|
||||
|
||||
var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1
|
||||
|
||||
// For Firefox, listening on click first switched to next image then shows the lightbox.
|
||||
// If you know how to fix this without switching to mousedown event, please.
|
||||
// For other browsers the event is click to make it possiblr to drag picture.
|
||||
var event = isFirefox ? 'mousedown' : 'click'
|
||||
|
||||
e.addEventListener(event, (evt) => {
|
||||
evt.preventDefault()
|
||||
showMobileLightbox(evt)
|
||||
}, true);
|
||||
function onClicked(e: CustomEvent<HTMLImageElement>) {
|
||||
if (isMobile) {
|
||||
showMobileLightbox(e.detail)
|
||||
}
|
||||
else {
|
||||
ImageViewer.instance.showLightbox(e.detail)
|
||||
}
|
||||
}
|
||||
|
||||
function onSelect(e: CustomEvent<GradioSelectData>) {
|
||||
// Setup lightbox
|
||||
// Wait for gradio gallery to show the large preview image, if no timeout then
|
||||
// the event might fire too early
|
||||
|
||||
const callback = isMobile ? setupImageForMobileLightbox
|
||||
: ImageViewer.instance.setupGalleryImageForLightbox.bind(ImageViewer.instance)
|
||||
|
||||
setTimeout(() => {
|
||||
const images = element.querySelectorAll<HTMLImageElement>('div.block div > img')
|
||||
if (images != null) {
|
||||
images.forEach(callback);
|
||||
}
|
||||
ImageViewer.instance.refreshImages();
|
||||
}, 200)
|
||||
|
||||
// Update index
|
||||
node.setProperty("index", e.detail.index as number)
|
||||
}
|
||||
@@ -176,6 +145,7 @@
|
||||
root={""}
|
||||
root_url={""}
|
||||
on:select={onSelect}
|
||||
on:clicked={onClicked}
|
||||
bind:imageWidth={$imageWidth}
|
||||
bind:imageHeight={$imageHeight}
|
||||
bind:selected_image
|
||||
|
||||
Reference in New Issue
Block a user