UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

439 lines (438 loc) • 12 kB
import { Component, Event, h, Listen, Element, Prop, Watch, Host, Build, Method } from "@stencil/core"; import { getElementDir, hasLabel } from "../../utils/dom"; import { getKey } from "../../utils/key"; export class CalciteRadioGroup { constructor() { //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- /** specify the appearance style of the radio group, defaults to solid. */ this.appearance = "solid"; /** specify the layout of the radio group, defaults to horizontal */ this.layout = "horizontal"; /** The scale of the radio group */ this.scale = "m"; /** specify the width of the group, defaults to auto */ this.width = "auto"; //-------------------------------------------------------------------------- // // Private State/Props // //-------------------------------------------------------------------------- this.hiddenInput = (() => { const input = document.createElement("input"); input.type = "hidden"; this.el.appendChild(input); return input; })(); } handleNameChange(value) { this.hiddenInput.name = value; } handleSelectedItemChange(newItem, oldItem) { if (newItem === oldItem) { return; } const items = this.getItems(); const match = Array.from(items) .filter((item) => item === newItem) .pop(); if (match) { this.selectItem(match); this.calciteRadioGroupChange.emit(match.value); } else if (items[0]) { items[0].tabIndex = 0; } } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- connectedCallback() { const items = this.getItems(); const lastChecked = Array.from(items) .filter((item) => item.checked) .pop(); if (lastChecked) { this.selectItem(lastChecked); } else if (items[0]) { items[0].tabIndex = 0; } const { hiddenInput, name } = this; if (name) { hiddenInput.name = name; } if (lastChecked) { hiddenInput.value = lastChecked.value; } } componentDidLoad() { this.hasLoaded = true; } render() { return (h(Host, { role: "radiogroup", tabIndex: this.disabled ? -1 : null }, h("slot", null))); } //-------------------------------------------------------------------------- // // Event Listeners // //-------------------------------------------------------------------------- handleLabelFocus(e) { if (hasLabel(e.detail.labelEl, this.el)) { this.setFocus(); } } handleClick(event) { if (event.target.localName === "calcite-radio-group-item") { this.selectItem(event.target); } } handleSelected(event) { // only fire after initial setup to prevent semi-infinite loops if (this.hasLoaded) { event.stopPropagation(); event.preventDefault(); this.selectItem(event.target); } } handleKeyDown(event) { const keys = ["ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", " "]; const key = getKey(event.key); const { el, selectedItem } = this; if (keys.indexOf(key) === -1) { return; } let adjustedKey = key; if (getElementDir(el) === "rtl") { if (key === "ArrowRight") { adjustedKey = "ArrowLeft"; } if (key === "ArrowLeft") { adjustedKey = "ArrowRight"; } } const items = this.getItems(); let selectedIndex = -1; items.forEach((item, index) => { if (item === selectedItem) { selectedIndex = index; } }); switch (adjustedKey) { case "ArrowLeft": case "ArrowUp": event.preventDefault(); const previous = selectedIndex < 1 ? items.item(items.length - 1) : items.item(selectedIndex - 1); this.selectItem(previous); return; case "ArrowRight": case "ArrowDown": event.preventDefault(); const next = selectedIndex === -1 ? items.item(1) : items.item(selectedIndex + 1) || items.item(0); this.selectItem(next); return; case " ": event.preventDefault(); this.selectItem(event.target); return; default: return; } } // -------------------------------------------------------------------------- // // Methods // // -------------------------------------------------------------------------- /** Focuses the selected item. If there is no selection, it focuses the first item. */ async setFocus() { var _a; (_a = (this.selectedItem || this.getItems()[0])) === null || _a === void 0 ? void 0 : _a.focus(); } //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- getItems() { return this.el.querySelectorAll("calcite-radio-group-item"); } selectItem(selected) { if (selected === this.selectedItem) { return; } const items = this.getItems(); let match = null; items.forEach((item) => { const matches = item.value === selected.value; if ((matches && !item.checked) || (!matches && item.checked)) { item.checked = matches; } item.tabIndex = matches ? 0 : -1; if (matches) { match = item; } }); this.selectedItem = match; this.syncWithInputProxy(match); if (Build.isBrowser && match) { match.focus(); } } syncWithInputProxy(item) { this.hiddenInput.value = item ? item.value : ""; } static get is() { return "calcite-radio-group"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["calcite-radio-group.scss"] }; } static get styleUrls() { return { "$": ["calcite-radio-group.css"] }; } static get properties() { return { "appearance": { "type": "string", "mutable": false, "complexType": { "original": "RadioAppearance", "resolved": "\"outline\" | \"solid\"", "references": { "RadioAppearance": { "location": "import", "path": "./interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "specify the appearance style of the radio group, defaults to solid." }, "attribute": "appearance", "reflect": true, "defaultValue": "\"solid\"" }, "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "is the radio group disabled" }, "attribute": "disabled", "reflect": true }, "layout": { "type": "string", "mutable": false, "complexType": { "original": "Layout", "resolved": "\"horizontal\" | \"vertical\"", "references": { "Layout": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "specify the layout of the radio group, defaults to horizontal" }, "attribute": "layout", "reflect": true, "defaultValue": "\"horizontal\"" }, "name": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The group's name. Gets submitted with the form." }, "attribute": "name", "reflect": false }, "scale": { "type": "string", "mutable": false, "complexType": { "original": "Scale", "resolved": "\"l\" | \"m\" | \"s\"", "references": { "Scale": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "The scale of the radio group" }, "attribute": "scale", "reflect": true, "defaultValue": "\"m\"" }, "selectedItem": { "type": "unknown", "mutable": false, "complexType": { "original": "HTMLCalciteRadioGroupItemElement", "resolved": "HTMLCalciteRadioGroupItemElement", "references": { "HTMLCalciteRadioGroupItemElement": { "location": "global" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "The group's selected item." } }, "theme": { "type": "string", "mutable": false, "complexType": { "original": "Theme", "resolved": "\"dark\" | \"light\"", "references": { "Theme": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "The component's theme." }, "attribute": "theme", "reflect": true }, "width": { "type": "string", "mutable": false, "complexType": { "original": "Extract<\"auto\" | \"full\", Width>", "resolved": "\"auto\" | \"full\"", "references": { "Extract": { "location": "global" }, "Width": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "specify the width of the group, defaults to auto" }, "attribute": "width", "reflect": true, "defaultValue": "\"auto\"" } }; } static get events() { return [{ "method": "calciteRadioGroupChange", "name": "calciteRadioGroupChange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Fired when the selected option changes, event detail is the new value" }, "complexType": { "original": "string", "resolved": "string", "references": {} } }]; } static get methods() { return { "setFocus": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "Focuses the selected item. If there is no selection, it focuses the first item.", "tags": [] } } }; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "name", "methodName": "handleNameChange" }, { "propName": "selectedItem", "methodName": "handleSelectedItemChange" }]; } static get listeners() { return [{ "name": "calciteLabelFocus", "method": "handleLabelFocus", "target": "window", "capture": false, "passive": false }, { "name": "click", "method": "handleClick", "target": undefined, "capture": false, "passive": false }, { "name": "calciteRadioGroupItemChange", "method": "handleSelected", "target": undefined, "capture": false, "passive": false }, { "name": "keydown", "method": "handleKeyDown", "target": undefined, "capture": false, "passive": false }]; } }