UNPKG

@postnord/web-components

Version:
432 lines (431 loc) 17 kB
/*! * Built with Stencil * By PostNord. */ import { uuidv4 } from "../../../globals/helpers"; import { h, Host, Mixin } from "@stencil/core"; import { animateHeightFactory } from "../../../globals/mixins/index"; /** * The `pn-radio-button` is built with a native `input[type="radio"]` in the background. * This means you can use native events to listen to the changes. * * @nativeChange Use the `change` event to listen when the radio state is being changed. * * @slot - The default slot for adding custom HTML under the label/helpertext. {@since v7.17.0} * @slot helpertext - Use this slot to add custom HTML inside of the helpertext element. * Only recommended if you need a link without the `tile` prop. {@since v7.17.0} * @slot content - When using the `tile` and `expand` props, this slot will be revealed when the input is checked. {@since v7.17.0} */ export class PnRadioButton extends Mixin(animateHeightFactory) { constructor() { super(); } id = `pn-radio-${uuidv4()}`; idHelpertext = `${this.id}-helpertext`; contentArea; radioInput; hostElement; /** The radio label */ label; /** This adds an optional helpertext under the label. */ helpertext; /** This will be emitted on change and input events. */ value; /** The name of the radio group. */ name; /** Check the radio. */ checked = false; /** A unique HTML ID for the radio. */ radioid = this.id; /** Set the radio as required. @category State */ required = false; /** Disable the radio. @category State */ disabled = false; /** If set to true, color scheme will turn red, indicating that there is an issue or incorrect input related the radio. @category State */ invalid = false; /** * Turn the radio into a clickable tile. A border and padding is added. * * **Do not** use interactive elements (links/buttons) inside of the slots when using this prop. * An exception is made when using the `tile` + `expand` props together, * which allows you to use interactive elements. * * @category Tile */ tile = false; /** * Allow the radio to control the slot area "content". * When checked, the area is visible, when unchecked, the area is hidden. * * The prop `tile` must be used at the same time. * @see {@link tile} * @since v7.17.0 * @category Tile */ expand = false; /** * Add an icon on the right of your radio tile. The `tile` prop must be `true` for the icon to work. * @see {@link tile} * @category Tile */ icon = null; handleId() { this.idHelpertext = `${this.radioid}-helpertext`; } handleChecked(checked) { this.checked = checked ?? this.radioInput.checked; if (!this.displayContent()) return; if (this.checked) { this.openDropdown(this.contentArea); this.uncheckOtherRadios(); } else { this.closeDropdown(this.contentArea); } } handleChange(e) { const target = e.target; const { name } = target; const isSameRadioGroup = name === this.name; const isSameRadio = target === this.radioInput && target.checked; /** * Since content can be nested inside of this component, * double check that the event is coming from this specific radio. */ if (isSameRadioGroup) this.checked = isSameRadio; } componentWillLoad() { this.handleId(); } componentDidLoad() { requestAnimationFrame(() => this.displayContent() && this.checked && this.openDropdown(this.contentArea)); } uncheckOtherRadios() { if (!this.name) return; const selector = `pn-radio-button[name="${this.name}"]`; const list = document.querySelectorAll(selector); const radios = Array.from(list); radios.forEach(radio => radio !== this.hostElement && (radio.checked = false)); } isInvalid() { return this.invalid && !this.disabled; } displayText() { return this.displayLabel() || this.displayHelpertext(); } displayLabel() { return !!this.label; } displayHelpertext() { return !!(this.helpertext || this.hostElement.querySelector('[slot="helpertext"]')?.textContent); } displayIcon() { return this.tile && !!this.icon; } displayContent() { return this.tile && this.expand; } render() { return (h(Host, { key: 'f386b0de0377d3dbe6891a2396d32b91a808d53c' }, h("input", { key: '189e352a198275f4dbd733425166e9cb2ed417ef', type: "radio", id: this.radioid, class: "pn-radio-input", value: this.value, name: this.name, disabled: this.disabled, checked: this.checked, required: this.required, "aria-invalid": this.isInvalid()?.toString(), "aria-describedby": this.displayHelpertext() ? this.idHelpertext : null, "data-tile": this.tile, "data-expand": this.expand, ref: el => (this.radioInput = el) }), h("div", { key: '710dcfe5b1660d25a9d98eb2c3739eb092f2ab4e', class: "pn-radio", "data-tile": this.tile, "data-expand": this.expand, "data-invalid": this.isInvalid() }, h("div", { key: '2d6bd1b03ee6565b88773c60a529ae2c5be89ffd', class: "pn-radio-button" }, h("div", { key: '8b15488acb65132b2498d361d320bb804af793de', class: "pn-radio-outer" }, h("div", { key: '64ebbe9b4d18f158a43bb5d82116ef4b19325ca1', class: "pn-radio-inner" }))), h("p", { key: '3b5ed02eeae60cff45b3b4999055c6d853a6c22f', class: "pn-radio-content", hidden: !this.displayText() }, h("label", { key: 'd5f3c5f59630407a45cc48fcb26e60fe7419c73c', htmlFor: this.radioid, class: "pn-radio-label", hidden: !this.displayLabel() }, this.label), h("span", { key: 'd4329417f178f6e7bcff365a1c051ebaa51b0447', id: this.idHelpertext, class: "pn-radio-helpertext", hidden: !this.displayHelpertext() }, this.helpertext, h("slot", { key: 'defddc5970a0ea4c94c4f7b07e2cec3e7616d8c2', name: "helpertext" })), h("slot", { key: 'edaaf37351bb5d05bdc92696ac9ee2c8bc8e13f7' })), this.displayIcon() && h("pn-icon", { key: 'b9cd0ab07c691020efe39e8bb6ec39d02d5be826', icon: this.icon, color: "gray900" }), h("div", { key: '89655e67304498950e11c0388fddbbde350239a1', class: "pn-radio-container", "data-open": this.checked, hidden: !this.displayContent(), style: { height: '0px' }, ref: el => (this.contentArea = el) }, h("div", { key: 'eeebaf7c0b1fe8327810faec62877888cb6ff95d', class: "pn-radio-area" }, h("slot", { key: 'c997ee7e8ef719ff4b830df30a414cea637a54ff', name: "content" })))))); } static get is() { return "pn-radio-button"; } static get originalStyleUrls() { return { "$": ["pn-radio-button.scss"] }; } static get styleUrls() { return { "$": ["pn-radio-button.css"] }; } static get properties() { return { "label": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "The radio label" }, "getter": false, "setter": false, "reflect": false, "attribute": "label" }, "helpertext": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "This adds an optional helpertext under the label." }, "getter": false, "setter": false, "reflect": false, "attribute": "helpertext" }, "value": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": true, "optional": false, "docs": { "tags": [], "text": "This will be emitted on change and input events." }, "getter": false, "setter": false, "reflect": true, "attribute": "value" }, "name": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "The name of the radio group." }, "getter": false, "setter": false, "reflect": false, "attribute": "name" }, "checked": { "type": "boolean", "mutable": true, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Check the radio." }, "getter": false, "setter": false, "reflect": false, "attribute": "checked", "defaultValue": "false" }, "radioid": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "A unique HTML ID for the radio." }, "getter": false, "setter": false, "reflect": false, "attribute": "radioid", "defaultValue": "this.id" }, "required": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "category", "text": "State" }], "text": "Set the radio as required." }, "getter": false, "setter": false, "reflect": false, "attribute": "required", "defaultValue": "false" }, "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "category", "text": "State" }], "text": "Disable the radio." }, "getter": false, "setter": false, "reflect": false, "attribute": "disabled", "defaultValue": "false" }, "invalid": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "category", "text": "State" }], "text": "If set to true, color scheme will turn red, indicating that there is an issue or incorrect input related the radio." }, "getter": false, "setter": false, "reflect": false, "attribute": "invalid", "defaultValue": "false" }, "tile": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "category", "text": "Tile" }], "text": "Turn the radio into a clickable tile. A border and padding is added.\n\n**Do not** use interactive elements (links/buttons) inside of the slots when using this prop.\nAn exception is made when using the `tile` + `expand` props together,\nwhich allows you to use interactive elements." }, "getter": false, "setter": false, "reflect": false, "attribute": "tile", "defaultValue": "false" }, "expand": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "see", "text": "{@link tile }" }, { "name": "since", "text": "v7.17.0" }, { "name": "category", "text": "Tile" }], "text": "Allow the radio to control the slot area \"content\".\nWhen checked, the area is visible, when unchecked, the area is hidden.\n\nThe prop `tile` must be used at the same time." }, "getter": false, "setter": false, "reflect": false, "attribute": "expand", "defaultValue": "false" }, "icon": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "see", "text": "{@link tile }" }, { "name": "category", "text": "Tile" }], "text": "Add an icon on the right of your radio tile. The `tile` prop must be `true` for the icon to work." }, "getter": false, "setter": false, "reflect": false, "attribute": "icon", "defaultValue": "null" } }; } static get elementRef() { return "hostElement"; } static get watchers() { return [{ "propName": "radioid", "methodName": "handleId" }, { "propName": "checked", "methodName": "handleChecked" }]; } static get listeners() { return [{ "name": "change", "method": "handleChange", "target": "window", "capture": false, "passive": false }]; } }