UNPKG

@postnord/web-components

Version:

PostNord Web Components

256 lines (255 loc) 9.74 kB
/*! * Built with Stencil * By PostNord. */ import { h, Host } from "@stencil/core"; import { angle_down } from "pn-design-assets/pn-assets/icons.js"; import { ripple } from "../../../../index"; /** * Use this row component inside a `pn-accordion` to create an accordion group. * * **Note**: you can use the `pn-accordion-row` without the parent accordion. * But you will have to style the gap between each row. * * @slot label - Include your own HTML as the label. * ``` * <h3 slot="label">Custom title</h3> * ``` */ export class PnAccordionRow { details; summary; content; animation; hostElement; isClosing = false; isExpanding = false; /** The label/title of the accordion row, you can also pass a slot with the name "label" if you want to put custom HTML content in there. */ label; /** The open/close status of the accordion row. */ state = false; /** Optional unique id for the `summary` element. @category ID */ buttonid; /** Optional unique id for the `div` element containing the content. @category ID */ contentid; /** Dispatched every time the row is toggled. Includes the row element and the boolean `state` prop. */ togglerow; /** Internal function for closing rows when using the `single` prop on the `pn-accordion`. */ handleRowState(event) { if (event.detail !== this.state) { this.details.style.overflow = 'hidden'; this.closeAccordion(); } } componentWillLoad() { const slottedLabel = !!this.hostElement.querySelector('[slot="label"]'); this.label = slottedLabel ? null : this.label; } toggleOpen(open) { const state = open ?? !this.state; if (this.state !== open) this.togglerow.emit({ element: this.details, state }); this.state = state; } clickHandler(e) { e.preventDefault(); this.details.style.overflow = 'hidden'; if (this.isClosing || !this.state) this.openAccordion(); else if (this.isClosing || this.state) this.closeAccordion(); const { x, width, y, top } = this.hostElement.getBoundingClientRect(); const clientCor = e.clientX === 0 && e.clientY === 0 ? { clientX: x + width - 24, clientY: y - top } : e; ripple(clientCor, this.hostElement, '.pn-accordion-row-text'); } openAccordion() { this.details.style.height = `${this.details.offsetHeight}px`; this.toggleOpen(true); window.requestAnimationFrame(() => { this.isExpanding = true; const startHeight = `${this.details.offsetHeight}px`; const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`; this.cancelAnimations(); this.animate(true, startHeight, endHeight); }); } closeAccordion() { this.isClosing = true; const startHeight = `${this.details.offsetHeight}px`; const endHeight = `${this.summary.offsetHeight}px`; this.cancelAnimations(); this.animate(false, startHeight, endHeight); } animate(open, startHeight, endHeight) { this.animation = this.details.animate({ height: [startHeight, endHeight], }, { duration: 400, easing: 'cubic-bezier(0.6, 0, 0.2, 1)', }); this.animation.onfinish = () => this.animationFinish(open); this.animation.oncancel = () => { if (open) this.isExpanding = false; else this.isClosing = false; }; } animationFinish(open) { this.toggleOpen(open); this.cancelAnimations(); this.isClosing = false; this.isExpanding = false; this.details.style.height = ''; this.details.style.overflow = ''; } cancelAnimations() { if (this.animation) this.animation.cancel(); } render() { return (h(Host, { key: 'e98e506637a1303e493b529928435dceb965bc35' }, h("details", { key: '34fec5b0c944442f48e8c6d23ef7756366906023', class: "pn-accordion-row", open: this.state, ref: (el) => (this.details = el) }, h("summary", { key: '9f2359b4aa9c3e894a5b436968e1783ac3361c65', id: this.buttonid, class: "pn-accordion-row-summary", onClick: e => this.clickHandler(e), ref: el => (this.summary = el) }, h("div", { key: '261f053b904d2f6f50ea90a610176829cfc2bf89', class: "pn-accordion-row-text" }, this.label ? this.label : '', h("slot", { key: 'ba20d07bfa5af6b771b98b407b7688b65339f341', name: "label" }), h("pn-icon", { key: 'fea80322eb33a895fc5fce7a9245de98e9f28e2a', class: "pn-accordion-row-icon", icon: angle_down }))), h("div", { key: 'f3bf848e95eb68e3bcb268f3a35924709df0823e', id: this.contentid, class: "pn-accordion-row-content", ref: (el) => (this.content = el) }, h("slot", { key: 'd7c939e3ca67e430906c655b347c08c236f528fa' }))))); } static get is() { return "pn-accordion-row"; } static get originalStyleUrls() { return { "$": ["pn-accordion-row.scss"] }; } static get styleUrls() { return { "$": ["pn-accordion-row.css"] }; } static get properties() { return { "label": { "type": "string", "mutable": true, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The label/title of the accordion row, you can also pass a slot with the name \"label\" if you want to put custom HTML content in there." }, "getter": false, "setter": false, "attribute": "label", "reflect": false }, "state": { "type": "boolean", "mutable": true, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "The open/close status of the accordion row." }, "getter": false, "setter": false, "attribute": "state", "reflect": false, "defaultValue": "false" }, "buttonid": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "category", "text": "ID" }], "text": "Optional unique id for the `summary` element." }, "getter": false, "setter": false, "attribute": "buttonid", "reflect": false }, "contentid": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "category", "text": "ID" }], "text": "Optional unique id for the `div` element containing the content." }, "getter": false, "setter": false, "attribute": "contentid", "reflect": false } }; } static get states() { return { "isClosing": {}, "isExpanding": {} }; } static get events() { return [{ "method": "togglerow", "name": "togglerow", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Dispatched every time the row is toggled. Includes the row element and the boolean `state` prop." }, "complexType": { "original": "{ element: HTMLElement; state: Boolean }", "resolved": "{ element: HTMLElement; state: Boolean; }", "references": { "HTMLElement": { "location": "global", "id": "global::HTMLElement" }, "Boolean": { "location": "global", "id": "global::Boolean" } } } }]; } static get elementRef() { return "hostElement"; } static get listeners() { return [{ "name": "rowstate", "method": "handleRowState", "target": undefined, "capture": false, "passive": false }]; } } //# sourceMappingURL=pn-accordion-row.js.map