Bläddra i källkod

Refactor and tweak listbox

Caleb Porzio 2 år sedan
förälder
incheckning
4d033840f9
2 ändrade filer med 69 tillägg och 18 borttagningar
  1. 68 17
      packages/ui/src/list-context.js
  2. 1 1
      packages/ui/src/listbox.js

+ 68 - 17
packages/ui/src/list-context.js

@@ -36,9 +36,45 @@ export function generateContext(multiple) {
             return key
         },
 
+        destroyItem(el) {
+            let key = keyByValue(this.elsByKey, el)
+
+            delete this.values[key]
+            delete this.elsByKey[key]
+            delete this.orderedKeys[this.orderedKeys.indexOf(key)]
+            delete this.searchableText[key]
+            delete this.disabledKeys[key]
+
+            this.reorderKeys()
+        },
+
         /**
          * Handle elements...
          */
+         reorderKeys() {
+            // Filter out elements removed from the dom...
+            this.orderedKeys.forEach((key) => {
+                let el = this.elsByKey[key]
+
+                if (el.isConnected) return
+
+                this.destroyItem(el)
+            })
+
+            this.orderedKeys = this.orderedKeys.slice().sort((a, z) => {
+                if (a === null || z === null) return 0
+
+                let aEl = this.elsByKey[a]
+                let zEl = this.elsByKey[z]
+
+                let position = aEl.compareDocumentPosition(zEl)
+
+                if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1
+                if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1
+                return 0
+            })
+        },
+
         activeEl() {
             if (! this.activeKey) return
 
@@ -85,8 +121,29 @@ export function generateContext(multiple) {
             return this.isDisabled(key)
         },
 
-        scrollToKey(key) {
-            this.elsByKey[key].scrollIntoView({ block: 'nearest' })
+        get isScrollingTo() { return this.scrollingCount > 0 },
+
+        scrollingCount: 0,
+
+        activateAndScrollToKey(key) {
+            // This addresses the following problem:
+            // If deactivate is hooked up to mouseleave,
+            // scrolling to an element will trigger deactivation.
+            // This "isScrollingTo" is exposed to prevent that.
+            this.scrollingCount++
+
+            this.activateKey(key)
+
+            let targetEl = this.elsByKey[key]
+
+            targetEl.scrollIntoView({ block: 'nearest' })
+
+            setTimeout(() => {
+                this.scrollingCount--
+            // Unfortunately, browser experimentation has shown me
+            // that 25ms is the sweet spot when holding down an
+            // arrow key to scroll the list of items...
+            }, 25)
         },
 
         /**
@@ -181,12 +238,16 @@ export function generateContext(multiple) {
             this.activeKey = key
         },
 
-        deactivate() { return this.activeKey = null },
+        deactivate() {
+            if (! this.activeKey) return
+            if (this.isScrollingTo) return
+
+            this.activeKey = null
+        },
 
         /**
          * Handle active key traveral...
          */
-
         nextKey() {
             if (! this.activeKey) return
 
@@ -233,6 +294,8 @@ export function generateContext(multiple) {
         },
 
         activateByKeyEvent(e) {
+            this.reorderKeys()
+
             let hasActive = this.hasActive()
 
             let targetKey
@@ -274,9 +337,7 @@ export function generateContext(multiple) {
             }
 
             if (targetKey) {
-                this.activateKey(targetKey)
-
-                setTimeout(() => this.scrollToKey(targetKey))
+                this.activateAndScrollToKey(targetKey)
             }
         }
     }
@@ -286,15 +347,5 @@ function keyByValue(object, value) {
     return Object.keys(object).find(key => object[key] === value)
 }
 
-// reorderList() {
-//     this.items = this.items.slice().sort((a, z) => {
-//         if (a === null || z === null) return 0
-
-//         let position = a.el.compareDocumentPosition(z.el)
 
-//         if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1
-//         if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1
-//         return 0
-//     })
-// },
 

+ 1 - 1
packages/ui/src/listbox.js

@@ -76,7 +76,7 @@ function handleRoot(el, Alpine) {
                         this.__value = this.__context.selectedValueOrValues()
                     })
 
-                    setTimeout(() => {
+                    queueMicrotask(() => {
                         this.__ready = true
                     })
                 },