@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
352 lines (351 loc) • 10.6 kB
JavaScript
/*!
* All material copyright ESRI, All Rights Reserved, unless otherwise specified.
* See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details.
* v1.5.0-next.4
*/
import { h } from "@stencil/core";
import { focusElementInGroup, toAriaBoolean } from "../../utils/dom";
import { connectInteractive, disconnectInteractive, updateHostInteraction } from "../../utils/interactive";
import { createObserver } from "../../utils/observers";
import { componentLoaded, setComponentLoaded, setUpLoadableComponent } from "../../utils/loadable";
/**
* @slot - A slot for adding one or more `calcite-chip`s.
*/
export class ChipGroup {
constructor() {
//--------------------------------------------------------------------------
//
// Private Properties
//
//--------------------------------------------------------------------------
this.mutationObserver = createObserver("mutation", () => this.updateItems());
this.items = [];
//--------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------
this.updateItems = (event) => {
const target = event ? event.target : this.slotRefEl;
this.items = target
.assignedElements({ flatten: true })
.filter((el) => el?.matches("calcite-chip"));
this.items.forEach((el) => {
el.interactive = true;
el.scale = this.scale;
el.selectionMode = this.selectionMode;
});
this.setSelectedItems(false);
};
this.setSelectedItems = (emit, elToMatch) => {
if (elToMatch) {
this.items.forEach((el) => {
const matchingEl = elToMatch === el;
switch (this.selectionMode) {
case "multiple":
if (matchingEl) {
el.selected = !el.selected;
}
break;
case "single":
el.selected = matchingEl ? !el.selected : false;
break;
case "single-persist":
el.selected = !!matchingEl;
break;
}
});
}
this.selectedItems = this.items.filter((el) => el.selected);
if (emit) {
this.calciteChipGroupSelect.emit();
}
};
this.disabled = false;
this.label = undefined;
this.scale = "m";
this.selectionMode = "none";
this.selectedItems = [];
}
onSelectionModeChange() {
this.updateItems();
}
//--------------------------------------------------------------------------
//
// Lifecycle
//
//--------------------------------------------------------------------------
connectedCallback() {
connectInteractive(this);
this.mutationObserver?.observe(this.el, { childList: true, subtree: true });
}
componentDidRender() {
disconnectInteractive(this);
updateHostInteraction(this);
}
componentDidLoad() {
setComponentLoaded(this);
}
disconnectedCallback() {
this.mutationObserver?.disconnect();
}
async componentWillLoad() {
setUpLoadableComponent(this);
}
//--------------------------------------------------------------------------
//
// Event Listeners
//
//--------------------------------------------------------------------------
calciteInternalChipKeyEventListener(event) {
if (event.composedPath().includes(this.el)) {
const interactiveItems = this.items.filter((el) => !el.disabled);
switch (event.detail.key) {
case "ArrowRight":
focusElementInGroup(interactiveItems, event.detail.target, "next");
break;
case "ArrowLeft":
focusElementInGroup(interactiveItems, event.detail.target, "previous");
break;
case "Home":
focusElementInGroup(interactiveItems, event.detail.target, "first");
break;
case "End":
focusElementInGroup(interactiveItems, event.detail.target, "last");
break;
}
}
}
calciteChipCloseListener(event) {
const item = event.target;
if (this.items.includes(item)) {
if (this.items.indexOf(item) > 0) {
focusElementInGroup(this.items, item, "previous");
}
else if (this.items.indexOf(item) === 0) {
focusElementInGroup(this.items, item, "next");
}
else {
focusElementInGroup(this.items, item, "first");
}
}
this.items = this.items.filter((el) => el !== item);
}
calciteChipSelectListener(event) {
if (event.composedPath().includes(this.el)) {
this.setSelectedItems(true, event.target);
}
}
// --------------------------------------------------------------------------
//
// Public Methods
//
// --------------------------------------------------------------------------
/**
* Sets focus on the component's first focusable element.
*/
async setFocus() {
await componentLoaded(this);
if (!this.disabled) {
(this.selectedItems[0] || this.items[0])?.setFocus();
}
}
//--------------------------------------------------------------------------
//
// Render Methods
//
//--------------------------------------------------------------------------
render() {
const role = this.selectionMode === "none" || this.selectionMode === "multiple" ? "group" : "radiogroup";
return (h("div", { "aria-disabled": toAriaBoolean(this.disabled), "aria-label": this.label, class: "container", role: role }, h("slot", { onSlotchange: this.updateItems, ref: (el) => (this.slotRefEl = el) })));
}
static get is() { return "calcite-chip-group"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() {
return {
"$": ["chip-group.scss"]
};
}
static get styleUrls() {
return {
"$": ["chip-group.css"]
};
}
static get properties() {
return {
"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"
},
"label": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": true,
"optional": false,
"docs": {
"tags": [],
"text": "Accessible name for the component."
},
"attribute": "label",
"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": "Specifies the size of the component. Child `calcite-chip`s inherit the component's value."
},
"attribute": "scale",
"reflect": true,
"defaultValue": "\"m\""
},
"selectionMode": {
"type": "string",
"mutable": false,
"complexType": {
"original": "Extract<\n \"multiple\" | \"single\" | \"single-persist\" | \"none\",\n SelectionMode\n >",
"resolved": "\"multiple\" | \"none\" | \"single\" | \"single-persist\"",
"references": {
"Extract": {
"location": "global"
},
"SelectionMode": {
"location": "import",
"path": "../interfaces"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Specifies the selection mode of the component."
},
"attribute": "selection-mode",
"reflect": true,
"defaultValue": "\"none\""
},
"selectedItems": {
"type": "unknown",
"mutable": true,
"complexType": {
"original": "HTMLCalciteChipElement[]",
"resolved": "HTMLCalciteChipElement[]",
"references": {
"HTMLCalciteChipElement": {
"location": "global"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "readonly",
"text": undefined
}],
"text": "Specifies the component's selected items."
},
"defaultValue": "[]"
}
};
}
static get events() {
return [{
"method": "calciteChipGroupSelect",
"name": "calciteChipGroupSelect",
"bubbles": true,
"cancelable": false,
"composed": true,
"docs": {
"tags": [],
"text": "Emits when the component's selection changes."
},
"complexType": {
"original": "void",
"resolved": "void",
"references": {}
}
}];
}
static get methods() {
return {
"setFocus": {
"complexType": {
"signature": "() => Promise<void>",
"parameters": [],
"references": {
"Promise": {
"location": "global"
}
},
"return": "Promise<void>"
},
"docs": {
"text": "Sets focus on the component's first focusable element.",
"tags": []
}
}
};
}
static get elementRef() { return "el"; }
static get watchers() {
return [{
"propName": "selectionMode",
"methodName": "onSelectionModeChange"
}];
}
static get listeners() {
return [{
"name": "calciteInternalChipKeyEvent",
"method": "calciteInternalChipKeyEventListener",
"target": undefined,
"capture": false,
"passive": false
}, {
"name": "calciteChipClose",
"method": "calciteChipCloseListener",
"target": undefined,
"capture": false,
"passive": false
}, {
"name": "calciteChipSelect",
"method": "calciteChipSelectListener",
"target": undefined,
"capture": false,
"passive": false
}];
}
}