From 87f198df0f558aa60a33342f0802637fec30a8ed Mon Sep 17 00:00:00 2001 From: Ionut Colceriu Date: Mon, 15 Jan 2024 12:44:13 +0200 Subject: [PATCH 1/4] Upgrade outdated. --- package-lock.json | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 93033303..d0cf7012 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1351,9 +1351,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz", + "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1804,9 +1804,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.19.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.6.tgz", - "integrity": "sha512-X36s5CXMrrJOs2lQCdDF68apW4Rfx9ixYMawlepwmE4Anezv/AV2LSpKD1Ub8DAc+urp5bk0BGZ6NtmBitfnsg==", + "version": "18.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.7.tgz", + "integrity": "sha512-IGRJfoNX10N/PfrReRZ1br/7SQ+2vF/tK3KXNwzXz82D32z5dMQEoOlFew18nLSN+vMNcLY4GrKfzwi/yWI8/w==", "dependencies": { "undici-types": "~5.26.4" } @@ -4149,9 +4149,9 @@ "dev": true }, "node_modules/copy-webpack-plugin": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.0.tgz", - "integrity": "sha512-WQfxZKgoRB94/g0BP/cjADvE6jXQKoSk0lFFeIC5HZlE1JuabYPwhQUtOZsGI0cI+QMsi6vhdIDy4eNXKcTgFg==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.1.tgz", + "integrity": "sha512-dhMfjJMYKDmmbG6Yn2pRSs1g8FgeQRtbE/JM6VAM9Xouk3KO1UVrwlLHLXxaI5F+o9WgnRfhFZzY9eV34O2gZQ==", "dev": true, "dependencies": { "fast-glob": "^3.3.2", @@ -5055,9 +5055,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.628", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.628.tgz", - "integrity": "sha512-2k7t5PHvLsufpP6Zwk0nof62yLOsCf032wZx7/q0mv8gwlXjhcxI3lz6f0jBr0GrnWKcm3burXzI3t5IrcdUxw==", + "version": "1.4.630", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.630.tgz", + "integrity": "sha512-osHqhtjojpCsACVnuD11xO5g9xaCyw7Qqn/C2KParkMv42i8jrJJgx3g7mkHfpxwhy9MnOJr8+pKOdZ7qzgizg==", "dev": true }, "node_modules/emoji-regex": { @@ -12837,15 +12837,16 @@ } }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", "dev": true, "dependencies": { "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.1" }, "engines": { "node": ">= 0.4" From ae86950bae91af1d2942ed2671103e4e65a705d6 Mon Sep 17 00:00:00 2001 From: Ionut Colceriu Date: Mon, 15 Jan 2024 12:49:15 +0200 Subject: [PATCH 2/4] Fix issues with the bubble not showing up on outlook. useCapture for the focusin/out events in the bubble. --- src/content/bubble/bubble.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/bubble/bubble.js b/src/content/bubble/bubble.js index d3f52f99..d7df1239 100644 --- a/src/content/bubble/bubble.js +++ b/src/content/bubble/bubble.js @@ -169,8 +169,8 @@ function create (settings = {}) { document.documentElement.appendChild(bubbleInstance); // show the bubble on focus - document.addEventListener('focusin', focusTextfield); - document.addEventListener('focusout', blurTextfield); + document.addEventListener('focusin', focusTextfield, true); + document.addEventListener('focusout', blurTextfield, true); // reposition bubble on scroll document.addEventListener('scroll', scrollDocument, true); @@ -182,8 +182,8 @@ export function destroy () { bubbleInstance = null } - document.removeEventListener('focusin', focusTextfield); - document.removeEventListener('focusout', blurTextfield); + document.removeEventListener('focusin', focusTextfield, true); + document.removeEventListener('focusout', blurTextfield, true); document.removeEventListener('scroll', scrollDocument, true); // disconnect all observers From 2982601c07800d37e1ac0bbdac1d0c46b3b89d04 Mon Sep 17 00:00:00 2001 From: Ionut Colceriu Date: Mon, 15 Jan 2024 12:53:54 +0200 Subject: [PATCH 3/4] Tweak bubble formatting and indentation. --- src/content/bubble/bubble.js | 346 +++++++++++++++++------------------ 1 file changed, 173 insertions(+), 173 deletions(-) diff --git a/src/content/bubble/bubble.js b/src/content/bubble/bubble.js index d7df1239..093ab690 100644 --- a/src/content/bubble/bubble.js +++ b/src/content/bubble/bubble.js @@ -18,144 +18,144 @@ export const bubbleTagName = `b-bubble-${Date.now().toString(36)}` customElements.define( bubbleTagName, class extends HTMLElement { - constructor() { - super(); + constructor() { + super() - this.ready = false; - this.bubbleVisibilityTimer = null; + this.ready = false + this.bubbleVisibilityTimer = null + } + connectedCallback () { + // element was already created, + // just moved around in the dom. + if (this.ready || !this.isConnected) { + return } - connectedCallback () { - // element was already created, - // just moved around in the dom. - if (this.ready || !this.isConnected) { - return; - } - // dialog shortcut - const shortcut = this.getAttribute('shortcut') || 'ctrl+space'; - - const template = ` - - - - Search templates (${shortcut}) - - `; - const shadowRoot = this.attachShadow({mode: 'open'}); - shadowRoot.innerHTML = template; - - this.$button = this.shadowRoot.querySelector('.b-bubble'); - this.$button.addEventListener('mousedown', (e) => { - // prevent stealing focus when clicking the button - e.preventDefault(); - }); - this.$button.addEventListener('click', (e) => { - e.stopPropagation() - - // trigger the event on the bubble, to position the dialog next to it. - e.target.dispatchEvent(new CustomEvent(dialogShowEvent, { - bubbles: true, - composed: true, - })) - }); - - - this.ready = true; - } - attributeChangedCallback (name, oldValue, newValue) { - if (name === 'visible') { - const visibleClassName = 'b-bubble-visible'; - - if (this.bubbleVisibilityTimer) { - clearTimeout(this.bubbleVisibilityTimer); - } - - // timer makes the visible/not-visible state to be less "flickery" - // when rapidly focusing and blurring textfields, - // and makes the transitions be visible. - this.bubbleVisibilityTimer = setTimeout(() => { - if (newValue === 'true') { - this.$button.classList.add(visibleClassName); - } else { - this.$button.classList.remove(visibleClassName); - } - }, 200); - } + // dialog shortcut + const shortcut = this.getAttribute('shortcut') || 'ctrl+space' + + const template = ` + + + + Search templates (${shortcut}) + + ` + const shadowRoot = this.attachShadow({mode: 'open'}) + shadowRoot.innerHTML = template + + this.$button = this.shadowRoot.querySelector('.b-bubble') + this.$button.addEventListener('mousedown', (e) => { + // prevent stealing focus when clicking the button + e.preventDefault() + }) + this.$button.addEventListener('click', (e) => { + e.stopPropagation() + + // trigger the event on the bubble, to position the dialog next to it. + e.target.dispatchEvent(new CustomEvent(dialogShowEvent, { + bubbles: true, + composed: true, + })) + }) + + + this.ready = true + } + attributeChangedCallback (name, oldValue, newValue) { + if (name === 'visible') { + const visibleClassName = 'b-bubble-visible' - if (name === 'top' || name === 'right') { - this.style[name] = `${newValue}px`; + if (this.bubbleVisibilityTimer) { + clearTimeout(this.bubbleVisibilityTimer) + } + + // timer makes the visible/not-visible state to be less "flickery" + // when rapidly focusing and blurring textfields, + // and makes the transitions be visible. + this.bubbleVisibilityTimer = setTimeout(() => { + if (newValue === 'true') { + this.$button.classList.add(visibleClassName); + } else { + this.$button.classList.remove(visibleClassName); } + }, 200) } - static get observedAttributes() { - return [ - 'visible', - 'top', - 'right' - ]; + + if (name === 'top' || name === 'right') { + this.style[name] = `${newValue}px` } + } + static get observedAttributes() { + return [ + 'visible', + 'top', + 'right', + ] + } } ) function focusTextfield (e) { // used for showing the dialog completion - activeTextfield = e.target; + activeTextfield = e.target - return showBubble(e.target); + return showBubble(e.target) } function blurTextfield (e) { // don't hide the bubble if the newly focused node is in the dialog. // eg. when clicking the bubble. if (e.relatedTarget && e.relatedTarget.closest(dialogTagName)) { - return; + return } - return hideBubble(); + return hideBubble() } // reposition the bubble on scroll -let scrollTick = false; +let scrollTick = false function scrollDocument (e) { if (!scrollTick) { - window.requestAnimationFrame(() => { - if ( - e.target && - // must be an element node (eg. not the document) - e.target.nodeType === Node.ELEMENT_NODE && - e.target.contains(activeTextfield) && - bubbleInstance && - bubbleInstance.getAttribute('visible') === 'true' - ) { - bubbleInstance.setAttribute('top', getTopPosition(activeTextfield, e.target)); - } + window.requestAnimationFrame(() => { + if ( + e.target && + // must be an element node (eg. not the document) + e.target.nodeType === Node.ELEMENT_NODE && + e.target.contains(activeTextfield) && + bubbleInstance && + bubbleInstance.getAttribute('visible') === 'true' + ) { + bubbleInstance.setAttribute('top', getTopPosition(activeTextfield, e.target)) + } - scrollTick = false; - }); + scrollTick = false + }) - scrollTick = true; + scrollTick = true } } export function setup (settings = {}) { // if bubble is enabled in settings if (settings.dialog_button === false) { - return; + return } // if bubble is enabled in dom if (bubbleEnabled()) { - return create(settings); + return create(settings) } const domObserver = new MutationObserver((records, observer) => { - if (bubbleEnabled()) { - observer.disconnect(); - create(settings); - } + if (bubbleEnabled()) { + observer.disconnect() + create(settings) + } }); domObserver.observe(document.body, { - attributes: true - }); + attributes: true + }) domObservers.push(domObserver) } @@ -163,17 +163,17 @@ export function setup (settings = {}) { function create (settings = {}) { // bubble is created outside the body. // when textfields are focused, move it to the offsetParent for positioning. - bubbleInstance = document.createElement(bubbleTagName); + bubbleInstance = document.createElement(bubbleTagName) // custom dialog shortcut - bubbleInstance.setAttribute('shortcut', settings.dialog_shortcut); - document.documentElement.appendChild(bubbleInstance); + bubbleInstance.setAttribute('shortcut', settings.dialog_shortcut) + document.documentElement.appendChild(bubbleInstance) // show the bubble on focus - document.addEventListener('focusin', focusTextfield, true); - document.addEventListener('focusout', blurTextfield, true); + document.addEventListener('focusin', focusTextfield, true) + document.addEventListener('focusout', blurTextfield, true) // reposition bubble on scroll - document.addEventListener('scroll', scrollDocument, true); + document.addEventListener('scroll', scrollDocument, true) } export function destroy () { @@ -182,9 +182,9 @@ export function destroy () { bubbleInstance = null } - document.removeEventListener('focusin', focusTextfield, true); - document.removeEventListener('focusout', blurTextfield, true); - document.removeEventListener('scroll', scrollDocument, true); + document.removeEventListener('focusin', focusTextfield, true) + document.removeEventListener('focusout', blurTextfield, true) + document.removeEventListener('scroll', scrollDocument, true) // disconnect all observers domObservers.forEach((observer) => { @@ -195,101 +195,101 @@ export function destroy () { // top-right sticky positioning, // considering scroll. function getTopPosition (textfield, parent) { - const textfieldRect = textfield.getBoundingClientRect(); - const parentRect = parent.getBoundingClientRect(); - const distanceFromParent = textfieldRect.top - parentRect.top; + const textfieldRect = textfield.getBoundingClientRect() + const parentRect = parent.getBoundingClientRect() + const distanceFromParent = textfieldRect.top - parentRect.top - let top = textfield.offsetTop; + let top = textfield.offsetTop - // top position of textfield is scrolled out of view - if (distanceFromParent < 0) { - top = top + Math.abs(distanceFromParent); - } + // top position of textfield is scrolled out of view + if (distanceFromParent < 0) { + top = top + Math.abs(distanceFromParent) + } - return top; + return top } function isValidTextfield (elem) { - // if the element is a textfield - if (elem.matches('textarea, [contenteditable]')) { - // check if the element is big enough - // to only show the bubble for large textfields - const metrics = elem.getBoundingClientRect(); - if (metrics.width > 100 && metrics.height > 34) { - return true; - } + // if the element is a textfield + if (elem.matches('textarea, [contenteditable]')) { + // check if the element is big enough + // to only show the bubble for large textfields + const metrics = elem.getBoundingClientRect(); + if (metrics.width > 100 && metrics.height > 34) { + return true } + } - return false; + return false } // finds the first offsetParent that could be scrollable function findScrollParent (target) { - const parent = target.offsetParent; - if (!parent) { - return null; - } - const parentStyles = window.getComputedStyle(parent); - - const scrollValues = [ - 'scroll', - 'auto', - // used by outlook.com, draws scrollbars on top of the content. - // deprecated form the spec and only supported in webkit-like browsers. - // https://developer.mozilla.org/en-US/docs/Web/CSS/overflow - 'overlay' - ]; - if ( - scrollValues.includes(parentStyles.overflow) || - scrollValues.includes(parentStyles.overflowY) - ) { - return parent; - } + const parent = target.offsetParent + if (!parent) { + return null + } + const parentStyles = window.getComputedStyle(parent) + + const scrollValues = [ + 'scroll', + 'auto', + // used by outlook.com, draws scrollbars on top of the content. + // deprecated form the spec and only supported in webkit-like browsers. + // https://developer.mozilla.org/en-US/docs/Web/CSS/overflow + 'overlay', + ] + if ( + scrollValues.includes(parentStyles.overflow) || + scrollValues.includes(parentStyles.overflowY) + ) { + return parent + } - return findScrollParent(parent); + return findScrollParent(parent) } function showBubble (textfield) { - // only show it for valid elements - if (!isValidTextfield(textfield)) { - return false; - } - - // detect rtl - const textfieldStyles = window.getComputedStyle(textfield); - const direction = textfieldStyles.direction || 'ltr'; - bubbleInstance.setAttribute('dir', direction); + // only show it for valid elements + if (!isValidTextfield(textfield)) { + return false + } - const offsetParent = textfield.offsetParent; + // detect rtl + const textfieldStyles = window.getComputedStyle(textfield) + const direction = textfieldStyles.direction || 'ltr' + bubbleInstance.setAttribute('dir', direction) - if (offsetParent) { - const offsetStyles = window.getComputedStyle(offsetParent); + const offsetParent = textfield.offsetParent - // in case the offsetParent is a unpositioned table element (td, th, table) - // make it relative, for the button to have the correct positioning. - // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent - if (offsetStyles.position === 'static') { - offsetParent.style.position = 'relative'; - } + if (offsetParent) { + const offsetStyles = window.getComputedStyle(offsetParent) - // position the element relative to it's offsetParent - const offsetRight = offsetParent.offsetWidth - textfield.offsetLeft - textfield.offsetWidth; + // in case the offsetParent is a unpositioned table element (td, th, table) + // make it relative, for the button to have the correct positioning. + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent + if (offsetStyles.position === 'static') { + offsetParent.style.position = 'relative' + } - let top = textfield.offsetTop; - const scrollParent = findScrollParent(textfield); - if (scrollParent) { - top = getTopPosition(textfield, scrollParent); - } + // position the element relative to it's offsetParent + const offsetRight = offsetParent.offsetWidth - textfield.offsetLeft - textfield.offsetWidth - offsetParent.appendChild(bubbleInstance); - bubbleInstance.setAttribute('right', offsetRight); - bubbleInstance.setAttribute('top', top); - bubbleInstance.setAttribute('visible', 'true'); + let top = textfield.offsetTop; + const scrollParent = findScrollParent(textfield) + if (scrollParent) { + top = getTopPosition(textfield, scrollParent) } + + offsetParent.appendChild(bubbleInstance) + bubbleInstance.setAttribute('right', offsetRight) + bubbleInstance.setAttribute('top', top) + bubbleInstance.setAttribute('visible', 'true') + } } function hideBubble () { - bubbleInstance.removeAttribute('visible'); + bubbleInstance.removeAttribute('visible') } export function enableBubble () { From 88024988f88cb22cd1135a81266f229876ad93a0 Mon Sep 17 00:00:00 2001 From: Ionut Colceriu Date: Mon, 15 Jan 2024 12:57:14 +0200 Subject: [PATCH 4/4] Bump version. --- package-lock.json | 4 ++-- package.json | 2 +- src/content/bubble/bubble.js | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index d0cf7012..e81b0457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "briskine", - "version": "7.10.6", + "version": "7.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "briskine", - "version": "7.10.6", + "version": "7.10.7", "license": "GPL-3.0-or-later", "dependencies": { "@webcomponents/custom-elements": "^1.4.3", diff --git a/package.json b/package.json index 2a1d14b6..98962bb3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "briskine", - "version": "7.10.6", + "version": "7.10.7", "description": "Write everything faster.", "private": true, "type": "module", diff --git a/src/content/bubble/bubble.js b/src/content/bubble/bubble.js index 093ab690..85a30e99 100644 --- a/src/content/bubble/bubble.js +++ b/src/content/bubble/bubble.js @@ -75,9 +75,9 @@ customElements.define( // and makes the transitions be visible. this.bubbleVisibilityTimer = setTimeout(() => { if (newValue === 'true') { - this.$button.classList.add(visibleClassName); + this.$button.classList.add(visibleClassName) } else { - this.$button.classList.remove(visibleClassName); + this.$button.classList.remove(visibleClassName) } }, 200) } @@ -152,7 +152,7 @@ export function setup (settings = {}) { observer.disconnect() create(settings) } - }); + }) domObserver.observe(document.body, { attributes: true }) @@ -214,7 +214,7 @@ function isValidTextfield (elem) { if (elem.matches('textarea, [contenteditable]')) { // check if the element is big enough // to only show the bubble for large textfields - const metrics = elem.getBoundingClientRect(); + const metrics = elem.getBoundingClientRect() if (metrics.width > 100 && metrics.height > 34) { return true } @@ -275,7 +275,7 @@ function showBubble (textfield) { // position the element relative to it's offsetParent const offsetRight = offsetParent.offsetWidth - textfield.offsetLeft - textfield.offsetWidth - let top = textfield.offsetTop; + let top = textfield.offsetTop const scrollParent = findScrollParent(textfield) if (scrollParent) { top = getTopPosition(textfield, scrollParent)