@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
439 lines (438 loc) • 12 kB
JavaScript
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
}]; }
}