Caleb Porzio 2 سال پیش
والد
کامیت
003a424f8b

+ 10 - 1
index.html

@@ -1,15 +1,20 @@
 <html>
     <!-- <script src="./packages/intersect/dist/cdn.js" defer></script>
     <script src="./packages/morph/dist/cdn.js" defer></script>
-    <script src="./packages/history/dist/cdn.js"></script>
     <script src="./packages/persist/dist/cdn.js"></script>
     <script src="./packages/focus/dist/cdn.js"></script>
     <script src="./packages/mask/dist/cdn.js"></script>
     <script src="./packages/ui/dist/cdn.js" defer></script> -->
+    <script src="./packages/history/dist/cdn.js"></script>
     <script src="./packages/alpinejs/dist/cdn.js" defer></script>
     <!-- <script src="//cdn.tailwindcss.com"></script> -->
     <script src="//cdn.tailwindcss.com"></script>
 
+    <div x-data="{ count: $queryString(1) }">
+        <input type="text" x-model="count">
+        <span x-text="count"></span>
+    </div>
+
     <div x-data="{ users: [{ name: 'lebowski' }] }">
         <template x-for="(user, idx) in users">
             <span x-text="users[idx].name" x-yo></span>
@@ -18,6 +23,10 @@
         <button @click="users = []">Reset</button>
     </div>
 
+    <div x-data="{ foo: undefined }">
+        Yo: <input type="text" x-model="foo">
+    </div>
+
     <!-- Play around here... -->
 
     <div x-data>

+ 0 - 2
packages/alpinejs/src/directives/x-model.js

