Skip to content

Commit 7d1850a

Browse files
committed
Add Vue nodes mode compatibility for tooltips
1 parent 76b68f9 commit 7d1850a

4 files changed

Lines changed: 138 additions & 2 deletions

File tree

js/drawing/resolution_master_draw_methods.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ export const drawingMethods = {
8888
this.drawSliderMode(ctx, currentY);
8989
}
9090

91-
if (this.showTooltip && this.tooltipElement && this.tooltips[this.tooltipElement]) {
91+
if (!this.isVueNodesMode?.()
92+
&& this.showTooltip
93+
&& this.tooltipElement
94+
&& this.tooltips[this.tooltipElement]) {
9295
this.drawTooltip(ctx);
9396
}
9497
} finally {

js/interaction/resolution_master_interaction_methods.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,18 +534,24 @@ export const interactionMethods = {
534534
clearTimeout(this.tooltipTimer);
535535
this.tooltipTimer = null;
536536
}
537+
this.hideVueCompatTooltip?.();
537538
if (this.showTooltip) {
538539
this.showTooltip = false;
539540
this.tooltipElement = null;
540541
this.requestCanvasUpdate(true);
541542
}
542543
if (element && this.tooltips[element]) {
543544
const initialMousePos = { x: e.canvasX, y: e.canvasY };
545+
const initialClientPos = { clientX: e.clientX, clientY: e.clientY };
544546
this.tooltipTimer = setTimeout(() => {
545547
this.tooltipElement = element;
546548
this.showTooltip = true;
547549
this.tooltipFixedPos = initialMousePos;
548-
this.requestCanvasUpdate(true);
550+
if (this.isVueNodesMode?.()) {
551+
this.showVueCompatTooltip?.(element, initialClientPos);
552+
} else {
553+
this.requestCanvasUpdate(true);
554+
}
549555
}, this.tooltipDelay);
550556
}
551557
},

js/node/resolution_master_node_lifecycle.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ export const nodeLifecycleMethods = {
506506
clearTimeout(this.tooltipTimer);
507507
this.tooltipTimer = null;
508508
}
509+
this.hideVueCompatTooltip();
509510
this.hoverElement = null;
510511
this.tooltipElement = null;
511512
this.showTooltip = false;
@@ -518,6 +519,80 @@ export const nodeLifecycleMethods = {
518519
this._vueCompatCanvasHandlers = { handlePointerMove, handlePointerLeave };
519520
},
520521

