wired-elements
Version:
Collection of hand-drawn sketchy web components
167 lines (154 loc) • 4.06 kB
text/typescript
import { fireEvent } from './wired-base';
import { css, TemplateResult, html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
interface RadioItem extends HTMLElement {
name: string;
checked: boolean;
}
export class WiredRadioGroup extends LitElement {
selected?: string;
private radioNodes: RadioItem[] = [];
private checkListener = this.handleChecked.bind(this);
static get styles() {
return css`
:host {
display: inline-block;
font-family: inherit;
outline: none;
}
:host ::slotted(*) {
padding: var(--wired-radio-group-item-padding, 5px);
}
`;
}
render(): TemplateResult {
return html`<slot id="slot" ="${this.slotChange}"></slot>`;
}
connectedCallback() {
super.connectedCallback();
this.addEventListener('change', this.checkListener);
}
disconnectedCallback() {
super.disconnectedCallback();
this.removeEventListener('change', this.checkListener);
}
private handleChecked(event: Event) {
const checked = (event as CustomEvent).detail.checked;
const item = event.target as any as RadioItem;
const name = item.name || '';
if (!checked) {
item.checked = true;
} else {
this.selected = (checked && name) || '';
this.fireSelected();
}
}
slotChange() {
this.requestUpdate();
}
firstUpdated() {
this.setAttribute('role', 'radiogroup');
this.tabIndex = +(this.getAttribute('tabindex') || 0);
this.addEventListener('keydown', (event) => {
switch (event.keyCode) {
case 37:
case 38:
event.preventDefault();
this.selectPrevious();
break;
case 39:
case 40:
event.preventDefault();
this.selectNext();
break;
}
});
}
updated() {
const slot = this.shadowRoot!.getElementById('slot') as HTMLSlotElement;
const nodes = slot.assignedNodes();
this.radioNodes = [];
if (nodes && nodes.length) {
for (let i = 0; i < nodes.length; i++) {
const element = nodes[i] as RadioItem;
if (element.tagName === 'WIRED-RADIO') {
this.radioNodes.push(element);
const name = element.name || '';
if (this.selected && (name === this.selected)) {
element.checked = true;
} else {
element.checked = false;
}
}
}
}
}
private selectPrevious() {
const list = this.radioNodes;
if (list.length) {
let radio = null;
let index = -1;
if (this.selected) {
for (let i = 0; i < list.length; i++) {
const n = list[i];
if (n.name === this.selected) {
index = i;
break;
}
}
if (index < 0) {
radio = list[0];
} else {
index--;
if (index < 0) {
index = list.length - 1;
}
radio = list[index];
}
} else {
radio = list[0];
}
if (radio) {
radio.focus();
this.selected = radio.name;
this.fireSelected();
}
}
}
private selectNext() {
const list = this.radioNodes;
if (list.length) {
let radio = null;
let index = -1;
if (this.selected) {
for (let i = 0; i < list.length; i++) {
const n = list[i];
if (n.name === this.selected) {
index = i;
break;
}
}
if (index < 0) {
radio = list[0];
} else {
index++;
if (index >= list.length) {
index = 0;
}
radio = list[index];
}
} else {
radio = list[0];
}
if (radio) {
radio.focus();
this.selected = radio.name;
this.fireSelected();
}
}
}
private fireSelected() {
fireEvent(this, 'selected', { selected: this.selected });
}
}