UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

347 lines (346 loc) • 11.6 kB
/*! * 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, Host } from "@stencil/core"; import { connectConditionalSlotComponent, disconnectConditionalSlotComponent } from "../../utils/conditionalSlot"; import { closestElementCrossShadowBoundary, getElementDir, getElementProp, getSlotted, toAriaBoolean } from "../../utils/dom"; import { CSS_UTILITY } from "../../utils/resources"; import { SLOTS, CSS } from "./resources"; /** * @slot - A slot for adding custom content, including nested `calcite-accordion-item`s. * @slot actions-end - A slot for adding `calcite-action`s or content to the end side of the component's header. * @slot actions-start - A slot for adding `calcite-action`s or content to the start side of the component's header. */ export class AccordionItem { constructor() { /** what icon position does the parent accordion specify */ this.iconPosition = "end"; /** handle clicks on item header */ this.itemHeaderClickHandler = () => this.emitRequestedItem(); /** Specifies the scale of the `accordion-item` controlled by the parent, defaults to m */ this.scale = "m"; this.expanded = false; this.heading = undefined; this.description = undefined; this.iconStart = undefined; this.iconEnd = undefined; this.iconFlipRtl = undefined; } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- connectedCallback() { this.parent = this.el.parentElement; this.iconType = getElementProp(this.el, "icon-type", "chevron"); this.iconPosition = getElementProp(this.el, "icon-position", this.iconPosition); this.scale = getElementProp(this.el, "scale", this.scale); connectConditionalSlotComponent(this); } componentDidLoad() { this.itemPosition = this.getItemPosition(); this.calciteInternalAccordionItemRegister.emit({ parent: this.parent, position: this.itemPosition }); } disconnectedCallback() { disconnectConditionalSlotComponent(this); } // -------------------------------------------------------------------------- // // Render Methods // // -------------------------------------------------------------------------- renderActionsStart() { const { el } = this; return getSlotted(el, SLOTS.actionsStart) ? (h("div", { class: CSS.actionsStart }, h("slot", { name: SLOTS.actionsStart }))) : null; } renderActionsEnd() { const { el } = this; return getSlotted(el, SLOTS.actionsEnd) ? (h("div", { class: CSS.actionsEnd }, h("slot", { name: SLOTS.actionsEnd }))) : null; } render() { const { iconFlipRtl } = this; const dir = getElementDir(this.el); const iconStartEl = this.iconStart ? (h("calcite-icon", { class: CSS.iconStart, flipRtl: iconFlipRtl === "both" || iconFlipRtl === "start", icon: this.iconStart, key: "icon-start", scale: this.scale === "l" ? "m" : "s" })) : null; const iconEndEl = this.iconEnd ? (h("calcite-icon", { class: CSS.iconEnd, flipRtl: iconFlipRtl === "both" || iconFlipRtl === "end", icon: this.iconEnd, key: "icon-end", scale: this.scale === "l" ? "m" : "s" })) : null; const { description } = this; return (h(Host, null, h("div", { class: { [`icon-position--${this.iconPosition}`]: true, [`icon-type--${this.iconType}`]: true } }, h("div", { class: { [CSS.header]: true, [CSS_UTILITY.rtl]: dir === "rtl" } }, this.renderActionsStart(), h("div", { "aria-expanded": toAriaBoolean(this.expanded), class: CSS.headerContent, onClick: this.itemHeaderClickHandler, role: "button", tabindex: "0" }, h("div", { class: CSS.headerContainer }, iconStartEl, h("div", { class: CSS.headerText }, h("span", { class: CSS.heading }, this.heading), description ? h("span", { class: CSS.description }, description) : null), iconEndEl), h("calcite-icon", { class: CSS.expandIcon, icon: this.iconType === "chevron" ? "chevronDown" : this.iconType === "caret" ? "caretDown" : this.expanded ? "minus" : "plus", scale: this.scale === "l" ? "m" : "s" })), this.renderActionsEnd()), h("div", { class: CSS.content }, h("slot", null))))); } //-------------------------------------------------------------------------- // // Event Listeners // //-------------------------------------------------------------------------- keyDownHandler(event) { if (event.target === this.el) { switch (event.key) { case " ": case "Enter": this.emitRequestedItem(); event.preventDefault(); break; } } } updateActiveItemOnChange(event) { this.requestedAccordionItem = event.detail .requestedAccordionItem; if (this.el.parentNode !== this.requestedAccordionItem.parentNode) { return; } this.determineActiveItem(); event.stopPropagation(); } //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- determineActiveItem() { this.selectionMode = getElementProp(this.el, "selection-mode", "multiple"); switch (this.selectionMode) { case "multiple": if (this.el === this.requestedAccordionItem) { this.expanded = !this.expanded; } break; case "single": this.expanded = this.el === this.requestedAccordionItem ? !this.expanded : false; break; case "single-persist": this.expanded = this.el === this.requestedAccordionItem; break; } } emitRequestedItem() { this.calciteInternalAccordionItemSelect.emit({ requestedAccordionItem: this.el }); } getItemPosition() { const { el } = this; const items = closestElementCrossShadowBoundary(el, "calcite-accordion")?.querySelectorAll("calcite-accordion-item"); return items ? Array.from(items).indexOf(el) : -1; } static get is() { return "calcite-accordion-item"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["accordion-item.scss"] }; } static get styleUrls() { return { "$": ["accordion-item.css"] }; } static get properties() { return { "expanded": { "type": "boolean", "mutable": true, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, the component is expanded." }, "attribute": "expanded", "reflect": true, "defaultValue": "false" }, "heading": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies heading text for the component." }, "attribute": "heading", "reflect": false }, "description": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies a description for the component." }, "attribute": "description", "reflect": false }, "iconStart": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies an icon to display at the start of the component." }, "attribute": "icon-start", "reflect": true }, "iconEnd": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies an icon to display at the end of the component." }, "attribute": "icon-end", "reflect": true }, "iconFlipRtl": { "type": "string", "mutable": false, "complexType": { "original": "FlipContext", "resolved": "\"both\" | \"end\" | \"start\"", "references": { "FlipContext": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Displays the `iconStart` and/or `iconEnd` as flipped when the element direction is right-to-left (`\"rtl\"`)." }, "attribute": "icon-flip-rtl", "reflect": true } }; } static get events() { return [{ "method": "calciteInternalAccordionItemSelect", "name": "calciteInternalAccordionItemSelect", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "" }, "complexType": { "original": "RequestedItem", "resolved": "RequestedItem", "references": { "RequestedItem": { "location": "import", "path": "./interfaces" } } } }, { "method": "calciteInternalAccordionItemClose", "name": "calciteInternalAccordionItemClose", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "" }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "calciteInternalAccordionItemRegister", "name": "calciteInternalAccordionItemRegister", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "" }, "complexType": { "original": "RegistryEntry", "resolved": "RegistryEntry", "references": { "RegistryEntry": { "location": "import", "path": "./interfaces" } } } }]; } static get elementRef() { return "el"; } static get listeners() { return [{ "name": "keydown", "method": "keyDownHandler", "target": undefined, "capture": false, "passive": false }, { "name": "calciteInternalAccordionChange", "method": "updateActiveItemOnChange", "target": "body", "capture": false, "passive": false }]; } }