• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2020 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import {DOM, V8CustomElement} from './helper.mjs';
6
7DOM.defineCustomElement(
8    'view/tool-tip', (templateText) => class Tooltip extends V8CustomElement {
9      _targetNode;
10      _content;
11      _isHidden = true;
12
13      constructor() {
14        super(templateText);
15        this._intersectionObserver = new IntersectionObserver((entries) => {
16          if (entries[0].intersectionRatio <= 0) {
17            this.hide();
18          } else {
19            this.show();
20            this.requestUpdate(true);
21          }
22        });
23        document.addEventListener('click', (event) => {
24          // Only hide the tooltip if we click anywhere outside of it.
25          let target = event.target;
26          while (target) {
27            if (target == this) return;
28            target = target.parentNode;
29          }
30          this.hide()
31        });
32      }
33
34      _update() {
35        if (!this._targetNode || this._isHidden) return;
36        const rect = this._targetNode.getBoundingClientRect();
37        rect.x += rect.width / 2;
38        let atRight = this._useRight(rect.x);
39        let atBottom = this._useBottom(rect.y);
40        if (atBottom) rect.y += rect.height;
41        this._setPosition(rect, atRight, atBottom);
42        this.requestUpdate(true);
43      }
44
45      set positionOrTargetNode(positionOrTargetNode) {
46        if (positionOrTargetNode.nodeType === undefined) {
47          this.position = positionOrTargetNode;
48        } else {
49          this.targetNode = positionOrTargetNode;
50        }
51      }
52
53      set targetNode(targetNode) {
54        this._intersectionObserver.disconnect();
55        this._targetNode = targetNode;
56        if (targetNode === undefined) return;
57        if (!(targetNode instanceof SVGElement)) {
58          this._intersectionObserver.observe(targetNode);
59        }
60        this.requestUpdate(true);
61      }
62
63      set position(position) {
64        this._targetNode = undefined;
65        this._setPosition(
66            position, this._useRight(position.x), this._useBottom(position.y));
67      }
68
69      _setPosition(viewportPosition, atRight, atBottom) {
70        const horizontalMode = atRight ? 'right' : 'left';
71        const verticalMode = atBottom ? 'bottom' : 'top';
72        this.bodyNode.className = horizontalMode + ' ' + verticalMode;
73        const pageX = viewportPosition.x + window.scrollX;
74        this.style.left = `${pageX}px`;
75        const pageY = viewportPosition.y + window.scrollY;
76        this.style.top = `${pageY}px`;
77      }
78
79      _useBottom(viewportY) {
80        return viewportY <= 400;
81      }
82
83      _useRight(viewportX) {
84        return viewportX < document.documentElement.clientWidth / 2;
85      }
86
87      set content(content) {
88        if (!content) return this.hide();
89        this.show();
90        if (this._content === content) return;
91        this._content = content;
92
93        if (typeof content === 'string') {
94          this.contentNode.innerHTML = content;
95          this.contentNode.className = 'textContent';
96        } else if (content?.nodeType && content?.nodeName) {
97          this._setContentNode(content);
98        } else {
99          if (this.contentNode.firstChild?.localName == 'property-link-table') {
100            this.contentNode.firstChild.propertyDict = content;
101          } else {
102            const node = DOM.element('property-link-table');
103            node.instanceLinkButtons = true;
104            node.propertyDict = content;
105            this._setContentNode(node);
106          }
107        }
108      }
109
110      _setContentNode(content) {
111        const newContent = DOM.div();
112        newContent.appendChild(content);
113        this.contentNode.replaceWith(newContent);
114        newContent.id = 'content';
115      }
116
117      hide() {
118        this._content = undefined;
119        if (this._isHidden) return;
120        this._isHidden = true;
121        this.bodyNode.style.display = 'none';
122        this.targetNode = undefined;
123      }
124
125      show() {
126        if (!this._isHidden) return;
127        this.bodyNode.style.display = 'block';
128        this._isHidden = false;
129      }
130
131      get bodyNode() {
132        return this.$('#body');
133      }
134
135      get contentNode() {
136        return this.$('#content');
137      }
138    });
139