diff --git a/package-lock.json b/package-lock.json
index 93033303..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",
@@ -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"
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 d3f52f99..85a30e99 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 (this.bubbleVisibilityTimer) {
+ clearTimeout(this.bubbleVisibilityTimer)
+ }
- if (name === 'top' || name === 'right') {
- this.style[name] = `${newValue}px`;
+ // 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);
- document.addEventListener('focusout', blurTextfield);
+ 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);
- document.removeEventListener('focusout', blurTextfield);
- 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 () {