/*! * All material copyright ESRI, All Rights Reserved, unless otherwise specified. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details. * v1.0.0-beta.97 */ import { Build, h, Host } from "@stencil/core"; import { getElementDir } from "../../utils/dom"; import { connectLabel, disconnectLabel } from "../../utils/label"; import { afterConnectDefaultValueSet, connectForm, disconnectForm, HiddenFormInputSlot } from "../../utils/form"; import { updateHostInteraction } from "../../utils/interactive"; /** * @slot - A slot for adding `calcite-radio-group-item`s. */ export class RadioGroup { constructor() { //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- /** Specifies the appearance style of the component. */ this.appearance = "solid"; /** When `true`, interaction is prevented and the component is displayed with lower opacity. */ this.disabled = false; /** * When `true`, the component must have a value in order for the form to submit. * * @internal */ this.required = false; /** Defines the layout of the component. */ this.layout = "horizontal"; /** Specifies the size of the component. */ this.scale = "m"; /** The component's `selectedItem` value. */ this.value = null; /** Specifies the width of the component. */ this.width = "auto"; //-------------------------------------------------------------------------- // // Event Listeners // //-------------------------------------------------------------------------- this.handleClick = (event) => { if (event.target.localName === "calcite-radio-group-item") { this.selectItem(event.target, true); } }; } valueHandler(value) { const items = this.getItems(); items.forEach((item) => (item.checked = item.value === value)); } handleSelectedItemChange(newItem, oldItem) { this.value = newItem === null || newItem === void 0 ? void 0 : newItem.value; if (newItem === oldItem) { return; } const items = this.getItems(); const match = Array.from(items) .filter((item) => item === newItem) .pop(); if (match) { this.selectItem(match); } else if (items[0]) { items[0].tabIndex = 0; } } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- componentWillLoad() { 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; } } componentDidLoad() { afterConnectDefaultValueSet(this, this.value); } connectedCallback() { connectLabel(this); connectForm(this); } disconnectedCallback() { disconnectLabel(this); disconnectForm(this); } componentDidRender() { updateHostInteraction(this); } render() { return (h(Host, { onClick: this.handleClick, role: "radiogroup" }, h("slot", null), h(HiddenFormInputSlot, { component: this }))); } handleSelected(event) { event.preventDefault(); this.selectItem(event.target); event.stopPropagation(); } handleKeyDown(event) { const keys = ["ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", " "]; const { key } = event; 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, true); return; case "ArrowRight": case "ArrowDown": event.preventDefault(); const next = selectedIndex === -1 ? items.item(1) : items.item(selectedIndex + 1) || items.item(0); this.selectItem(next, true); return; case " ": event.preventDefault(); this.selectItem(event.target, true); return; default: return; } } // -------------------------------------------------------------------------- // // Methods // // -------------------------------------------------------------------------- /** Sets focus on the component. */ async setFocus() { var _a; (_a = (this.selectedItem || this.getItems()[0])) === null || _a === void 0 ? void 0 : _a.focus(); } //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- onLabelClick() { this.setFocus(); } getItems() { return this.el.querySelectorAll("calcite-radio-group-item"); } selectItem(selected, emit = false) { 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; if (emit) { this.calciteRadioGroupChange.emit(match.value); } } }); this.selectedItem = match; if (Build.isBrowser && match) { match.focus(); } } static get is() { return "calcite-radio-group"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["radio-group.scss"] }; } static get styleUrls() { return { "$": ["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": "Specifies the appearance style of the component." }, "attribute": "appearance", "reflect": true, "defaultValue": "\"solid\"" }, "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, interaction is prevented and the component is displayed with lower opacity." }, "attribute": "disabled", "reflect": true, "defaultValue": "false" }, "required": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "When `true`, the component must have a value in order for the form to submit." }, "attribute": "required", "reflect": true, "defaultValue": "false" }, "layout": { "type": "string", "mutable": false, "complexType": { "original": "Layout", "resolved": "\"grid\" | \"horizontal\" | \"vertical\"", "references": { "Layout": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Defines the layout of the component." }, "attribute": "layout", "reflect": true, "defaultValue": "\"horizontal\"" }, "name": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies the name of the component on form submission." }, "attribute": "name", "reflect": true }, "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": "Specifies the size of the component." }, "attribute": "scale", "reflect": true, "defaultValue": "\"m\"" }, "value": { "type": "string", "mutable": true, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The component's `selectedItem` value." }, "attribute": "value", "reflect": false, "defaultValue": "null" }, "selectedItem": { "type": "unknown", "mutable": true, "complexType": { "original": "HTMLCalciteRadioGroupItemElement", "resolved": "HTMLCalciteRadioGroupItemElement", "references": { "HTMLCalciteRadioGroupItemElement": { "location": "global" } } }, "required": false, "optional": false, "docs": { "tags": [{ "name": "readonly", "text": undefined }], "text": "The component's selected item `HTMLElement`." } }, "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": "Specifies the width of the component." }, "attribute": "width", "reflect": true, "defaultValue": "\"auto\"" } }; } static get events() { return [{ "method": "calciteRadioGroupChange", "name": "calciteRadioGroupChange", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [], "text": "Fires when the selected option changes, where the event detail is the new value." }, "complexType": { "original": "string", "resolved": "string", "references": {} } }]; } static get methods() { return { "setFocus": { "complexType": { "signature": "() => Promise", "parameters": [], "references": { "Promise": { "location": "global" } }, "return": "Promise" }, "docs": { "text": "Sets focus on the component.", "tags": [] } } }; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "value", "methodName": "valueHandler" }, { "propName": "selectedItem", "methodName": "handleSelectedItemChange" }]; } static get listeners() { return [{ "name": "calciteInternalRadioGroupItemChange", "method": "handleSelected", "target": undefined, "capture": false, "passive": false }, { "name": "keydown", "method": "handleKeyDown", "target": undefined, "capture": false, "passive": false }]; } }