// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import {Debouncer, DOM, V8CustomElement} from './helper.mjs'; DOM.defineCustomElement('view/tool-tip', (templateText) => class Tooltip extends V8CustomElement { _targetNode; _content; _isHidden = true; _debouncedSetData = new Debouncer((...args) => this._setData(...args), 500) constructor() { super(templateText); this._intersectionObserver = new IntersectionObserver((entries) => { if (entries[0].intersectionRatio <= 0) { this.hide(); } else { this.show(); this.requestUpdate(true); } }); document.addEventListener('click', (event) => { // Only hide the tooltip if we click anywhere outside of it. let target = event.target; while (target) { if (target == this) return; target = target.parentNode; } this.hide() }); } _update() { if (!this._targetNode || this._isHidden) return; if (!this._targetNode.parentNode) return; const rect = this._targetNode.getBoundingClientRect(); rect.x += rect.width / 2; let atRight = this._useRight(rect.x); let atBottom = this._useBottom(rect.y); if (atBottom) rect.y += rect.height; this._setPosition(rect, atRight, atBottom); this.requestUpdate(true); } set data({content, positionOrTargetNode, immediate}) { if (immediate) { this._debouncedSetData.callNow(content, positionOrTargetNode) } else { this._debouncedSetData.call(content, positionOrTargetNode) } } _setData(content, positionOrTargetNode) { if (positionOrTargetNode.nodeType === undefined) { this._targetNode = undefined; const position = positionOrTargetNode; this._setPosition( position, this._useRight(position.x), this._useBottom(position.y)); } else { this._setTargetNode(positionOrTargetNode); } this._setContent(content); } _setTargetNode(targetNode) { this._intersectionObserver.disconnect(); this._targetNode = targetNode; if (targetNode === undefined) return; if (!(targetNode instanceof SVGElement)) { this._intersectionObserver.observe(targetNode); } this.requestUpdate(true); } _setPosition(viewportPosition, atRight, atBottom) { const horizontalMode = atRight ? 'right' : 'left'; const verticalMode = atBottom ? 'bottom' : 'top'; this.bodyNode.className = horizontalMode + ' ' + verticalMode; const pageX = viewportPosition.x + window.scrollX; this.style.left = `${pageX}px`; const pageY = viewportPosition.y + window.scrollY; this.style.top = `${pageY}px`; } _useBottom(viewportY) { return viewportY <= 400; } _useRight(viewportX) { return viewportX < document.documentElement.clientWidth / 2; } _setContent(content) { if (!content) return this.hide(); this.show(); if (this._content === content) return; this._content = content; if (typeof content === 'string') { this.contentNode.innerHTML = content; this.contentNode.className = 'textContent'; } else if (content?.nodeType && content?.nodeName) { this._setContentNode(content); } else { if (this.contentNode.firstChild?.localName == 'property-link-table') { this.contentNode.firstChild.propertyDict = content; } else { const node = DOM.element('property-link-table'); node.instanceLinkButtons = true; node.propertyDict = content; this._setContentNode(node); } } } _setContentNode(content) { const newContent = DOM.div(); newContent.appendChild(content); this.contentNode.replaceWith(newContent); newContent.id = 'content'; } hide() { this._content = undefined; if (this._isHidden) return; this._isHidden = true; this.bodyNode.style.display = 'none'; this.targetNode = undefined; } show() { if (!this._isHidden) return; this.bodyNode.style.display = 'block'; this._isHidden = false; } get bodyNode() { return this.$('#body'); } get contentNode() { return this.$('#content'); } });