164 lines
3.8 KiB
JavaScript
164 lines
3.8 KiB
JavaScript
|
// @ts-check
|
||
|
|
||
|
import { $el } from "../../ui.js";
|
||
|
import { applyClasses, toggleElement } from "../utils.js";
|
||
|
import { prop } from "../../utils.js";
|
||
|
|
||
|
/**
|
||
|
* @typedef {{
|
||
|
* icon?: string;
|
||
|
* overIcon?: string;
|
||
|
* iconSize?: number;
|
||
|
* content?: string | HTMLElement;
|
||
|
* tooltip?: string;
|
||
|
* enabled?: boolean;
|
||
|
* action?: (e: Event, btn: ComfyButton) => void,
|
||
|
* classList?: import("../utils.js").ClassList,
|
||
|
* visibilitySetting?: { id: string, showValue: any },
|
||
|
* app?: import("../../app.js").ComfyApp
|
||
|
* }} ComfyButtonProps
|
||
|
*/
|
||
|
export class ComfyButton {
|
||
|
#over = 0;
|
||
|
#popupOpen = false;
|
||
|
isOver = false;
|
||
|
iconElement = $el("i.mdi");
|
||
|
contentElement = $el("span");
|
||
|
/**
|
||
|
* @type {import("./popup.js").ComfyPopup}
|
||
|
*/
|
||
|
popup;
|
||
|
|
||
|
/**
|
||
|
* @param {ComfyButtonProps} opts
|
||
|
*/
|
||
|
constructor({
|
||
|
icon,
|
||
|
overIcon,
|
||
|
iconSize,
|
||
|
content,
|
||
|
tooltip,
|
||
|
action,
|
||
|
classList = "comfyui-button",
|
||
|
visibilitySetting,
|
||
|
app,
|
||
|
enabled = true,
|
||
|
}) {
|
||
|
this.element = $el("button", {
|
||
|
onmouseenter: () => {
|
||
|
this.isOver = true;
|
||
|
if(this.overIcon) {
|
||
|
this.updateIcon();
|
||
|
}
|
||
|
},
|
||
|
onmouseleave: () => {
|
||
|
this.isOver = false;
|
||
|
if(this.overIcon) {
|
||
|
this.updateIcon();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}, [this.iconElement, this.contentElement]);
|
||
|
|
||
|
this.icon = prop(this, "icon", icon, toggleElement(this.iconElement, { onShow: this.updateIcon }));
|
||
|
this.overIcon = prop(this, "overIcon", overIcon, () => {
|
||
|
if(this.isOver) {
|
||
|
this.updateIcon();
|
||
|
}
|
||
|
});
|
||
|
this.iconSize = prop(this, "iconSize", iconSize, this.updateIcon);
|
||
|
this.content = prop(
|
||
|
this,
|
||
|
"content",
|
||
|
content,
|
||
|
toggleElement(this.contentElement, {
|
||
|
onShow: (el, v) => {
|
||
|
if (typeof v === "string") {
|
||
|
el.textContent = v;
|
||
|
} else {
|
||
|
el.replaceChildren(v);
|
||
|
}
|
||
|
},
|
||
|
})
|
||
|
);
|
||
|
|
||
|
this.tooltip = prop(this, "tooltip", tooltip, (v) => {
|
||
|
if (v) {
|
||
|
this.element.title = v;
|
||
|
} else {
|
||
|
this.element.removeAttribute("title");
|
||
|
}
|
||
|
});
|
||
|
this.classList = prop(this, "classList", classList, this.updateClasses);
|
||
|
this.hidden = prop(this, "hidden", false, this.updateClasses);
|
||
|
this.enabled = prop(this, "enabled", enabled, () => {
|
||
|
this.updateClasses();
|
||
|
this.element.disabled = !this.enabled;
|
||
|
});
|
||
|
this.action = prop(this, "action", action);
|
||
|
this.element.addEventListener("click", (e) => {
|
||
|
if (this.popup) {
|
||
|
// we are either a touch device or triggered by click not hover
|
||
|
if (!this.#over) {
|
||
|
this.popup.toggle();
|
||
|
}
|
||
|
}
|
||
|
this.action?.(e, this);
|
||
|
});
|
||
|
|
||
|
if (visibilitySetting?.id) {
|
||
|
const settingUpdated = () => {
|
||
|
this.hidden = app.ui.settings.getSettingValue(visibilitySetting.id) !== visibilitySetting.showValue;
|
||
|
};
|
||
|
app.ui.settings.addEventListener(visibilitySetting.id + ".change", settingUpdated);
|
||
|
settingUpdated();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
updateIcon = () => (this.iconElement.className = `mdi mdi-${(this.isOver && this.overIcon) || this.icon}${this.iconSize ? " mdi-" + this.iconSize + "px" : ""}`);
|
||
|
updateClasses = () => {
|
||
|
const internalClasses = [];
|
||
|
if (this.hidden) {
|
||
|
internalClasses.push("hidden");
|
||
|
}
|
||
|
if (!this.enabled) {
|
||
|
internalClasses.push("disabled");
|
||
|
}
|
||
|
if (this.popup) {
|
||
|
if (this.#popupOpen) {
|
||
|
internalClasses.push("popup-open");
|
||
|
} else {
|
||
|
internalClasses.push("popup-closed");
|
||
|
}
|
||
|
}
|
||
|
applyClasses(this.element, this.classList, ...internalClasses);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param { import("./popup.js").ComfyPopup } popup
|
||
|
* @param { "click" | "hover" } mode
|
||
|
*/
|
||
|
withPopup(popup, mode = "click") {
|
||
|
this.popup = popup;
|
||
|
|
||
|
if (mode === "hover") {
|
||
|
for (const el of [this.element, this.popup.element]) {
|
||
|
el.addEventListener("mouseenter", () => {
|
||
|
this.popup.open = !!++this.#over;
|
||
|
});
|
||
|
el.addEventListener("mouseleave", () => {
|
||
|
this.popup.open = !!--this.#over;
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
popup.addEventListener("change", () => {
|
||
|
this.#popupOpen = popup.open;
|
||
|
this.updateClasses();
|
||
|
});
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
}
|