JSFiddle - React, Tailwind, and code Playground
HTML
<todo-list></todo-list>
JavaScript
/**
* Base class for todo elements containing shared functionality
* between both the <todo-list> and <todo-task> elements.
*/
class TodoElement extends HTMLElement {
/**
* Generates an HTML string representation of the current
* element's HTML tag.
* @param closed - When true generates a closing tag.
*/
tag(closed = false) {
const name = this.tagName.toLowerCase()
return `<${closed ? "/" : ""}${name}${closed ? "" : this.attrs()}>`
}
/**
* Provides the attributes string included in tag()-generated
* tag HTML. This will be empty by default. Individual
* components can define their own values by overridding
* this method.
*/
attrs() {
return ""
}
/**
* Adds styles and sets up listeners for add buttons. Add buttons
* exist both in the todo-list and individual todo-task elements.
*/
setupAdd() {
const addBtn = this.shadowRoot.getElementById("add-btn")
addBtn.addEventListener("click", () => this.onAddClick())
const style = document.createElement("style")
// Using a ::before pseudo element for the add button text
// keeps it from being copied when the user selects all the
// text in the component.
style.textContent = `
#add-btn::before{content:"+";color:green;}
#add-btn{opacity:0.333;}
#add-btn:hover,#add-btn:focus{opacity:1;}
`
this.shadowRoot.appendChild(style)
}
/**
* Add button click handler. When clicked, add buttons generate an
* event handled by todo-list.
*/
onAddClick() {
this.dispatchEvent(new Event("todo-add", {
bubbles: true
}))
}
}
/**
* Component definition for <todo-list> elements. A todo-list can
* contain any number of <todo-task> elements that together make up
* a todo list.
*/
customElements.define("todo-list", class extends TodoElement {
/**
* Constructor called when creating new <todo-list> elements.
*/
constructor() {
super()
const shadow = this.attachShadow({ mode:...