UNPKG

@theia/core

Version:

Theia is a cloud & desktop IDE framework implemented in TypeScript.

208 lines • 9.5 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2022 Ericsson and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 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; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var HoverService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.HoverService = exports.HoverPosition = void 0; const inversify_1 = require("inversify"); const common_1 = require("../common"); const browser_1 = require("./browser"); const markdown_renderer_1 = require("./markdown-rendering/markdown-renderer"); const preferences_1 = require("./preferences"); require("../../src/browser/style/hover-service.css"); var HoverPosition; (function (HoverPosition) { function invertIfNecessary(position, target, host, totalWidth, totalHeight) { if (position === 'left') { if (target.left - host.width - 5 < 0) { return 'right'; } } else if (position === 'right') { if (target.right + host.width + 5 > totalWidth) { return 'left'; } } else if (position === 'top') { if (target.top - host.height - 5 < 0) { return 'bottom'; } } else if (position === 'bottom') { if (target.bottom + host.height + 5 > totalHeight) { return 'top'; } } return position; } HoverPosition.invertIfNecessary = invertIfNecessary; })(HoverPosition = exports.HoverPosition || (exports.HoverPosition = {})); let HoverService = HoverService_1 = class HoverService { constructor() { this.lastHidHover = Date.now(); this.disposeOnHide = new common_1.DisposableCollection(); } get markdownRenderer() { this._markdownRenderer || (this._markdownRenderer = this.markdownRendererFactory()); return this._markdownRenderer; } get hoverHost() { if (!this._hoverHost) { this._hoverHost = document.createElement('div'); this._hoverHost.classList.add(HoverService_1.hostClassName); this._hoverHost.style.position = 'absolute'; } return this._hoverHost; } requestHover(request) { if (request.target !== this.hoverTarget) { this.cancelHover(); this.pendingTimeout = (0, common_1.disposableTimeout)(() => this.renderHover(request), this.getHoverDelay()); } } getHoverDelay() { return Date.now() - this.lastHidHover < 200 ? 0 : this.preferences.get('workbench.hover.delay', common_1.isOSX ? 1500 : 500); } async renderHover(request) { const host = this.hoverHost; let firstChild; const { target, content, position, cssClasses } = request; if (cssClasses) { host.classList.add(...cssClasses); } this.hoverTarget = target; if (content instanceof HTMLElement) { host.appendChild(content); firstChild = content; } else if (typeof content === 'string') { host.textContent = content; } else { const renderedContent = this.markdownRenderer.render(content); this.disposeOnHide.push(renderedContent); host.appendChild(renderedContent.element); firstChild = renderedContent.element; } // browsers might insert linebreaks when the hover appears at the edge of the window // resetting the position prevents that host.style.left = '0px'; host.style.top = '0px'; document.body.append(host); if (request.visualPreview) { // If just a string is being rendered use the size of the outer box const width = firstChild ? firstChild.offsetWidth : this.hoverHost.offsetWidth; const visualPreview = request.visualPreview(width); if (visualPreview) { host.appendChild(visualPreview); } } await (0, browser_1.animationFrame)(); // Allow the browser to size the host const updatedPosition = this.setHostPosition(target, host, position); this.disposeOnHide.push({ dispose: () => { this.lastHidHover = Date.now(); host.classList.remove(updatedPosition); if (cssClasses) { host.classList.remove(...cssClasses); } } }); this.listenForMouseOut(); } setHostPosition(target, host, position) { const targetDimensions = target.getBoundingClientRect(); const hostDimensions = host.getBoundingClientRect(); const documentWidth = document.body.getBoundingClientRect().width; // document.body.getBoundingClientRect().height doesn't work as expected // scrollHeight will always be accurate here: https://stackoverflow.com/a/44077777 const documentHeight = document.documentElement.scrollHeight; position = HoverPosition.invertIfNecessary(position, targetDimensions, hostDimensions, documentWidth, documentHeight); if (position === 'top' || position === 'bottom') { const targetMiddleWidth = targetDimensions.left + (targetDimensions.width / 2); const middleAlignment = targetMiddleWidth - (hostDimensions.width / 2); const furthestRight = Math.min(documentWidth - hostDimensions.width, middleAlignment); const left = Math.max(0, furthestRight); const top = position === 'top' ? targetDimensions.top - hostDimensions.height - 5 : targetDimensions.bottom + 5; host.style.setProperty('--theia-hover-before-position', `${targetMiddleWidth - left - 5}px`); host.style.top = `${top}px`; host.style.left = `${left}px`; } else { const targetMiddleHeight = targetDimensions.top + (targetDimensions.height / 2); const middleAlignment = targetMiddleHeight - (hostDimensions.height / 2); const furthestTop = Math.min(documentHeight - hostDimensions.height, middleAlignment); const top = Math.max(0, furthestTop); const left = position === 'left' ? targetDimensions.left - hostDimensions.width - 5 : targetDimensions.right + 5; host.style.setProperty('--theia-hover-before-position', `${targetMiddleHeight - top - 5}px`); host.style.left = `${left}px`; host.style.top = `${top}px`; } host.classList.add(position); return position; } listenForMouseOut() { const handleMouseMove = (e) => { var _a; if (e.target instanceof Node && !this.hoverHost.contains(e.target) && !((_a = this.hoverTarget) === null || _a === void 0 ? void 0 : _a.contains(e.target))) { this.cancelHover(); } }; document.addEventListener('mousemove', handleMouseMove); this.disposeOnHide.push({ dispose: () => document.removeEventListener('mousemove', handleMouseMove) }); } cancelHover() { var _a; (_a = this.pendingTimeout) === null || _a === void 0 ? void 0 : _a.dispose(); this.unRenderHover(); this.disposeOnHide.dispose(); this.hoverTarget = undefined; } unRenderHover() { this.hoverHost.remove(); this.hoverHost.replaceChildren(); } }; HoverService.hostClassName = 'theia-hover'; HoverService.styleSheetId = 'theia-hover-style'; __decorate([ (0, inversify_1.inject)(preferences_1.PreferenceService), __metadata("design:type", Object) ], HoverService.prototype, "preferences", void 0); __decorate([ (0, inversify_1.inject)(markdown_renderer_1.MarkdownRendererFactory), __metadata("design:type", Function) ], HoverService.prototype, "markdownRendererFactory", void 0); HoverService = HoverService_1 = __decorate([ (0, inversify_1.injectable)() ], HoverService); exports.HoverService = HoverService; //# sourceMappingURL=hover-service.js.map