- acme-demo-lookup fixture is a reorder widget: a click reverses its items, sends the new id order as detail.payload and renders the returned items from the data attribute and the summer-result event with textContent, no HTTP - partials-and-widgets gains a Widget element contract subsection listing the attributes the SPA sets and the summer-action / summer-result events - cabana README names both events in the form widgets bullet
63 lines
2.1 KiB
JavaScript
63 lines
2.1 KiB
JavaScript
// A plain custom element that demonstrates a reorder widget: one light-DOM
|
|
// button and an ordered list. The SPA owns HTTP; this element only speaks the
|
|
// summer-action / summer-result events. It makes no request and reads no
|
|
// cookie or storage. A click reverses the current items (the simplest stand-in
|
|
// for a drag reorder) and sends the new id order as the action's payload; the
|
|
// returned data (its items array) arrives both as the `data` attribute and as
|
|
// the summer-result event's detail.data, and is rendered with textContent.
|
|
class AcmeDemoLookup extends HTMLElement {
|
|
static get observedAttributes() {
|
|
return ['data']
|
|
}
|
|
|
|
constructor() {
|
|
super()
|
|
this.items = []
|
|
this.list = null
|
|
this.addEventListener('summer-result', (event) => this.receive(event.detail && event.detail.data))
|
|
}
|
|
|
|
connectedCallback() {
|
|
if (this.firstChild) return
|
|
const button = document.createElement('button')
|
|
button.type = 'button'
|
|
button.textContent = this.getAttribute('label') || ''
|
|
button.addEventListener('click', () => {
|
|
this.items = this.items.slice().reverse()
|
|
this.render()
|
|
const payload = { order: this.items.map((item) => item.id) }
|
|
this.dispatchEvent(new CustomEvent('summer-action', { bubbles: true, composed: true, detail: { payload } }))
|
|
})
|
|
this.list = document.createElement('ol')
|
|
this.append(button, this.list)
|
|
this.render()
|
|
}
|
|
|
|
attributeChangedCallback(name, _old, value) {
|
|
if (name !== 'data') return
|
|
try {
|
|
this.receive(value === null ? null : JSON.parse(value))
|
|
} catch {
|
|
this.receive(null)
|
|
}
|
|
}
|
|
|
|
receive(data) {
|
|
this.items = data && Array.isArray(data.items) ? data.items.filter((item) => item && typeof item === 'object') : []
|
|
this.render()
|
|
}
|
|
|
|
render() {
|
|
if (!this.list) return
|
|
this.list.textContent = ''
|
|
for (const item of this.items) {
|
|
const li = document.createElement('li')
|
|
li.textContent = String(item.title == null ? item.id : item.title)
|
|
this.list.append(li)
|
|
}
|
|
}
|
|
}
|
|
if (!customElements.get('acme-demo-lookup')) {
|
|
customElements.define('acme-demo-lookup', AcmeDemoLookup)
|
|
}
|