瀏覽代碼

Fix x-trap when cloning DOM

Caleb Porzio 3 年之前
父節點
當前提交
492914e8bb
共有 3 個文件被更改,包括 53 次插入44 次删除
  1. 2 1
      packages/alpinejs/src/alpine.js
  2. 2 2
      packages/alpinejs/src/clone.js
  3. 49 41
      packages/trap/src/index.js

+ 2 - 1
packages/alpinejs/src/alpine.js

@@ -13,7 +13,7 @@ import { nextTick } from './nextTick'
 import { plugin } from './plugin'
 import { magic } from './magics'
 import { store } from './store'
-import { clone } from './clone'
+import { clone, skipDuringClone } from './clone'
 import { data } from './datas'
 
 let Alpine = {
@@ -25,6 +25,7 @@ let Alpine = {
     flushAndStopDeferringMutations,
     disableEffectScheduling,
     setReactivityEngine,
+    skipDuringClone,
     addRootSelector,
     deferMutations,
     mapAttributes,

+ 2 - 2
packages/alpinejs/src/clone.js

@@ -4,8 +4,8 @@ import { walk } from "./utils/walk"
 
 let isCloning = false
 
-export function skipDuringClone(callback) {
-    return (...args) => isCloning || callback(...args)
+export function skipDuringClone(callback, fallback = () => {}) {
+    return (...args) => isCloning ? fallback(...args) : callback(...args)
 }
 
 export function onlyDuringClone(callback) {

+ 49 - 41
packages/trap/src/index.js

@@ -1,47 +1,55 @@
 import { createFocusTrap } from 'focus-trap';
 
 export default function (Alpine) {
-    Alpine.directive('trap', (el, { expression, modifiers }, { effect, evaluateLater }) => {
-        let evaluator = evaluateLater(expression)
-
-        let oldValue = false
-
-        let trap = createFocusTrap(el, { 
-            escapeDeactivates: false,
-            allowOutsideClick: true,
-            fallbackFocus: () => el,
-        })
-
-        let undoInert = () => {}
-        let undoDisableScrolling = () => {}
-
-        effect(() => evaluator(value => {
-            if (oldValue === value) return
-
-            // Start trapping.
-            if (value && ! oldValue) {
-                setTimeout(() => {
-                    if (modifiers.includes('inert')) undoInert = setInert(el)
-                    if (modifiers.includes('noscroll')) undoDisableScrolling = disableScrolling()
-
-                    trap.activate()
-                });
-            }
-
-            // Stop trapping.
-            if (! value && oldValue) {
-                undoInert()
-                undoInert = () => {}
-
-                undoDisableScrolling()
-                undoDisableScrolling = () => {}
-
-                trap.deactivate()
-            }
-
-            oldValue = !! value
-        }))
-    })
+    Alpine.directive('trap', Alpine.skipDuringClone(
+        (el, { expression, modifiers }, { effect, evaluateLater }) => {
+            let evaluator = evaluateLater(expression)
+
+            let oldValue = false
+
+            let trap = createFocusTrap(el, { 
+                escapeDeactivates: false,
+                allowOutsideClick: true,
+                fallbackFocus: () => el,
+            })
+
+            let undoInert = () => {}
+            let undoDisableScrolling = () => {}
+
+            effect(() => evaluator(value => {
+                if (oldValue === value) return
+
+                // Start trapping.
+                if (value && ! oldValue) {
+                    setTimeout(() => {
+                        if (modifiers.includes('inert')) undoInert = setInert(el)
+                        if (modifiers.includes('noscroll')) undoDisableScrolling = disableScrolling()
+
+                        trap.activate()
+                    });
+                }
+
+                // Stop trapping.
+                if (! value && oldValue) {
+                    undoInert()
+                    undoInert = () => {}
+
+                    undoDisableScrolling()
+                    undoDisableScrolling = () => {}
+
+                    trap.deactivate()
+                }
+
+                oldValue = !! value
+            }))
+        },
+        // When cloning, we only want to add aria-hidden attributes to the
+        // DOM and not try to actually trap, as trapping can mess with the
+        // live DOM and isn't just isolated to the cloned DOM.
+        (el, { expression, modifiers }, { evaluate }) => {
+            if (modifiers.includes('inert') && evaluate(expression)) setInert(el)
+        },
+    ))
 }
 
 function setInert(el) {