Pop-up dictionary browser extension for language learning. Successor to Yomichan. (PERSONAL FORK)
at lambda-fork/main 135 lines 3.7 kB view raw
1/* 2 * Copyright (C) 2023-2025 Yomitan Authors 3 * Copyright (C) 2017-2022 Yomichan Authors 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19import {EventListenerCollection} from '../core/event-listener-collection.js'; 20import {querySelectorNotNull} from '../dom/query-selector.js'; 21 22export class DisplayNotification { 23 /** 24 * @param {HTMLElement} container 25 * @param {HTMLElement} node 26 */ 27 constructor(container, node) { 28 /** @type {HTMLElement} */ 29 this._container = container; 30 /** @type {HTMLElement} */ 31 this._node = node; 32 /** @type {HTMLElement} */ 33 this._body = querySelectorNotNull(node, '.footer-notification-body'); 34 /** @type {HTMLElement} */ 35 this._closeButton = querySelectorNotNull(node, '.footer-notification-close-button'); 36 /** @type {EventListenerCollection} */ 37 this._eventListeners = new EventListenerCollection(); 38 /** @type {?import('core').Timeout} */ 39 this._closeTimer = null; 40 } 41 42 /** @type {HTMLElement} */ 43 get container() { 44 return this._container; 45 } 46 47 /** @type {HTMLElement} */ 48 get node() { 49 return this._node; 50 } 51 52 /** */ 53 open() { 54 if (!this.isClosed()) { return; } 55 56 this._clearTimer(); 57 58 const node = this._node; 59 this._container.appendChild(node); 60 const style = getComputedStyle(node); 61 node.hidden = true; 62 style.getPropertyValue('opacity'); // Force CSS update, allowing animation 63 node.hidden = false; 64 this._eventListeners.addEventListener(this._closeButton, 'click', this._onCloseButtonClick.bind(this), false); 65 } 66 67 /** 68 * @param {boolean} [animate] 69 */ 70 close(animate = false) { 71 if (this.isClosed()) { return; } 72 73 if (animate) { 74 if (this._closeTimer !== null) { return; } 75 76 this._node.hidden = true; 77 this._closeTimer = setTimeout(this._onDelayClose.bind(this), 200); 78 } else { 79 this._clearTimer(); 80 81 this._eventListeners.removeAllEventListeners(); 82 const parent = this._node.parentNode; 83 if (parent !== null) { 84 parent.removeChild(this._node); 85 } 86 } 87 } 88 89 /** 90 * @param {string|Node} value 91 */ 92 setContent(value) { 93 if (typeof value === 'string') { 94 this._body.textContent = value; 95 } else { 96 this._body.textContent = ''; 97 this._body.appendChild(value); 98 } 99 } 100 101 /** 102 * @returns {boolean} 103 */ 104 isClosing() { 105 return this._closeTimer !== null; 106 } 107 108 /** 109 * @returns {boolean} 110 */ 111 isClosed() { 112 return this._node.parentNode === null; 113 } 114 115 // Private 116 117 /** */ 118 _onCloseButtonClick() { 119 this.close(true); 120 } 121 122 /** */ 123 _onDelayClose() { 124 this._closeTimer = null; 125 this.close(false); 126 } 127 128 /** */ 129 _clearTimer() { 130 if (this._closeTimer !== null) { 131 clearTimeout(this._closeTimer); 132 this._closeTimer = null; 133 } 134 } 135}