Переглянути джерело

Add short syntax: @ and : && fix class binding to string output

Caleb Porzio 5 роки тому
батько
коміт
40627248fc
7 змінених файлів з 66 додано та 6 видалено
  1. 0 0
      dist/alpine.js
  2. 0 0
      dist/alpine.js.map
  3. 1 1
      package.json
  4. 3 1
      src/component.js
  5. 18 4
      src/utils.js
  6. 26 0
      test/bind.spec.js
  7. 18 0
      test/on.spec.js

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/alpine.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/alpine.js.map


+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
   "main": "dist/alpine.js",
   "name": "alpinejs",
-  "version": "1.4.0",
+  "version": "1.5.0",
   "repository": {
     "type": "git",
     "url": "git://github.com/alpinejs/alpine.git"

+ 3 - 1
src/component.js

@@ -415,7 +415,9 @@ export default class Component {
                 el.value = value
             }
         } else if (attrName === 'class') {
-            if (Array.isArray(value)) {
+            if (typeof value === 'string') {
+                el.setAttribute('class', value)
+            } else if (Array.isArray(value)) {
                 el.setAttribute('class', value.join(' '))
             } else {
                 // Use the class object syntax that vue uses to toggle them.

+ 18 - 4
src/utils.js

@@ -66,18 +66,22 @@ export function saferEvalNoReturn(expression, dataContext, additionalHelperVaria
 }
 
 export function isXAttr(attr) {
+    const name = replaceAtAndColonWithStandardSyntax(attr.name)
+
     const xAttrRE = /x-(on|bind|data|text|html|model|if|show|cloak|transition|ref)/
 
-    return xAttrRE.test(attr.name)
+    return xAttrRE.test(name)
 }
 
 export function getXAttrs(el, type) {
     return Array.from(el.attributes)
         .filter(isXAttr)
         .map(attr => {
-            const typeMatch = attr.name.match(/x-(on|bind|data|text|html|model|if|show|cloak|transition|ref)/)
-            const valueMatch = attr.name.match(/:([a-zA-Z\-]+)/)
-            const modifiers = attr.name.match(/\.[^.\]]+(?=[^\]]*$)/g) || []
+            const name = replaceAtAndColonWithStandardSyntax(attr.name)
+
+            const typeMatch = name.match(/x-(on|bind|data|text|html|model|if|show|cloak|transition|ref)/)
+            const valueMatch = name.match(/:([a-zA-Z\-]+)/)
+            const modifiers = name.match(/\.[^.\]]+(?=[^\]]*$)/g) || []
 
             return {
                 type: typeMatch ? typeMatch[1] : null,
@@ -94,6 +98,16 @@ export function getXAttrs(el, type) {
         })
 }
 
+export function replaceAtAndColonWithStandardSyntax(name) {
+    if (name.startsWith('@')) {
+        return name.replace('@', 'x-on:')
+    } else if (name.startsWith(':')) {
+        return name.replace(':', 'x-bind:')
+    }
+
+    return name
+}
+
 export function transitionIn(el, callback, forceSkip = false) {
     if (forceSkip) callback()
 

+ 26 - 0
test/bind.spec.js

@@ -1,4 +1,5 @@
 import Alpine from 'alpinejs'
+import { wait } from '@testing-library/dom'
 
 global.MutationObserver = class {
     observe() {}
@@ -102,6 +103,19 @@ test('class attribute bindings are added by array syntax', async () => {
     expect(document.querySelector('span').classList.contains('foo')).toBeTruthy
 })
 
+test('class attribute bindings are synced by string syntax', async () => {
+    document.body.innerHTML = `
+        <div x-data="{foo: 'bar baz'}">
+            <span class="" x-bind:class="foo"></span>
+        </div>
+    `
+
+    Alpine.start()
+
+    expect(document.querySelector('span').classList.contains('bar')).toBeTruthy
+    expect(document.querySelector('span').classList.contains('baz')).toBeTruthy
+})
+
 test('boolean attributes set to false are removed from element', async () => {
     document.body.innerHTML = `
         <div x-data="{ isSet: false }">
@@ -139,3 +153,15 @@ test('boolean attributes set to true are added to element', async () => {
     expect(document.querySelectorAll('input')[2].required).toBeTruthy()
     expect(document.querySelectorAll('input')[3].readOnly).toBeTruthy()
 })
+
+test('binding supports short syntax', async () => {
+    document.body.innerHTML = `
+        <div x-data="{ foo: 'bar' }">
+            <span :class="foo"></span>
+        </div>
+    `
+
+    Alpine.start()
+
+    expect(document.querySelector('span').classList.contains('bar')).toBeTruthy()
+})

+ 18 - 0
test/on.spec.js

@@ -205,3 +205,21 @@ test('click away', async () => {
 
     await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false) })
 })
+
+test('supports short syntax', async () => {
+    document.body.innerHTML = `
+        <div x-data="{ foo: 'bar' }">
+            <button @click="foo = 'baz'"></button>
+
+            <span x-bind:foo="foo"></span>
+        </div>
+    `
+
+    Alpine.start()
+
+    expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
+
+    document.querySelector('button').click()
+
+    await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
+})

Деякі файли не було показано, через те що забагато файлів було змінено