UNPKG

@ionic/core

Version:
389 lines (388 loc) • 15.8 kB
/*! * (C) Ionic http://ionicframework.com - MIT License */ import { Host, h } from "@stencil/core"; import { isOptionSelected } from "../../utils/forms/index"; import { addEventListener, removeEventListener } from "../../utils/helpers"; import { createColorClasses, hostContext } from "../../utils/theme"; import { getIonMode } from "../../global/ionic-global"; /** * @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use. * * @slot - The label text to associate with the radio. Use the "labelPlacement" property to control where the label is placed relative to the radio. * * @part container - The container for the radio mark. * @part label - The label text describing the radio. * @part mark - The checkmark or dot used to indicate the checked state. */ export class Radio { constructor() { this.inputId = `ion-rb-${radioButtonIds++}`; this.radioGroup = null; this.updateState = () => { if (this.radioGroup) { const { compareWith, value: radioGroupValue } = this.radioGroup; this.checked = isOptionSelected(radioGroupValue, this.value, compareWith); } }; this.onClick = () => { const { radioGroup, checked, disabled } = this; if (disabled) { return; } /** * The modern control does not use a native input * inside of the radio host, so we cannot rely on the * ev.preventDefault() behavior above. If the radio * is checked and the parent radio group allows for empty * selection, then we can set the checked state to false. * Otherwise, the checked state should always be set * to true because the checked state cannot be toggled. */ if (checked && (radioGroup === null || radioGroup === void 0 ? void 0 : radioGroup.allowEmptySelection)) { this.checked = false; } else { this.checked = true; } }; this.onFocus = () => { this.ionFocus.emit(); }; this.onBlur = () => { this.ionBlur.emit(); }; this.checked = false; this.buttonTabindex = -1; this.color = undefined; this.name = this.inputId; this.disabled = false; this.value = undefined; this.labelPlacement = 'start'; this.justify = undefined; this.alignment = undefined; } valueChanged() { /** * The new value of the radio may * match the radio group's value, * so we see if it should be checked. */ this.updateState(); } componentDidLoad() { /** * The value may be `undefined` if it * gets set before the radio is * rendered. This ensures that the radio * is checked if the value matches. This * happens most often when Angular is * rendering the radio. */ this.updateState(); } /** @internal */ async setFocus(ev) { if (ev !== undefined) { ev.stopPropagation(); ev.preventDefault(); } this.el.focus(); } /** @internal */ async setButtonTabindex(value) { this.buttonTabindex = value; } connectedCallback() { if (this.value === undefined) { this.value = this.inputId; } const radioGroup = (this.radioGroup = this.el.closest('ion-radio-group')); if (radioGroup) { this.updateState(); addEventListener(radioGroup, 'ionValueChange', this.updateState); } } disconnectedCallback() { const radioGroup = this.radioGroup; if (radioGroup) { removeEventListener(radioGroup, 'ionValueChange', this.updateState); this.radioGroup = null; } } get hasLabel() { return this.el.textContent !== ''; } renderRadioControl() { return (h("div", { class: "radio-icon", part: "container" }, h("div", { class: "radio-inner", part: "mark" }), h("div", { class: "radio-ripple" }))); } render() { const { checked, disabled, color, el, justify, labelPlacement, hasLabel, buttonTabindex, alignment } = this; const mode = getIonMode(this); const inItem = hostContext('ion-item', el); return (h(Host, { key: '8badd4aec277addc0793e14df21f73bb345e99b7', onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, { [mode]: true, 'in-item': inItem, 'radio-checked': checked, 'radio-disabled': disabled, [`radio-justify-${justify}`]: justify !== undefined, [`radio-alignment-${alignment}`]: alignment !== undefined, [`radio-label-placement-${labelPlacement}`]: true, // Focus and active styling should not apply when the radio is in an item 'ion-activatable': !inItem, 'ion-focusable': !inItem, }), role: "radio", "aria-checked": checked ? 'true' : 'false', "aria-disabled": disabled ? 'true' : null, tabindex: buttonTabindex }, h("label", { key: '8765b847edc93a1b5a16506e155ed03da807bb10', class: "radio-wrapper" }, h("div", { key: '3d568a0192a32d4f0b8a920019c79ff02639b5c9', class: { 'label-text-wrapper': true, 'label-text-wrapper-hidden': !hasLabel, }, part: "label" }, h("slot", { key: '331f3dc2ce5f6ed8f124fc4560f92e0f7c668a85' })), h("div", { key: '473bd4aaf448753e385f2dda3fddc9f56379aa19', class: "native-wrapper" }, this.renderRadioControl())))); } static get is() { return "ion-radio"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "ios": ["radio.ios.scss"], "md": ["radio.md.scss"] }; } static get styleUrls() { return { "ios": ["radio.ios.css"], "md": ["radio.md.css"] }; } static get properties() { return { "color": { "type": "string", "mutable": false, "complexType": { "original": "Color", "resolved": "\"danger\" | \"dark\" | \"light\" | \"medium\" | \"primary\" | \"secondary\" | \"success\" | \"tertiary\" | \"warning\" | string & Record<never, never> | undefined", "references": { "Color": { "location": "import", "path": "../../interface", "id": "src/interface.d.ts::Color" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "The color to use from your application's color palette.\nDefault options are: `\"primary\"`, `\"secondary\"`, `\"tertiary\"`, `\"success\"`, `\"warning\"`, `\"danger\"`, `\"light\"`, `\"medium\"`, and `\"dark\"`.\nFor more information on colors, see [theming](/docs/theming/basics)." }, "attribute": "color", "reflect": true }, "name": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The name of the control, which is submitted with the form data." }, "attribute": "name", "reflect": false, "defaultValue": "this.inputId" }, "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "If `true`, the user cannot interact with the radio." }, "attribute": "disabled", "reflect": false, "defaultValue": "false" }, "value": { "type": "any", "mutable": false, "complexType": { "original": "any | null", "resolved": "any", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "the value of the radio." }, "attribute": "value", "reflect": false }, "labelPlacement": { "type": "string", "mutable": false, "complexType": { "original": "'start' | 'end' | 'fixed' | 'stacked'", "resolved": "\"end\" | \"fixed\" | \"stacked\" | \"start\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Where to place the label relative to the radio.\n`\"start\"`: The label will appear to the left of the radio in LTR and to the right in RTL.\n`\"end\"`: The label will appear to the right of the radio in LTR and to the left in RTL.\n`\"fixed\"`: The label has the same behavior as `\"start\"` except it also has a fixed width. Long text will be truncated with ellipses (\"...\").\n`\"stacked\"`: The label will appear above the radio regardless of the direction. The alignment of the label can be controlled with the `alignment` property." }, "attribute": "label-placement", "reflect": false, "defaultValue": "'start'" }, "justify": { "type": "string", "mutable": false, "complexType": { "original": "'start' | 'end' | 'space-between'", "resolved": "\"end\" | \"space-between\" | \"start\" | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "How to pack the label and radio within a line.\n`\"start\"`: The label and radio will appear on the left in LTR and\non the right in RTL.\n`\"end\"`: The label and radio will appear on the right in LTR and\non the left in RTL.\n`\"space-between\"`: The label and radio will appear on opposite\nends of the line with space between the two elements.\nSetting this property will change the radio `display` to `block`." }, "attribute": "justify", "reflect": false }, "alignment": { "type": "string", "mutable": false, "complexType": { "original": "'start' | 'center'", "resolved": "\"center\" | \"start\" | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "How to control the alignment of the radio and label on the cross axis.\n`\"start\"`: The label and control will appear on the left of the cross axis in LTR, and on the right side in RTL.\n`\"center\"`: The label and control will appear at the center of the cross axis in both LTR and RTL.\nSetting this property will change the radio `display` to `block`." }, "attribute": "alignment", "reflect": false } }; } static get states() { return { "checked": {}, "buttonTabindex": {} }; } static get events() { return [{ "method": "ionFocus", "name": "ionFocus", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when the radio button has focus." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "ionBlur", "name": "ionBlur", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when the radio button loses focus." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }]; } static get methods() { return { "setFocus": { "complexType": { "signature": "(ev?: globalThis.Event) => Promise<void>", "parameters": [{ "name": "ev", "type": "Event | undefined", "docs": "" }], "references": { "Promise": { "location": "global", "id": "global::Promise" }, "globalThis": { "location": "global", "id": "global::globalThis" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [{ "name": "internal", "text": undefined }] } }, "setButtonTabindex": { "complexType": { "signature": "(value: number) => Promise<void>", "parameters": [{ "name": "value", "type": "number", "docs": "" }], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [{ "name": "internal", "text": undefined }] } } }; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "value", "methodName": "valueChanged" }]; } } let radioButtonIds = 0;