522+
showVueCompatTooltip(element, pointerPosition) {
523+
const text = this.tooltips?.[element];
524+
const clientX = Number(pointerPosition?.clientX);
525+
const clientY = Number(pointerPosition?.clientY);
526+
if (!text || !Number.isFinite(clientX) || !Number.isFinite(clientY) || typeof document === "undefined") {
527+
return;
528+
}
529+
530+
if (!this._vueCompatTooltip) {
531+
const tooltip = document.createElement("div");
532+
tooltip.dataset.resolutionMasterTooltip = "true";
533+
tooltip.style.cssText = [
534+
"position:fixed",
535+
"z-index:100000",
536+
"pointer-events:none",
537+
"box-sizing:border-box",
538+
"max-width:266px",
539+
"padding:8px 8px 4px",
540+
"border:1px solid rgba(200,200,200,0.3)",
541+
"border-radius:6px",
542+
"background:linear-gradient(180deg, rgba(45,45,45,0.95), rgba(35,35,35,0.95))",
543+
"box-shadow:2px 2px 0 rgba(0,0,0,0.3)",
544+
"color:#fff",
545+
"font:12px Arial, sans-serif",
546+
"line-height:16px",
547+
"white-space:normal"
548+
].join(";");
549+
document.body.appendChild(tooltip);
550+
this._vueCompatTooltip = tooltip;
551+
}
552+
553+
const tooltip = this._vueCompatTooltip;
554+
tooltip.textContent = text;
555+
tooltip.style.display = "block";
556+
tooltip.style.visibility = "hidden";
557+
tooltip.style.left = "0px";
558+
tooltip.style.top = "0px";
559+
560+
const tooltipRect = tooltip.getBoundingClientRect();
561+
const viewportWidth = document.documentElement?.clientWidth || globalThis.innerWidth || 0;
562+
const viewportHeight = document.documentElement?.clientHeight || globalThis.innerHeight || 0;
563+
const viewportMargin = 8;
564+
let left = clientX + 15;
565+
let top = clientY - tooltipRect.height - 10;
566+
567+
if (viewportWidth > 0 && left + tooltipRect.width > viewportWidth - viewportMargin) {
568+
left = clientX - tooltipRect.width - 15;
569+
}
570+
if (top < viewportMargin) {
571+
top = clientY + 20;
572+
}
573+
if (viewportWidth > 0) {
574+
left = Math.max(viewportMargin, Math.min(left, viewportWidth - tooltipRect.width - viewportMargin));
575+
}
576+
if (viewportHeight > 0) {
577+
top = Math.max(viewportMargin, Math.min(top, viewportHeight - tooltipRect.height - viewportMargin));
578+
}
579+
580+
tooltip.style.left = `${Math.round(left)}px`;
581+
tooltip.style.top = `${Math.round(top)}px`;
582+
tooltip.style.visibility = "visible";
583+
},
584+
585+
hideVueCompatTooltip() {
586+
if (this._vueCompatTooltip) {
587+
this._vueCompatTooltip.style.display = "none";
588+
}
589+
},
590+
591+
teardownVueCompatTooltip() {
592+
this._vueCompatTooltip?.remove?.();
593+
this._vueCompatTooltip = null;
594+
},
595+
521596
teardownVueCompatCanvasEvents() {
522597
if (this._vueCompatHeightRedrawFrame != null) {
523598
if (typeof cancelAnimationFrame === 'function') {
@@ -536,6 +611,7 @@ export const nodeLifecycleMethods = {
536611
this._vueCompatCanvasElement = null;
537612
this._vueCompatCanvasHandlers = null;
538613
this._vueCompatForwardingNodePointer = false;
614+
this.teardownVueCompatTooltip();
539615
this.teardownVueCompatCanvasLayout();
540616
},
541617

tests/js/node_lifecycle.test.mjs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,57 @@ test("Vue output slot centers are measured relative to the widget canvas", () =>
175175
});
176176

177177

178+
test("Vue tooltip is rendered in the document overlay and constrained to the viewport", () => {
179+
const previousDocument = globalThis.document;
180+
const tooltip = {
181+
dataset: {},
182+
style: {},
183+
textContent: "",
184+
removed: false,
185+
getBoundingClientRect() {
186+
return { width: 120, height: 40 };
187+
},
188+
remove() {
189+
this.removed = true;
190+
}
191+
};
192+
globalThis.document = {
193+
documentElement: { clientWidth: 300, clientHeight: 200 },
194+
createElement() {
195+
return tooltip;
196+
},
197+
body: {
198+
appendChild() {}
199+
}
200+
};
201+
202+
try {
203+
const controller = createLifecycleController();
204+
controller.tooltips = { widthValueArea: "Click to enter width manually." };
205+
206+
controller.showVueCompatTooltip("widthValueArea", { clientX: 280, clientY: 20 });
207+
208+
assert.equal(tooltip.textContent, "Click to enter width manually.");
209+
assert.equal(tooltip.style.left, "145px");
210+
assert.equal(tooltip.style.top, "40px");
211+
assert.equal(tooltip.style.visibility, "visible");
212+
213+
controller.hideVueCompatTooltip();
214+
assert.equal(tooltip.style.display, "none");
215+
216+
controller.teardownVueCompatTooltip();
217+
assert.equal(tooltip.removed, true);
218+
assert.equal(controller._vueCompatTooltip, null);
219+
} finally {
220+
if (previousDocument === undefined) {
221+
delete globalThis.document;
222+
} else {
223+
globalThis.document = previousDocument;
224+
}
225+
}
226+
});
227+
228+
178229
test("LiteGraph lifecycle hooks synchronize serialization and clean up on removal", () => {
179230
const events = [];
180231
const widthWidget = { name: "width", value: 640 };

0 commit comments

Comments
 (0)