UNPKG

9.5 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2022 Ericsson and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16// *****************************************************************************
17var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
18 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
19 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
20 else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
21 return c > 3 && r && Object.defineProperty(target, key, r), r;
22};
23var __metadata = (this && this.__metadata) || function (k, v) {
24 if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
25};
26var HoverService_1;
27Object.defineProperty(exports, "__esModule", { value: true });
28exports.HoverService = exports.HoverPosition = void 0;
29const inversify_1 = require("inversify");
30const common_1 = require("../common");
31const browser_1 = require("./browser");
32const markdown_renderer_1 = require("./markdown-rendering/markdown-renderer");
33const preferences_1 = require("./preferences");
34require("../../src/browser/style/hover-service.css");
35var HoverPosition;
36(function (HoverPosition) {
37 function invertIfNecessary(position, target, host, totalWidth, totalHeight) {
38 if (position === 'left') {
39 if (target.left - host.width - 5 < 0) {
40 return 'right';
41 }
42 }
43 else if (position === 'right') {
44 if (target.right + host.width + 5 > totalWidth) {
45 return 'left';
46 }
47 }
48 else if (position === 'top') {
49 if (target.top - host.height - 5 < 0) {
50 return 'bottom';
51 }
52 }
53 else if (position === 'bottom') {
54 if (target.bottom + host.height + 5 > totalHeight) {
55 return 'top';
56 }
57 }
58 return position;
59 }
60 HoverPosition.invertIfNecessary = invertIfNecessary;
61})(HoverPosition = exports.HoverPosition || (exports.HoverPosition = {}));
62let HoverService = HoverService_1 = class HoverService {
63 constructor() {
64 this.lastHidHover = Date.now();
65 this.disposeOnHide = new common_1.DisposableCollection();
66 }
67 get markdownRenderer() {
68 this._markdownRenderer || (this._markdownRenderer = this.markdownRendererFactory());
69 return this._markdownRenderer;
70 }
71 get hoverHost() {
72 if (!this._hoverHost) {
73 this._hoverHost = document.createElement('div');
74 this._hoverHost.classList.add(HoverService_1.hostClassName);
75 this._hoverHost.style.position = 'absolute';
76 }
77 return this._hoverHost;
78 }
79 requestHover(request) {
80 if (request.target !== this.hoverTarget) {
81 this.cancelHover();
82 this.pendingTimeout = (0, common_1.disposableTimeout)(() => this.renderHover(request), this.getHoverDelay());
83 }
84 }
85 getHoverDelay() {
86 return Date.now() - this.lastHidHover < 200
87 ? 0
88 : this.preferences.get('workbench.hover.delay', common_1.isOSX ? 1500 : 500);
89 }
90 async renderHover(request) {
91 const host = this.hoverHost;
92 let firstChild;
93 const { target, content, position, cssClasses } = request;
94 if (cssClasses) {
95 host.classList.add(...cssClasses);
96 }
97 this.hoverTarget = target;
98 if (content instanceof HTMLElement) {
99 host.appendChild(content);
100 firstChild = content;
101 }
102 else if (typeof content === 'string') {
103 host.textContent = content;
104 }
105 else {
106 const renderedContent = this.markdownRenderer.render(content);
107 this.disposeOnHide.push(renderedContent);
108 host.appendChild(renderedContent.element);
109 firstChild = renderedContent.element;
110 }
111 // browsers might insert linebreaks when the hover appears at the edge of the window
112 // resetting the position prevents that
113 host.style.left = '0px';
114 host.style.top = '0px';
115 document.body.append(host);
116 if (request.visualPreview) {
117 // If just a string is being rendered use the size of the outer box
118 const width = firstChild ? firstChild.offsetWidth : this.hoverHost.offsetWidth;
119 const visualPreview = request.visualPreview(width);
120 if (visualPreview) {
121 host.appendChild(visualPreview);
122 }
123 }
124 await (0, browser_1.animationFrame)(); // Allow the browser to size the host
125 const updatedPosition = this.setHostPosition(target, host, position);
126 this.disposeOnHide.push({
127 dispose: () => {
128 this.lastHidHover = Date.now();
129 host.classList.remove(updatedPosition);
130 if (cssClasses) {
131 host.classList.remove(...cssClasses);
132 }
133 }
134 });
135 this.listenForMouseOut();
136 }
137 setHostPosition(target, host, position) {
138 const targetDimensions = target.getBoundingClientRect();
139 const hostDimensions = host.getBoundingClientRect();
140 const documentWidth = document.body.getBoundingClientRect().width;
141 // document.body.getBoundingClientRect().height doesn't work as expected
142 // scrollHeight will always be accurate here: https://stackoverflow.com/a/44077777
143 const documentHeight = document.documentElement.scrollHeight;
144 position = HoverPosition.invertIfNecessary(position, targetDimensions, hostDimensions, documentWidth, documentHeight);
145 if (position === 'top' || position === 'bottom') {
146 const targetMiddleWidth = targetDimensions.left + (targetDimensions.width / 2);
147 const middleAlignment = targetMiddleWidth - (hostDimensions.width / 2);
148 const furthestRight = Math.min(documentWidth - hostDimensions.width, middleAlignment);
149 const left = Math.max(0, furthestRight);
150 const top = position === 'top'
151 ? targetDimensions.top - hostDimensions.height - 5
152 : targetDimensions.bottom + 5;
153 host.style.setProperty('--theia-hover-before-position', `${targetMiddleWidth - left - 5}px`);
154 host.style.top = `${top}px`;
155 host.style.left = `${left}px`;
156 }
157 else {
158 const targetMiddleHeight = targetDimensions.top + (targetDimensions.height / 2);
159 const middleAlignment = targetMiddleHeight - (hostDimensions.height / 2);
160 const furthestTop = Math.min(documentHeight - hostDimensions.height, middleAlignment);
161 const top = Math.max(0, furthestTop);
162 const left = position === 'left'
163 ? targetDimensions.left - hostDimensions.width - 5
164 : targetDimensions.right + 5;
165 host.style.setProperty('--theia-hover-before-position', `${targetMiddleHeight - top - 5}px`);
166 host.style.left = `${left}px`;
167 host.style.top = `${top}px`;
168 }
169 host.classList.add(position);
170 return position;
171 }
172 listenForMouseOut() {
173 const handleMouseMove = (e) => {
174 var _a;
175 if (e.target instanceof Node && !this.hoverHost.contains(e.target) && !((_a = this.hoverTarget) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
176 this.cancelHover();
177 }
178 };
179 document.addEventListener('mousemove', handleMouseMove);
180 this.disposeOnHide.push({ dispose: () => document.removeEventListener('mousemove', handleMouseMove) });
181 }
182 cancelHover() {
183 var _a;
184 (_a = this.pendingTimeout) === null || _a === void 0 ? void 0 : _a.dispose();
185 this.unRenderHover();
186 this.disposeOnHide.dispose();
187 this.hoverTarget = undefined;
188 }
189 unRenderHover() {
190 this.hoverHost.remove();
191 this.hoverHost.replaceChildren();
192 }
193};
194HoverService.hostClassName = 'theia-hover';
195HoverService.styleSheetId = 'theia-hover-style';
196__decorate([
197 (0, inversify_1.inject)(preferences_1.PreferenceService),
198 __metadata("design:type", Object)
199], HoverService.prototype, "preferences", void 0);
200__decorate([
201 (0, inversify_1.inject)(markdown_renderer_1.MarkdownRendererFactory),
202 __metadata("design:type", Function)
203], HoverService.prototype, "markdownRendererFactory", void 0);
204HoverService = HoverService_1 = __decorate([
205 (0, inversify_1.injectable)()
206], HoverService);
207exports.HoverService = HoverService;
208//# sourceMappingURL=hover-service.js.map
\No newline at end of file