Spaces:
Running
on
Zero
Running
on
Zero
| import type { LGraphNode, LGraphNodeConstructor } from "typings/litegraph.js"; | |
| import { createElement as $el, getClosestOrSelf, setAttributes } from "./utils_dom.js"; | |
| type RgthreeDialogButton = { | |
| label: string; | |
| className?: string; | |
| closes?: boolean; | |
| disabled?: boolean; | |
| callback?: (e: PointerEvent | MouseEvent) => void; | |
| }; | |
| export type RgthreeDialogOptions = { | |
| content: string | HTMLElement | HTMLElement[]; | |
| class?: string | string[]; | |
| title?: string | HTMLElement | HTMLElement[]; | |
| closeX?: boolean; | |
| closeOnEsc?: boolean; | |
| closeOnModalClick?: boolean; | |
| closeButtonLabel?: string | boolean; | |
| buttons?: RgthreeDialogButton[]; | |
| onBeforeClose?: () => Promise<boolean> | boolean; | |
| }; | |
| /** | |
| * A Dialog that shows content, and closes. | |
| */ | |
| export class RgthreeDialog extends EventTarget { | |
| element: HTMLDialogElement; | |
| contentElement: HTMLDivElement; | |
| titleElement: HTMLDivElement; | |
| options: RgthreeDialogOptions; | |
| constructor(options: RgthreeDialogOptions) { | |
| super(); | |
| this.options = options; | |
| let container = $el("div.rgthree-dialog-container"); | |
| this.element = $el("dialog", { | |
| classes: ["rgthree-dialog", options.class || ""], | |
| child: container, | |
| parent: document.body, | |
| events: { | |
| click: (event: MouseEvent) => { | |
| // Close the dialog if we've clicked outside of our container. The dialog modal will | |
| // report itself as the dialog itself, so we use the inner container div (and CSS to | |
| // remove default padding from the dialog element). | |
| if ( | |
| !this.element.open || | |
| event.target === container || | |
| getClosestOrSelf(event.target, `.rgthree-dialog-container`) === container | |
| ) { | |
| return; | |
| } | |
| return this.close(); | |
| }, | |
| }, | |
| }); | |
| this.element.addEventListener("close", (event) => { | |
| this.onDialogElementClose(); | |
| }); | |
| this.titleElement = $el("div.rgthree-dialog-container-title", { | |
| parent: container, | |
| children: !options.title | |
| ? null | |
| : options.title instanceof Element || Array.isArray(options.title) | |
| ? options.title | |
| : typeof options.title === "string" | |
| ? !options.title.includes("<h2") | |
| ? $el("h2", { html: options.title }) | |
| : options.title | |
| : options.title, | |
| }); | |
| this.contentElement = $el("div.rgthree-dialog-container-content", { | |
| parent: container, | |
| child: options.content, | |
| }); | |
| const footerEl = $el("footer.rgthree-dialog-container-footer", { parent: container }); | |
| for (const button of options.buttons || []) { | |
| $el("button", { | |
| text: button.label, | |
| className: button.className, | |
| disabled: !!button.disabled, | |
| parent: footerEl, | |
| events: { | |
| click: (e: MouseEvent) => { | |
| button.callback?.(e); | |
| }, | |
| }, | |
| }); | |
| } | |
| if (options.closeButtonLabel !== false) { | |
| $el("button", { | |
| text: options.closeButtonLabel || "Close", | |
| className: "rgthree-button", | |
| parent: footerEl, | |
| events: { | |
| click: (e: MouseEvent) => { | |
| this.close(e); | |
| }, | |
| }, | |
| }); | |
| } | |
| } | |
| setTitle(content: string | HTMLElement | HTMLElement[]) { | |
| const title = | |
| typeof content !== "string" || content.includes("<h2") | |
| ? content | |
| : $el("h2", { html: content }); | |
| setAttributes(this.titleElement, { children: title }); | |
| } | |
| setContent(content: string | HTMLElement | HTMLElement[]) { | |
| setAttributes(this.contentElement, { children: content }); | |
| } | |
| show() { | |
| document.body.classList.add("rgthree-dialog-open"); | |
| this.element.showModal(); | |
| this.dispatchEvent(new CustomEvent("show")); | |
| return this; | |
| } | |
| async close(e?: MouseEvent | PointerEvent) { | |
| if (this.options.onBeforeClose && !(await this.options.onBeforeClose())) { | |
| return; | |
| } | |
| this.element.close(); | |
| } | |
| onDialogElementClose() { | |
| document.body.classList.remove("rgthree-dialog-open"); | |
| this.element.remove(); | |
| this.dispatchEvent(new CustomEvent("close", this.getCloseEventDetail())); | |
| } | |
| protected getCloseEventDetail(): { detail: any } { | |
| return { detail: null }; | |
| } | |
| } | |
| /** | |
| * A help extension for the dialog class that standardizes help content. | |
| */ | |
| export class RgthreeHelpDialog extends RgthreeDialog { | |
| constructor( | |
| node: LGraphNode | LGraphNodeConstructor, | |
| content: string, | |
| opts: Partial<RgthreeDialogOptions> = {}, | |
| ) { | |
| const title = (node.type || node.title || "").replace( | |
| /\s*\(rgthree\).*/, | |
| " <small>by rgthree</small>", | |
| ); | |
| const options = Object.assign({}, opts, { | |
| class: "-iconed -help", | |
| title, | |
| content, | |
| }); | |
| super(options); | |
| } | |
| } | |