Explorar el Código

Merge branch 'main' into add-focus

Caleb Porzio hace 3 años
padre
commit
f858774bb0

+ 1 - 1
packages/alpinejs/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "alpinejs",
     "name": "alpinejs",
-    "version": "3.7.0",
+    "version": "3.7.1",
     "description": "The rugged, minimal JavaScript framework",
     "description": "The rugged, minimal JavaScript framework",
     "author": "Caleb Porzio",
     "author": "Caleb Porzio",
     "license": "MIT",
     "license": "MIT",

+ 1 - 1
packages/alpinejs/src/directives/x-on.js

@@ -8,7 +8,7 @@ mapAttributes(startingWith('@', into(prefix('on:'))))
 directive('on', skipDuringClone((el, { value, modifiers, expression }, { cleanup }) => {
 directive('on', skipDuringClone((el, { value, modifiers, expression }, { cleanup }) => {
     let evaluate = expression ? evaluateLater(el, expression) : () => {}
     let evaluate = expression ? evaluateLater(el, expression) : () => {}
    
    
-    // Forward events liseners on portals.
+    // Forward event listeners on portals.
     if (el.tagName.toLowerCase() === 'template') {
     if (el.tagName.toLowerCase() === 'template') {
         if (! el._x_forwardEvents) el._x_forwardEvents = []
         if (! el._x_forwardEvents) el._x_forwardEvents = []
         if (! el._x_forwardEvents.includes(value)) el._x_forwardEvents.push(value)
         if (! el._x_forwardEvents.includes(value)) el._x_forwardEvents.push(value)

+ 2 - 3
packages/alpinejs/src/magics/$watch.js

@@ -10,9 +10,8 @@ magic('watch', el => (key, callback) => {
     let oldValue
     let oldValue
 
 
     effect(() => evaluate(value => {
     effect(() => evaluate(value => {
-        // This is a hack to force deep reactivity for things like "items.push()".
-        let div = document.createElement('div')
-        div.dataset.throwAway = value
+        // JSON.stringify touches every single property at any level enabling deep watching
+        JSON.stringify(value)
 
 
         if (! firstTime) {
         if (! firstTime) {
             // We have to queue this watcher as a microtask so that
             // We have to queue this watcher as a microtask so that

+ 1 - 1
packages/collapse/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@alpinejs/collapse",
     "name": "@alpinejs/collapse",
-    "version": "3.7.0",
+    "version": "3.7.1",
     "description": "Collapse and expand elements with robust animations",
     "description": "Collapse and expand elements with robust animations",
     "author": "Caleb Porzio",
     "author": "Caleb Porzio",
     "license": "MIT",
     "license": "MIT",

+ 11 - 19
packages/collapse/src/index.js

@@ -17,11 +17,10 @@ export default function (Alpine) {
         let setFunction = (el, styles) => {
         let setFunction = (el, styles) => {
             let revertFunction = Alpine.setStyles(el, styles);
             let revertFunction = Alpine.setStyles(el, styles);
 
 
-           return styles.height ? () => {} : revertFunction
+            return styles.height ? () => {} : revertFunction
         }
         }
 
 
         let transitionStyles = {
         let transitionStyles = {
-            overflow: 'hidden',
             transitionProperty: 'height',
             transitionProperty: 'height',
             transitionDuration: `${duration}s`,
             transitionDuration: `${duration}s`,
             transitionTimingFunction: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
             transitionTimingFunction: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
@@ -30,27 +29,25 @@ export default function (Alpine) {
         el._x_transition = {
         el._x_transition = {
             in(before = () => {}, after = () => {}) {
             in(before = () => {}, after = () => {}) {
                 el.hidden = false;
                 el.hidden = false;
+                el.style.display = null
 
 
                 let current = el.getBoundingClientRect().height
                 let current = el.getBoundingClientRect().height
 
 
-                Alpine.setStyles(el, {
-                    display: null,
-                    height: 'auto',
-                })
+                el.style.height = 'auto'
 
 
                 let full = el.getBoundingClientRect().height
                 let full = el.getBoundingClientRect().height
 
 
-                Alpine.setStyles(el, {
-                    overflow: null
-                })
-
                 if (current === full) { current = floor }
                 if (current === full) { current = floor }
 
 
                 Alpine.transition(el, Alpine.setStyles, {
                 Alpine.transition(el, Alpine.setStyles, {
                     during: transitionStyles,
                     during: transitionStyles,
                     start: { height: current+'px' },
                     start: { height: current+'px' },
                     end: { height: full+'px' },
                     end: { height: full+'px' },
-                }, () => el._x_isShown = true, () => {})
+                }, () => el._x_isShown = true, () => {
+                    if (el.style.height == `${full}px`) {
+                        el.style.overflow = null
+                    }
+                })
             },
             },
 
 
             out(before = () => {}, after = () => {}) {
             out(before = () => {}, after = () => {}) {
@@ -60,18 +57,13 @@ export default function (Alpine) {
                     during: transitionStyles,
                     during: transitionStyles,
                     start: { height: full+'px' },
                     start: { height: full+'px' },
                     end: { height: floor+'px' },
                     end: { height: floor+'px' },
-                }, () => {}, () => {
+                }, () => el.style.overflow = 'hidden', () => {
                     el._x_isShown = false
                     el._x_isShown = false
 
 
                     // check if element is fully collapsed
                     // check if element is fully collapsed
                     if (el.style.height == `${floor}px`) {
                     if (el.style.height == `${floor}px`) {
-                        Alpine.nextTick(() => {
-                            Alpine.setStyles(el, {
-                                display: 'none',
-                                overflow: 'hidden'
-                            })
-                            el.hidden = true;
-                        })
+                        el.style.display = 'none'
+                        el.hidden = true
                     }
                     }
                 })
                 })
             },
             },

+ 1 - 1
packages/docs/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@alpinejs/docs",
     "name": "@alpinejs/docs",
-    "version": "3.7.0-revision.3",
+    "version": "3.7.1-revision.1",
     "description": "The documentation for Alpine",
     "description": "The documentation for Alpine",
     "author": "Caleb Porzio",
     "author": "Caleb Porzio",
     "license": "MIT"
     "license": "MIT"

+ 4 - 2
packages/docs/src/en/directives/teleport.md

@@ -18,7 +18,9 @@ This is useful for things like modals (especially nesting them), where it's help
 
 
 By attaching `x-teleport` to a `<template>` element, you are telling Alpine to "append" that element to the provided selector.
 By attaching `x-teleport` to a `<template>` element, you are telling Alpine to "append" that element to the provided selector.
 
 
-> The `x-template` selector can be any string you would normally pass into something like `document.querySelector`
+> The `x-template` selector can be any string you would normally pass into something like `document.querySelector`. It will find the first element that matches, be it a tag name (`body`), class name (`.my-class`), ID (`#my-id`), or any other valid CSS selector.
+
+[→ Read more about `document.querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
 
 
 Here's a contrived modal example:
 Here's a contrived modal example:
 
 
@@ -63,7 +65,7 @@ Notice how when toggling the modal, the actual modal contents show up AFTER the
 <a name="forwarding-events"></a>
 <a name="forwarding-events"></a>
 ## Forwarding events
 ## Forwarding events
 
 
-Alpine tries it's best to make the experience of teleporting seamless. Anything you would normally do in a template, you should be able to do inside an `x-teleport` template. Teleported content can access the normal Alpine scope of the component as well as other features like `$refs`, `$root`, etc...
+Alpine tries its best to make the experience of teleporting seamless. Anything you would normally do in a template, you should be able to do inside an `x-teleport` template. Teleported content can access the normal Alpine scope of the component as well as other features like `$refs`, `$root`, etc...
 
 
 However, native DOM events have no concept of teleportation, so if, for example, you trigger a "click" event from inside a teleported element, that event will bubble up the DOM tree as it normally would.
 However, native DOM events have no concept of teleportation, so if, for example, you trigger a "click" event from inside a teleported element, that event will bubble up the DOM tree as it normally would.
 
 

+ 4 - 4
packages/docs/src/en/directives/transition.md

@@ -138,11 +138,11 @@ For direct control over exactly what goes into your transitions, you can apply C
     <div
     <div
         x-show="open"
         x-show="open"
         x-transition:enter="transition ease-out duration-300"
         x-transition:enter="transition ease-out duration-300"
-        x-transition:enter-start="opacity-0 transform scale-90"
-        x-transition:enter-end="opacity-100 transform scale-100"
+        x-transition:enter-start="opacity-0 scale-90"
+        x-transition:enter-end="opacity-100 scale-100"
         x-transition:leave="transition ease-in duration-300"
         x-transition:leave="transition ease-in duration-300"
-        x-transition:leave-start="opacity-100 transform scale-100"
-        x-transition:leave-end="opacity-0 transform scale-90"
+        x-transition:leave-start="opacity-100 scale-100"
+        x-transition:leave-end="opacity-0 scale-90"
     >Hello 👋</div>
     >Hello 👋</div>
 </div>
 </div>
 ```
 ```

+ 1 - 1
packages/docs/src/en/essentials/installation.md

@@ -33,7 +33,7 @@ This is by far the simplest way to get started with Alpine. Include the followin
 Notice the `@3.x.x` in the provided CDN link. This will pull the latest version of Alpine version 3. For stability in production, it's recommended that you hardcode the latest version in the CDN link.
 Notice the `@3.x.x` in the provided CDN link. This will pull the latest version of Alpine version 3. For stability in production, it's recommended that you hardcode the latest version in the CDN link.
 
 
 ```alpine
 ```alpine
-<script defer src="https://unpkg.com/alpinejs@3.7.0/dist/cdn.min.js"></script>
+<script defer src="https://unpkg.com/alpinejs@3.7.1/dist/cdn.min.js"></script>
 ```
 ```
 
 
 That's it! Alpine is now available for use inside your page.
 That's it! Alpine is now available for use inside your page.

+ 22 - 0
packages/docs/src/en/magics/watch.md

@@ -35,3 +35,25 @@ When the `<button>` is pressed, `foo.bar` will be set to "bob", and "bob" will b
     <button @click="open = ! open">Toggle Open</button>
     <button @click="open = ! open">Toggle Open</button>
 </div>
 </div>
 ```
 ```
+
+<a name="deep-watching"></a>
+### Deep watching
+
+`$watch` will automatically watches from changes at any level but you should keep in mind that, when a change is detected, the watcher will return the value of the observed property, not the value of the subproperty that has changed.
+
+```alpine
+<div x-data="{ foo: { bar: 'baz' }}" x-init="$watch('foo', (value, oldValue) => console.log(value, oldValue))">
+    <button @click="foo.bar = 'bob'">Update</button>
+</div>
+```
+
+When the `<button>` is pressed, `foo.bar` will be set to "bob", and "{bar: 'bob'} {bar: 'baz'}" will be logged to the console (new and old value).
+
+> ⚠️ Changing a property of a "watched" object as a side effect of the `$watch` callback will generate an infinite loop and eventually error. 
+
+```alpine
+<!-- 🚫 Infinite loop -->
+<div x-data="{ foo: { bar: 'baz', bob: 'lob' }}" x-init="$watch('foo', value => foo.bob = foo.bar">
+    <button @click="foo.bar = 'bob'">Update</button>
+</div>
+```

+ 1 - 1
packages/intersect/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@alpinejs/intersect",
     "name": "@alpinejs/intersect",
-    "version": "3.7.0",
+    "version": "3.7.1",
     "description": "Trigger JavaScript when an element enters the viewport",
     "description": "Trigger JavaScript when an element enters the viewport",
     "author": "Caleb Porzio",
     "author": "Caleb Porzio",
     "license": "MIT",
     "license": "MIT",

+ 1 - 1
packages/morph/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@alpinejs/morph",
     "name": "@alpinejs/morph",
-    "version": "3.7.0",
+    "version": "3.7.1",
     "description": "Diff and patch a block of HTML on a page with an HTML template",
     "description": "Diff and patch a block of HTML on a page with an HTML template",
     "author": "Caleb Porzio",
     "author": "Caleb Porzio",
     "license": "MIT",
     "license": "MIT",

+ 1 - 1
packages/persist/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@alpinejs/persist",
     "name": "@alpinejs/persist",
-    "version": "3.7.0",
+    "version": "3.7.1",
     "description": "Persist Alpine data across page loads",
     "description": "Persist Alpine data across page loads",
     "author": "Caleb Porzio",
     "author": "Caleb Porzio",
     "license": "MIT",
     "license": "MIT",

+ 1 - 1
packages/trap/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@alpinejs/trap",
     "name": "@alpinejs/trap",
-    "version": "3.7.0",
+    "version": "3.7.1",
     "description": "Trap focus on a page within a specific element",
     "description": "Trap focus on a page within a specific element",
     "author": "Caleb Porzio",
     "author": "Caleb Porzio",
     "license": "MIT",
     "license": "MIT",

+ 22 - 0
tests/cypress/integration/magics/$watch.spec.js

@@ -148,3 +148,25 @@ test('$watch ignores other dependencies',
         get('span').should(haveText('1'))
         get('span').should(haveText('1'))
     }
     }
 )
 )
+
+
+test('deep $watch',
+    html`
+        <div x-data="{ foo: { bar: 'baz'}, bob: 'lob' }" x-init="
+            $watch('foo', value => { bob = value.bar }, {deep: true});
+        ">
+            <h1 x-text="foo.bar"></h1>
+            <h2 x-text="bob"></h2>
+
+            <button x-on:click="foo.bar = 'law'"></button>
+        </div>
+    `,
+    ({ get }) => {
+        get('h1').should(haveText('baz'))
+        get('h2').should(haveText('lob'))
+        get('button').click()
+        get('h1').should(haveText('law'))
+        get('h2').should(haveText('law'))
+    }
+)
+

+ 2 - 2
tests/cypress/integration/plugins/collapse.spec.js

@@ -16,7 +16,7 @@ test('can collapse and expand element',
         get('h1').should(notHaveAttribute('hidden'))
         get('h1').should(notHaveAttribute('hidden'))
         get('button').click()
         get('button').click()
         get('h1').should(haveComputedStyle('height', '0px'))
         get('h1').should(haveComputedStyle('height', '0px'))
-        get('h1').should(haveAttribute('style', 'height: 0px; display: none; overflow: hidden;'))
+        get('h1').should(haveAttribute('style', 'height: 0px; overflow: hidden; display: none;'))
         get('h1').should(haveAttribute('hidden', 'hidden'))
         get('h1').should(haveAttribute('hidden', 'hidden'))
     },
     },
 )
 )
@@ -64,7 +64,7 @@ test('double-click on x-collapse does not mix styles up',
         get('h1').should(haveAttribute('style', 'display: none; height: 0px; overflow: hidden;'))
         get('h1').should(haveAttribute('style', 'display: none; height: 0px; overflow: hidden;'))
         get('button').click()
         get('button').click()
         get('button').click()
         get('button').click()
-        get('h1').should(haveAttribute('style', 'height: 0px; display: none; overflow: hidden;'))
+        get('h1').should(haveAttribute('style', 'height: 0px; overflow: hidden; display: none;'))
         get('button').click()
         get('button').click()
         get('h1').should(haveAttribute('style', 'height: auto;'))
         get('h1').should(haveAttribute('style', 'height: auto;'))
         get('button').click()
         get('button').click()