Caleb Porzio 3 years ago
parent
commit
a99a36fbfc

+ 37 - 4
index.html

@@ -11,14 +11,47 @@
     <script src="//cdn.tailwindcss.com"></script>
     <script src="https://unpkg.com/@popperjs/core@2"></script>
 
-    <div x-data x-popover>
-        <button x-popover:button>Open</button>
+    <script src="//unpkg.com/@ungap/custom-elements"></script>
 
-        <ul x-popover:panel>
+    <x-button style="background: black">Hey</x-button>
+
+    <template id="template">
+        <button>
+            <slot></slot>
+        </button>
+    </template>
+
+    <script>
+        customElements.define('x-button', class extends HTMLElement {
+            constructor() {
+                super()
+                let template = document.getElementById('template')
+                let templateContent = template.content
+                let shadowRoot = this.attachShadow({mode: 'closed'})
+                shadowRoot.appendChild(templateContent.cloneNode(true))
+            }
+        })
+    </script>
+
+    <div x-data="{ open: false }">
+        <button @click="open = ! open">Open</button>
+
+        <x-dialog x-model="open">
+            <x-dialog.panel>
+                Hey!
+            </x-dialog.panel>
+        </x-dialog>
+    </div>
+
+
+    <x-popover>
+        <x-popover.button>Open</x-popover.button>
+
+        <x-popover.panel as="ul">
             <li><a href="#">First</a></li>
             <li><a href="#">Second</a></li>
             <li><a href="#">Third</a></li>
-        </ul>
+        </x-popover.panel>
     </div>
 
     <!-- <main class="flex-1 overflow-auto bg-gray-50 h-screen">

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

@@ -16,6 +16,7 @@ import { plugin } from './plugin'
 import { magic } from './magics'
 import { store } from './store'
 import { bind } from './binds'
+import { registerElement as element } from './elements'
 import { data } from './datas'
 
 let Alpine = {
@@ -51,6 +52,7 @@ let Alpine = {
     initTree,
     nextTick,
     prefixed,
+    element,
     prefix,
     plugin,
     magic,

+ 23 - 1
packages/alpinejs/src/directives.js

@@ -20,9 +20,31 @@ export function directive(name, callback) {
 }
 
 export function directives(el, attributes, originalAttributeOverride) {
+    attributes = Array.from(attributes)
+
+    if (el._x_virtualDirectives) {
+        let vAttributes = Object.entries(el._x_virtualDirectives).map(([name, value]) => ({ name, value }))
+
+        let staticAttributes = attributesOnly(vAttributes)
+
+        // Handle binding normal HTML attributes (non-Alpine directives).
+        vAttributes = vAttributes.map(attribute => {
+            if (staticAttributes.find(attr => attr.name === attribute.name)) {
+                return {
+                    name: `x-bind:${attribute.name}`,
+                    value: `"${attribute.value}"`,
+                }
+            }
+
+            return attribute
+        })
+
+        attributes = attributes.concat(vAttributes)
+    }
+
     let transformedAttributeMap = {}
 
-    let directives = Array.from(attributes)
+    let directives = attributes
         .map(toTransformedAttributes((newName, oldName) => transformedAttributeMap[newName] = oldName))
         .filter(outNonAlpineAttributes)
         .map(toParsedDirectives(transformedAttributeMap, originalAttributeOverride))

+ 12 - 0
packages/alpinejs/src/elements.js

@@ -0,0 +1,12 @@
+
+export function registerElement(name, callback) {
+    class CustomAlpineElement extends HTMLElement {
+        constructor() {
+            super()
+
+            this._x_virtualDirectives = callback(this)
+        }
+    }
+
+    customElements.define('x-'+name, CustomAlpineElement)
+}

+ 28 - 20
packages/ui/src/dialog.js

@@ -1,26 +1,10 @@
 
 export default function (Alpine) {
-    Alpine.directive('dialog', (el, directive) => {
-        if      (directive.value === 'overlay')     handleOverlay(el, Alpine)
-        else if (directive.value === 'panel')       handlePanel(el, Alpine)
-        else if (directive.value === 'title')       handleTitle(el, Alpine)
-        else if (directive.value === 'description') handleDescription(el, Alpine)
-        else                                        handleRoot(el, Alpine)
-    })
-
-    Alpine.magic('dialog', el => {
-        let $data = Alpine.$data(el)
-
-        return {
-            get open() {
-                return $data.__isOpen
-            }
-        }
-    })
-}
+    Alpine.element('dialog.panel', () => ({
+        '@click.outside'() { this.$data.__close() },
+    }))
 
-function handleRoot(el, Alpine) {
-    Alpine.bind(el, {
+    Alpine.element('dialog', el => ({
         'x-data'() {
             return {
                 init() {
@@ -56,6 +40,30 @@ function handleRoot(el, Alpine) {
         ':aria-describedby'() { return this.$id('alpine-dialog-description') },
         'role': 'dialog',
         'aria-modal': 'true',
+    }))
+
+    Alpine.directive('dialog', (el, directive) => {
+        if      (directive.value === 'overlay')     handleOverlay(el, Alpine)
+        else if (directive.value === 'panel')       handlePanel(el, Alpine)
+        else if (directive.value === 'title')       handleTitle(el, Alpine)
+        else if (directive.value === 'description') handleDescription(el, Alpine)
+        else                                        handleRoot(el, Alpine)
+    })
+
+    Alpine.magic('dialog', el => {
+        let $data = Alpine.$data(el)
+
+        return {
+            get open() {
+                return $data.__isOpen
+            }
+        }
+    })
+}
+
+function handleRoot(el, Alpine) {
+    Alpine.bind(el, {
+
     })
 }