@@ -97,8 +97,6 @@ directive('model', (el, { modifiers, expression }, { effect, cleanup }) => {
     }
 
     el._x_forceModelUpdate = (value) => {
-        value = value === undefined ? getValue() : value
-
         // If nested model key is undefined, set the default value to empty string.
         if (value === undefined && typeof expression === 'string' && expression.match(/\./)) value = ''
 

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

@@ -9,7 +9,7 @@ export function entangle({ get: outerGet, set: outerSet }, { get: innerGet, set:
 
         if (firstRun) {
             outer = outerGet()
-            innerSet(outer)
+            innerSet(JSON.parse(JSON.stringify(outer))) // We need to break internal references using parse/stringify...
             inner = innerGet()
             firstRun = false
         } else {
@@ -24,7 +24,7 @@ export function entangle({ get: outerGet, set: outerSet }, { get: innerGet, set:
                 innerSet(outer)
                 inner = outer // Assign inner to outer so that it can be serialized for diffing...
             } else { // If inner changed...
-                outerSet(inner)
+                outerSet(JSON.parse(JSON.stringify(inner))) // We need to break internal references using parse/stringify...
                 outer = inner // Assign outer to inner so that it can be serialized for diffing...
             }
         }

+ 2 - 2
packages/alpinejs/src/utils/bind.js

@@ -48,7 +48,7 @@ function bindInputValue(el, value) {
         // automatically.
         if (Number.isInteger(value)) {
             el.value = value
-        } else if (! Number.isInteger(value) && ! Array.isArray(value) && typeof value !== 'boolean' && ! [null, undefined].includes(value)) {
+        } else if (! Array.isArray(value) && typeof value !== 'boolean' && ! [null, undefined].includes(value)) {
             el.value = String(value)
         } else {
             if (Array.isArray(value)) {
@@ -62,7 +62,7 @@ function bindInputValue(el, value) {
     } else {
         if (el.value === value) return
 
-        el.value = value
+        el.value = value === undefined ? '' : value
     }
 }
 

+ 2 - 0
packages/history/builds/module.js

@@ -1,3 +1,5 @@
 import history from '../src/index.js'
+import { track } from '../src/index.js'
 
 export default history
+export { track }

+ 113 - 34
packages/history/src/index.js

@@ -3,60 +3,80 @@ export default function history(Alpine) {
         let alias
 
         return interceptor((initialValue, getter, setter, path, key) => {
-            let pause = false
             let queryKey = alias || path
 
-            let value = initialValue
+            let { initial, replace } = track(queryKey, initialValue)
+
+            setter(initial)
+
+            Alpine.effect(() => {
+                replace(getter())
+            })
+
+            return initial
+        }, func => {
+            func.as = key => { alias = key; return func }
+        })
+    })
+
+    Alpine.history = { track }
+}
+
+export function track(name, initialValue, except = null) {
+    let pause = false
+    let url = new URL(window.location.href)
+    let value = initialValue
+
+    if (url.searchParams.has(name)) {
+        value = url.searchParams.get(name)
+    }
+
+    // Nothing happens here...
+    // let object = { value }
+
+    // url.searchParams.set(name, value)
+
+    // replace(url.toString(), name, object)
+
+    return {
+        initial: value, // Initial value...
+        replace(newValue) { // Update via replaceState...
+            let object = { value: newValue }
+
             let url = new URL(window.location.href)
 
-            if (url.searchParams.has(queryKey)) {
-                value = url.searchParams.get(queryKey)
-            }
+            url.searchParams.set(name, newValue)
 
-            setter(value)
+            replace(url.toString(), name, object)
+        },
+        push(newValue) { // Update via pushState...
+            if (pause) return
 
-            let object = { value }
+            let object = { value: newValue }
 
-            url.searchParams.set(queryKey, value)
+            let url = new URL(window.location.href)
 
-            replace(url.toString(), path, object)
+            url.searchParams.set(name, newValue)
 
+            push(url.toString(), name, object)
+        },
+        pop(receiver) { // onPopState...
             window.addEventListener('popstate', (e) => {
                 if (! e.state) return
                 if (! e.state.alpine) return
 
-                Object.entries(e.state.alpine).forEach(([newKey, { value }]) => {
-                    if (newKey !== key) return
+                Object.entries(e.state.alpine).forEach(([newName, { value }]) => {
+                    if (newName !== name) return
 
                     pause = true
 
-                    Alpine.disableEffectScheduling(() => {
-                        setter(value)
-                    })
+                    receiver(value)
 
                     pause = false
                 })
             })
-
-            Alpine.effect(() => {
-                let value = getter()
-
-                if (pause) return
-
-                let object = { value }
-
-                let url = new URL(window.location.href)
-
-                url.searchParams.set(queryKey, value)
-
-                push(url.toString(), path, object)
-            })
-
-            return value
-        }, func => {
-            func.as = key => { alias = key; return func }
-        })
-    })
+        }
+    }
 }
 
 function replace(url, key, object) {
@@ -74,3 +94,62 @@ function push(url, key, object) {
 
     window.history.pushState(state, '', url)
 }
+
+// Alpine.magic('queryString', (el, { interceptor }) =>  {
+//     let alias
+
+//     return interceptor((initialValue, getter, setter, path, key) => {
+//         let pause = false
+//         let queryKey = alias || path
+
+//         let value = initialValue
+//         let url = new URL(window.location.href)
+
+//         if (url.searchParams.has(queryKey)) {
+//             value = url.searchParams.get(queryKey)
+//         }
+
+//         setter(value)
+
+//         let object = { value }
+
+//         url.searchParams.set(queryKey, value)
+
+//         replace(url.toString(), path, object)
+
+//         window.addEventListener('popstate', (e) => {
+//             if (! e.state) return
+//             if (! e.state.alpine) return
+
+//             Object.entries(e.state.alpine).forEach(([newKey, { value }]) => {
+//                 if (newKey !== key) return
+
+//                 pause = true
+
+//                 Alpine.disableEffectScheduling(() => {
+//                     setter(value)
+//                 })
+
+//                 pause = false
+//             })
+//         })
+
+//         Alpine.effect(() => {
+//             let value = getter()
+
+//             if (pause) return
+
+//             let object = { value }
+
+//             let url = new URL(window.location.href)
+
+//             url.searchParams.set(queryKey, value)
+
+//             push(url.toString(), path, object)
+//         })
+
+//         return value
+//     }, func => {
+//         func.as = key => { alias = key; return func }
+//     })
+// })

+ 2 - 0
packages/morph/src/morph.js

@@ -99,6 +99,8 @@ export function morph(from, toHtml, options) {
     }
 
     function patchAttributes(from, to) {
+        if (from._x_transitioning) return
+
         if (from._x_isShown && ! to._x_isShown) {
             return
         }