@ou-imdt/create
Version:
Command line tool to create team boilerplate.
113 lines (95 loc) • 3.49 kB
JavaScript
// import Lit refs
import { css, html, nothing, unsafeCSS } from 'lit';
// import utils
import { mix } from '@utils';
// import internal symbols/hooks
import { defaultState } from '@core/internals';
// import base class (extends LitElement)
import { Base } from '@components';
// import a mixin and associated symbols/hooks
import { default as DelegateFocusMixin, focus, focusVisible } from '@mixins/DelegateFocusMixin';
// import component styles
import styles from './Component.css?raw';
// use symbols for private reactive/protected props (export to give access outside class/to subclasses)
// Lit doesn't support private class fields for technical reasons (seem more trouble than they're worth anyway!)
export const protectedProperty = Symbol('protectedProperty');
// name class accordingly and extend base directly or mix with as many mixins as needed
export default class Component extends mix(Base).with(DelegateFocusMixin) {
// component tag name
static tag = 'imdt-component';
// set component styles
static styles = css`
${unsafeCSS(styles)}
`;
// Lit reactive props
static properties = {
// add DelegateFocusMixin hooks
[focus]: {
type: Boolean,
attribute: 'focus', // attribute as CSS hook
reflect: true, // setting prop is reflected back to attribute
converter: {fromAttribute: () => null // removes attribute if externally set
}
},
[focusVisible]: {
type: Boolean,
attribute: 'focus-visible', // attribute as CSS hook
reflect: true,
converter: {fromAttribute: () => null // removes attribute if externally set
}
},
[protectedProperty]: {state: true // internal only, no attribute etc.
},
publicProperty: {
type: String,
attribute: 'publicProperty' // sets attribute name (defaults to prop key)
},
};
// define default state
static [defaultState] = {
...super[defaultState], // add mixin state ([focus]/[focusVisible] are in there)
[protectedProperty]: null,
publicProperty: 'Hello World!'
};
// use private class fields/methods to keep private to class (most useful in mixins)
// N.B. use with caution in extensible components (not accessible to subclasses)
#privateProperty = null;
#privateMethod() {
}
// constructor calling super()
constructor() {
super();
}
// lifecyle (CustomElement/Lit)
// a good place to add event listeners to this
connectedCallback() {
this.addEventListener('click', this);
// must call super method...
super.connectedCallback();
}
// remove event listeners here etc.
disconnectedCallback() {
this.removeEventListener('click', this);
// must call super method...
super.disconnectedCallback();
}
// handle changed props here and a good place to dispatch events to limit side effects
// (updating reactive props here won't trigger another update cycle)
willUpdate(changedProperties) {
//
if (changedProperties.has(focus)) {
// call overriden native method to dispatch namespaced custom event
this.dispatchEvent('focus', { detail: { focus: this[focus] }});
}
}
render() {
return html`<p class=${this[protectedProperty] ?? nothing}>${this.publicProperty}</p>`;
}
// do any work here that sits outside of update cycle (like updating accessible DOM)
updated() {
}
}
// register component
if (!customElements.get(Component.tag)) {
customElements.define(Component.tag, Component);
}