File size: 4,104 Bytes
e26a977
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// @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;
	}
}