Caleb Porzio преди 5 години
родител
ревизия
d270383841
променени са 8 файла, в които са добавени 79 реда и са изтрити 15 реда
  1. 6 1
      README.md
  2. 2 2
      dist/mix-manifest.json
  3. 18 5
      dist/project-x.js
  4. 0 0
      dist/project-x.min.js
  5. 10 1
      index.html
  6. 1 1
      package.json
  7. 17 5
      src/component.js
  8. 25 0
      test/on.spec.js

+ 6 - 1
README.md

@@ -139,7 +139,7 @@ In this example, the "hidden" class will only be applied when the value of the `
 For example:
 For example:
 `<button x-bind:disabled="myVar">Click me</button>`
 `<button x-bind:disabled="myVar">Click me</button>`
 
 
-This will add or remove the `disabled` attribute when `myVar` is true or false respectively. 
+This will add or remove the `disabled` attribute when `myVar` is true or false respectively.
 
 
 Most common boolean attributes are supported, like `readonly`, `required`, etc.
 Most common boolean attributes are supported, like `readonly`, `required`, etc.
 
 
@@ -178,6 +178,11 @@ Adding `.stop` to an event listener will call `stopPropagation` on the triggered
 
 
 Adding `.window` to an event listener will install the listener on the global window object instead of the DOM node on which it is declared. This is useful for when you want to modify component state when something changes with the window, like the resize event. In this example, when the window grows larger than 768 pixels wide, we will close the modal/dropdown, otherwise maintain the same state.
 Adding `.window` to an event listener will install the listener on the global window object instead of the DOM node on which it is declared. This is useful for when you want to modify component state when something changes with the window, like the resize event. In this example, when the window grows larger than 768 pixels wide, we will close the modal/dropdown, otherwise maintain the same state.
 
 
+**`.once` modifier**
+**Example:** `<button x-on:mouseenter.once="fetchSomething()"></button>`
+
+Adding the `.once` modifer to an event listener will ensure that the listener will only be handled once. This is useful for things you only want to do once, like fetching HTML partials and such.
+
 ---
 ---
 
 
 ### `x-model`
 ### `x-model`

+ 2 - 2
dist/mix-manifest.json

@@ -1,4 +1,4 @@
 {
 {
-    "/project-x.js": "/project-x.js?id=6a940bba5ac7fd0b68e1",
-    "/project-x.min.js": "/project-x.min.js?id=39ed1c431c27b0184930"
+    "/project-x.js": "/project-x.js?id=ec509918933bb1813fd2",
+    "/project-x.min.js": "/project-x.min.js?id=8bba743308e09dcd7d20"
 }
 }

+ 18 - 5
dist/project-x.js

@@ -1094,8 +1094,7 @@ function () {
       var _this2 = this;
       var _this2 = this;
 
 
       if (modifiers.includes('away')) {
       if (modifiers.includes('away')) {
-        // Listen for this event at the root level.
-        document.addEventListener(event, function (e) {
+        var handler = function handler(e) {
           // Don't do anything if the click came form the element or within it.
           // Don't do anything if the click came form the element or within it.
           if (el.contains(e.target)) return; // Don't do anything if this element isn't currently visible.
           if (el.contains(e.target)) return; // Don't do anything if this element isn't currently visible.
 
 
@@ -1103,15 +1102,29 @@ function () {
           // is from outside it, let's run the expression.
           // is from outside it, let's run the expression.
 
 
           _this2.runListenerHandler(expression, e);
           _this2.runListenerHandler(expression, e);
-        });
+
+          if (modifiers.includes('once')) {
+            document.removeEventListener(event, handler);
+          }
+        }; // Listen for this event at the root level.
+
+
+        document.addEventListener(event, handler);
       } else {
       } else {
         var node = modifiers.includes('window') ? window : el;
         var node = modifiers.includes('window') ? window : el;
-        node.addEventListener(event, function (e) {
+
+        var _handler = function _handler(e) {
           if (modifiers.includes('prevent')) e.preventDefault();
           if (modifiers.includes('prevent')) e.preventDefault();
           if (modifiers.includes('stop')) e.stopPropagation();
           if (modifiers.includes('stop')) e.stopPropagation();
 
 
           _this2.runListenerHandler(expression, e);
           _this2.runListenerHandler(expression, e);
-        });
+
+          if (modifiers.includes('once')) {
+            node.removeEventListener(event, _handler);
+          }
+        };
+
+        node.addEventListener(event, _handler);
       }
       }
     }
     }
   }, {
   }, {

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
dist/project-x.min.js


+ 10 - 1
index.html

@@ -5,7 +5,7 @@
             [x-cloak] { display: none; }
             [x-cloak] { display: none; }
         </style>
         </style>
 
 
-        <script src="https://cdn.jsdelivr.net/gh/calebporzio/project-x@v0.4.3/dist/project-x.min.js" defer></script>
+        <script src="https://cdn.jsdelivr.net/gh/calebporzio/project-x@v0.4.4/dist/project-x.min.js" defer></script>
     </head>
     </head>
     <body>
     <body>
         <table>
         <table>
@@ -138,6 +138,15 @@
                     </td>
                     </td>
                 </tr>
                 </tr>
 
 
+                <tr>
+                    <td>x-on:click.once</td>
+                    <td>
+                        <div x-data="{ count: 0 }">
+                            <button x-on:click.once="count++">I've been clicked: <span x-text="count"></span></button>
+                        </div>
+                    </td>
+                </tr>
+
                 <tr>
                 <tr>
                     <td>Append DOM</td>
                     <td>Append DOM</td>
                     <td>
                     <td>

+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
 {
   "main": "dist/project-x.js",
   "main": "dist/project-x.js",
   "name": "project-x",
   "name": "project-x",
-  "version": "0.4.3",
+  "version": "0.4.4",
   "repository": {
   "repository": {
     "type": "git",
     "type": "git",
     "url": "git://github.com/calebporzio/project-x.git"
     "url": "git://github.com/calebporzio/project-x.git"

+ 17 - 5
src/component.js

@@ -177,8 +177,7 @@ export default class Component {
 
 
     registerListener(el, event, modifiers, expression) {
     registerListener(el, event, modifiers, expression) {
         if (modifiers.includes('away')) {
         if (modifiers.includes('away')) {
-            // Listen for this event at the root level.
-            document.addEventListener(event, e => {
+            const handler = e => {
                 // Don't do anything if the click came form the element or within it.
                 // Don't do anything if the click came form the element or within it.
                 if (el.contains(e.target)) return
                 if (el.contains(e.target)) return
 
 
@@ -188,16 +187,29 @@ export default class Component {
                 // Now that we are sure the element is visible, AND the click
                 // Now that we are sure the element is visible, AND the click
                 // is from outside it, let's run the expression.
                 // is from outside it, let's run the expression.
                 this.runListenerHandler(expression, e)
                 this.runListenerHandler(expression, e)
-            })
+
+                if (modifiers.includes('once')) {
+                    document.removeEventListener(event, handler)
+                }
+            }
+
+            // Listen for this event at the root level.
+            document.addEventListener(event, handler)
         } else {
         } else {
             const node = modifiers.includes('window') ? window : el
             const node = modifiers.includes('window') ? window : el
 
 
-            node.addEventListener(event, e => {
+            const handler = e => {
                 if (modifiers.includes('prevent')) e.preventDefault()
                 if (modifiers.includes('prevent')) e.preventDefault()
                 if (modifiers.includes('stop')) e.stopPropagation()
                 if (modifiers.includes('stop')) e.stopPropagation()
 
 
                 this.runListenerHandler(expression, e)
                 this.runListenerHandler(expression, e)
-            })
+
+                if (modifiers.includes('once')) {
+                    node.removeEventListener(event, handler)
+                }
+            }
+
+            node.addEventListener(event, handler)
         }
         }
     }
     }
 
 

+ 25 - 0
test/on.spec.js

@@ -1,5 +1,6 @@
 import projectX from 'project-x'
 import projectX from 'project-x'
 import { wait } from '@testing-library/dom'
 import { wait } from '@testing-library/dom'
+const timeout = ms => new Promise(resolve => setTimeout(resolve, ms))
 
 
 global.MutationObserver = class {
 global.MutationObserver = class {
     observe() {}
     observe() {}
@@ -96,6 +97,30 @@ test('.window modifier', async () => {
     await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
     await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
 })
 })
 
 
+test('.once modifier', async () => {
+    document.body.innerHTML = `
+        <div x-data="{ count: 0 }">
+            <button x-on:click.once="count = count+1"></button>
+
+            <span x-bind:foo="count"
+        </div>
+    `
+
+    projectX.start()
+
+    expect(document.querySelector('span').getAttribute('foo')).toEqual('0')
+
+    document.querySelector('button').click()
+
+    await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('1') })
+
+    document.querySelector('button').click()
+
+    await timeout(25)
+
+    expect(document.querySelector('span').getAttribute('foo')).toEqual('1')
+})
+
 test('click away', async () => {
 test('click away', async () => {
     // Because jsDom doesn't support .offsetHeight and offsetWidth, we have to
     // Because jsDom doesn't support .offsetHeight and offsetWidth, we have to
     // make our own implementation using a specific class added to the class. Ugh.
     // make our own implementation using a specific class added to the class. Ugh.

Някои файлове не бяха показани, защото твърде много файлове са промени