diff --git a/examples/index.html b/examples/index.html
index 3ed6fe2..2cd56e0 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -4,8 +4,15 @@
text-expander demo
@@ -30,6 +37,7 @@ Multiword text-expander element
const {key, provide, text} = event.detail
if (key === '#') {
const menu = document.createElement('ul')
+ menu.classList.add('menu')
menu.role = 'listbox'
for (const issue of [
'#1 Implement a text-expander element',
diff --git a/src/text-expander-element.ts b/src/text-expander-element.ts
index a03be12..48a413a 100644
--- a/src/text-expander-element.ts
+++ b/src/text-expander-element.ts
@@ -20,25 +20,6 @@ type Key = {
const states = new WeakMap()
-function isTopLayer(el: Element) {
- try {
- if (el.matches(':popover-open')) return true
- } catch {
- /* fall through */
- }
- try {
- if (el.matches('dialog:modal')) return true
- } catch {
- /* fall through */
- }
- try {
- if (el.matches(':fullscreen')) return true
- } catch {
- /* fall through */
- }
- return false
-}
-
class TextExpander {
expander: TextExpanderElement
input: HTMLInputElement | HTMLTextAreaElement
@@ -103,18 +84,7 @@ class TextExpander {
this.expander.dispatchEvent(new Event('text-expander-activate'))
- let {top, left} = new InputRange(this.input, match.position).getBoundingClientRect()
- if (isTopLayer(menu)) {
- const rect = this.input.getBoundingClientRect()
- top += rect.top
- left += rect.left
- if (getComputedStyle(menu).position === 'absolute') {
- top += window.scrollY
- left += window.scrollX
- }
- }
- menu.style.top = `${top}px`
- menu.style.left = `${left}px`
+ this.positionMenu(menu, match.position)
this.combobox.start()
menu.addEventListener('combobox-commit', this.oncommit)
@@ -124,6 +94,27 @@ class TextExpander {
this.combobox.navigate(1)
}
+ private positionMenu(menu: HTMLElement, position: number) {
+ const caretRect = new InputRange(this.input, position).getBoundingClientRect()
+ const targetPosition = {left: caretRect.left, top: caretRect.top + caretRect.height}
+
+ const currentPosition = menu.getBoundingClientRect()
+
+ const delta = {
+ left: targetPosition.left - currentPosition.left,
+ top: targetPosition.top - currentPosition.top
+ }
+
+ if (delta.left !== 0 || delta.top !== 0) {
+ // Use computedStyle to avoid nesting calc() deeper and deeper
+ const currentStyle = getComputedStyle(menu)
+
+ // Using `calc` avoids having to parse the current pixel value
+ menu.style.left = currentStyle.left ? `calc(${currentStyle.left} + ${delta.left}px)` : `${delta.left}px`
+ menu.style.top = currentStyle.top ? `calc(${currentStyle.top} + ${delta.top}px)` : `${delta.top}px`
+ }
+ }
+
private deactivate() {
const menu = this.menu
if (!menu || !this.combobox) return false