UNPKG

@exmg/exmg-radio-group

Version:
122 lines 3.83 kB
import { ExmgRadioGroupItem } from './exmg-radio-group-item.js'; /** * Unique symbol for marking roots */ const selectionController = Symbol('selection controller'); class SelectionSet { constructor() { this.selected = null; this.focused = null; this.ordered = null; this.set = new Set(); } } export class SelectionController { static getController(element) { const root = element; if (!root[selectionController]) { root[selectionController] = new SelectionController(root); } return root[selectionController]; } constructor(element) { this.sets = {}; this.mouseIsDown = false; this.updating = false; // console.log('SelectionController constructor', element); element.addEventListener('keydown', (e) => this.keyDownHandler(e)); element.addEventListener('mousedown', () => this.mousedownHandler()); element.addEventListener('mouseup', () => this.mouseupHandler()); } keyDownHandler(e) { if (!(e.target instanceof ExmgRadioGroupItem)) { return; } const element = e.target; if (!this.has(element)) { return; } if (e.key === 'ArrowRight' || e.key === 'ArrowDown') { this.next(element); } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { this.previous(element); } } mousedownHandler() { this.mouseIsDown = true; } mouseupHandler() { this.mouseIsDown = false; } has(element) { const set = this.getSet(element.name); return set.set.has(element); } previous(element) { const order = this.getOrdered(element); const i = order.indexOf(element); this.focus(order[i - 1] || order[order.length - 1]); } next(element) { const order = this.getOrdered(element); const i = order.indexOf(element); this.focus(order[i + 1] || order[0]); } select(element) { element.click(); } /** * Helps to track the focused selection group and if it changes, focuses * the selected item in the group. This matches native radio button behavior. */ focus(element) { // Only manage focus state when using keyboard if (this.mouseIsDown) { return; } element.focusNative(); } getOrdered(element) { const set = this.getSet(element.name); if (!set.ordered) { set.ordered = Array.from(set.set); set.ordered.sort((a, b) => (a.compareDocumentPosition(b) === Node.DOCUMENT_POSITION_PRECEDING ? 1 : 0)); } return set.ordered.filter((item) => !item.disabled); } getSet(name) { if (!this.sets[name]) { this.sets[name] = new SelectionSet(); } return this.sets[name]; } register(element) { const set = this.getSet(element.name); set.set.add(element); set.ordered = null; } unregister(element) { const set = this.getSet(element.name); set.set.delete(element); set.ordered = null; if (set.selected === element) { set.selected = null; } } update(element) { if (this.updating) { return; } this.updating = true; if (element.checked) { const set = this.getSet(element.name); for (const e of set.set) { e.checked = e === element; } set.selected = element; } this.updating = false; } } //# sourceMappingURL=exmg-selection-controller.js.map