radio.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/core/internal/client';
  5. import { a as addEventListener, b as removeEventListener } from './helpers.js';
  6. import { h as hostContext, c as createColorClasses } from './theme.js';
  7. import { b as getIonMode } from './ionic-global.js';
  8. /**
  9. * Uses the compareWith param to compare two values to determine if they are equal.
  10. *
  11. * @param currentValue The current value of the control.
  12. * @param compareValue The value to compare against.
  13. * @param compareWith The function or property name to use to compare values.
  14. */
  15. const compareOptions = (currentValue, compareValue, compareWith) => {
  16. if (typeof compareWith === 'function') {
  17. return compareWith(currentValue, compareValue);
  18. }
  19. else if (typeof compareWith === 'string') {
  20. return currentValue[compareWith] === compareValue[compareWith];
  21. }
  22. else {
  23. return Array.isArray(compareValue) ? compareValue.includes(currentValue) : currentValue === compareValue;
  24. }
  25. };
  26. /**
  27. * Compares a value against the current value(s) to determine if it is selected.
  28. *
  29. * @param currentValue The current value of the control.
  30. * @param compareValue The value to compare against.
  31. * @param compareWith The function or property name to use to compare values.
  32. */
  33. const isOptionSelected = (currentValue, compareValue, compareWith) => {
  34. if (currentValue === undefined) {
  35. return false;
  36. }
  37. if (Array.isArray(currentValue)) {
  38. return currentValue.some((val) => compareOptions(val, compareValue, compareWith));
  39. }
  40. else {
  41. return compareOptions(currentValue, compareValue, compareWith);
  42. }
  43. };
  44. const radioIosCss = ":host{--inner-border-radius:50%;display:inline-block;position:relative;max-width:100%;min-height:inherit;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:2;-webkit-box-sizing:border-box;box-sizing:border-box}:host(.radio-disabled){pointer-events:none}.radio-icon{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:100%;height:100%;contain:layout size style}.radio-icon,.radio-inner{-webkit-box-sizing:border-box;box-sizing:border-box}input{position:absolute;top:0;left:0;right:0;bottom:0;width:100%;height:100%;margin:0;padding:0;border:0;outline:0;clip:rect(0 0 0 0);opacity:0;overflow:hidden;-webkit-appearance:none;-moz-appearance:none}:host(:focus){outline:none}:host(.in-item){-ms-flex:1 1 0px;flex:1 1 0;width:100%;height:100%}:host([slot=start]),:host([slot=end]){-ms-flex:initial;flex:initial;width:auto}.radio-wrapper{display:-ms-flexbox;display:flex;position:relative;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;height:inherit;min-height:inherit;cursor:inherit}.label-text-wrapper{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}:host(.in-item) .label-text-wrapper{margin-top:10px;margin-bottom:10px}:host(.in-item.radio-label-placement-stacked) .label-text-wrapper{margin-top:10px;margin-bottom:16px}:host(.in-item.radio-label-placement-stacked) .native-wrapper{margin-bottom:10px}.label-text-wrapper-hidden{display:none}.native-wrapper{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}:host(.radio-justify-space-between) .radio-wrapper{-ms-flex-pack:justify;justify-content:space-between}:host(.radio-justify-start) .radio-wrapper{-ms-flex-pack:start;justify-content:start}:host(.radio-justify-end) .radio-wrapper{-ms-flex-pack:end;justify-content:end}:host(.radio-alignment-start) .radio-wrapper{-ms-flex-align:start;align-items:start}:host(.radio-alignment-center) .radio-wrapper{-ms-flex-align:center;align-items:center}:host(.radio-justify-space-between),:host(.radio-justify-start),:host(.radio-justify-end),:host(.radio-alignment-start),:host(.radio-alignment-center){display:block}:host(.radio-label-placement-start) .radio-wrapper{-ms-flex-direction:row;flex-direction:row}:host(.radio-label-placement-start) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.radio-label-placement-end) .radio-wrapper{-ms-flex-direction:row-reverse;flex-direction:row-reverse}:host(.radio-label-placement-end) .label-text-wrapper{-webkit-margin-start:16px;margin-inline-start:16px;-webkit-margin-end:0;margin-inline-end:0}:host(.radio-label-placement-fixed) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.radio-label-placement-fixed) .label-text-wrapper{-ms-flex:0 0 100px;flex:0 0 100px;width:100px;min-width:100px}:host(.radio-label-placement-stacked) .radio-wrapper{-ms-flex-direction:column;flex-direction:column}:host(.radio-label-placement-stacked) .label-text-wrapper{-webkit-transform:scale(0.75);transform:scale(0.75);margin-left:0;margin-right:0;margin-bottom:16px;max-width:calc(100% / 0.75)}:host(.radio-label-placement-stacked.radio-alignment-start) .label-text-wrapper{-webkit-transform-origin:left top;transform-origin:left top}:host-context([dir=rtl]):host(.radio-label-placement-stacked.radio-alignment-start) .label-text-wrapper,:host-context([dir=rtl]).radio-label-placement-stacked.radio-alignment-start .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}@supports selector(:dir(rtl)){:host(.radio-label-placement-stacked.radio-alignment-start:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}}:host(.radio-label-placement-stacked.radio-alignment-center) .label-text-wrapper{-webkit-transform-origin:center top;transform-origin:center top}:host-context([dir=rtl]):host(.radio-label-placement-stacked.radio-alignment-center) .label-text-wrapper,:host-context([dir=rtl]).radio-label-placement-stacked.radio-alignment-center .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}@supports selector(:dir(rtl)){:host(.radio-label-placement-stacked.radio-alignment-center:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}}:host{--color-checked:var(--ion-color-primary, #0054e9)}:host(.ion-color.radio-checked) .radio-inner{border-color:var(--ion-color-base)}.item-radio.item-ios ion-label{-webkit-margin-start:0;margin-inline-start:0}.radio-inner{width:33%;height:50%}:host(.radio-checked) .radio-inner{-webkit-transform:rotate(45deg);transform:rotate(45deg);border-width:0.125rem;border-top-width:0;border-left-width:0;border-style:solid;border-color:var(--color-checked)}:host(.radio-disabled){opacity:0.3}:host(.ion-focused) .radio-icon::after{border-radius:var(--inner-border-radius);top:-8px;display:block;position:absolute;width:36px;height:36px;background:var(--ion-color-primary-tint, #1a65eb);content:\"\";opacity:0.2}:host(.ion-focused) .radio-icon::after{inset-inline-start:-9px}.native-wrapper .radio-icon{width:0.9375rem;height:1.5rem}";
  45. const IonRadioIosStyle0 = radioIosCss;
  46. const radioMdCss = ":host{--inner-border-radius:50%;display:inline-block;position:relative;max-width:100%;min-height:inherit;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:2;-webkit-box-sizing:border-box;box-sizing:border-box}:host(.radio-disabled){pointer-events:none}.radio-icon{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:100%;height:100%;contain:layout size style}.radio-icon,.radio-inner{-webkit-box-sizing:border-box;box-sizing:border-box}input{position:absolute;top:0;left:0;right:0;bottom:0;width:100%;height:100%;margin:0;padding:0;border:0;outline:0;clip:rect(0 0 0 0);opacity:0;overflow:hidden;-webkit-appearance:none;-moz-appearance:none}:host(:focus){outline:none}:host(.in-item){-ms-flex:1 1 0px;flex:1 1 0;width:100%;height:100%}:host([slot=start]),:host([slot=end]){-ms-flex:initial;flex:initial;width:auto}.radio-wrapper{display:-ms-flexbox;display:flex;position:relative;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;height:inherit;min-height:inherit;cursor:inherit}.label-text-wrapper{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}:host(.in-item) .label-text-wrapper{margin-top:10px;margin-bottom:10px}:host(.in-item.radio-label-placement-stacked) .label-text-wrapper{margin-top:10px;margin-bottom:16px}:host(.in-item.radio-label-placement-stacked) .native-wrapper{margin-bottom:10px}.label-text-wrapper-hidden{display:none}.native-wrapper{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}:host(.radio-justify-space-between) .radio-wrapper{-ms-flex-pack:justify;justify-content:space-between}:host(.radio-justify-start) .radio-wrapper{-ms-flex-pack:start;justify-content:start}:host(.radio-justify-end) .radio-wrapper{-ms-flex-pack:end;justify-content:end}:host(.radio-alignment-start) .radio-wrapper{-ms-flex-align:start;align-items:start}:host(.radio-alignment-center) .radio-wrapper{-ms-flex-align:center;align-items:center}:host(.radio-justify-space-between),:host(.radio-justify-start),:host(.radio-justify-end),:host(.radio-alignment-start),:host(.radio-alignment-center){display:block}:host(.radio-label-placement-start) .radio-wrapper{-ms-flex-direction:row;flex-direction:row}:host(.radio-label-placement-start) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.radio-label-placement-end) .radio-wrapper{-ms-flex-direction:row-reverse;flex-direction:row-reverse}:host(.radio-label-placement-end) .label-text-wrapper{-webkit-margin-start:16px;margin-inline-start:16px;-webkit-margin-end:0;margin-inline-end:0}:host(.radio-label-placement-fixed) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.radio-label-placement-fixed) .label-text-wrapper{-ms-flex:0 0 100px;flex:0 0 100px;width:100px;min-width:100px}:host(.radio-label-placement-stacked) .radio-wrapper{-ms-flex-direction:column;flex-direction:column}:host(.radio-label-placement-stacked) .label-text-wrapper{-webkit-transform:scale(0.75);transform:scale(0.75);margin-left:0;margin-right:0;margin-bottom:16px;max-width:calc(100% / 0.75)}:host(.radio-label-placement-stacked.radio-alignment-start) .label-text-wrapper{-webkit-transform-origin:left top;transform-origin:left top}:host-context([dir=rtl]):host(.radio-label-placement-stacked.radio-alignment-start) .label-text-wrapper,:host-context([dir=rtl]).radio-label-placement-stacked.radio-alignment-start .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}@supports selector(:dir(rtl)){:host(.radio-label-placement-stacked.radio-alignment-start:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}}:host(.radio-label-placement-stacked.radio-alignment-center) .label-text-wrapper{-webkit-transform-origin:center top;transform-origin:center top}:host-context([dir=rtl]):host(.radio-label-placement-stacked.radio-alignment-center) .label-text-wrapper,:host-context([dir=rtl]).radio-label-placement-stacked.radio-alignment-center .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}@supports selector(:dir(rtl)){:host(.radio-label-placement-stacked.radio-alignment-center:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}}:host{--color:rgb(var(--ion-text-color-rgb, 0, 0, 0), 0.6);--color-checked:var(--ion-color-primary, #0054e9);--border-width:0.125rem;--border-style:solid;--border-radius:50%}:host(.ion-color) .radio-inner{background:var(--ion-color-base)}:host(.ion-color.radio-checked) .radio-icon{border-color:var(--ion-color-base)}.radio-icon{margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;border-radius:var(--border-radius);border-width:var(--border-width);border-style:var(--border-style);border-color:var(--color)}.radio-inner{border-radius:var(--inner-border-radius);width:calc(50% + var(--border-width));height:calc(50% + var(--border-width));-webkit-transform:scale3d(0, 0, 0);transform:scale3d(0, 0, 0);-webkit-transition:-webkit-transform 280ms cubic-bezier(0.4, 0, 0.2, 1);transition:-webkit-transform 280ms cubic-bezier(0.4, 0, 0.2, 1);transition:transform 280ms cubic-bezier(0.4, 0, 0.2, 1);transition:transform 280ms cubic-bezier(0.4, 0, 0.2, 1), -webkit-transform 280ms cubic-bezier(0.4, 0, 0.2, 1);background:var(--color-checked)}:host(.radio-checked) .radio-icon{border-color:var(--color-checked)}:host(.radio-checked) .radio-inner{-webkit-transform:scale3d(1, 1, 1);transform:scale3d(1, 1, 1)}:host(.radio-disabled) .label-text-wrapper{opacity:0.38}:host(.radio-disabled) .native-wrapper{opacity:0.63}:host(.ion-focused) .radio-icon::after{border-radius:var(--inner-border-radius);display:block;position:absolute;width:36px;height:36px;background:var(--ion-color-primary-tint, #1a65eb);content:\"\";opacity:0.2}.native-wrapper .radio-icon{width:1.25rem;height:1.25rem}";
  47. const IonRadioMdStyle0 = radioMdCss;
  48. const Radio = /*@__PURE__*/ proxyCustomElement(class Radio extends HTMLElement {
  49. constructor() {
  50. super();
  51. this.__registerHost();
  52. this.__attachShadow();
  53. this.ionFocus = createEvent(this, "ionFocus", 7);
  54. this.ionBlur = createEvent(this, "ionBlur", 7);
  55. this.inputId = `ion-rb-${radioButtonIds++}`;
  56. this.radioGroup = null;
  57. this.updateState = () => {
  58. if (this.radioGroup) {
  59. const { compareWith, value: radioGroupValue } = this.radioGroup;
  60. this.checked = isOptionSelected(radioGroupValue, this.value, compareWith);
  61. }
  62. };
  63. this.onClick = () => {
  64. const { radioGroup, checked, disabled } = this;
  65. if (disabled) {
  66. return;
  67. }
  68. /**
  69. * The modern control does not use a native input
  70. * inside of the radio host, so we cannot rely on the
  71. * ev.preventDefault() behavior above. If the radio
  72. * is checked and the parent radio group allows for empty
  73. * selection, then we can set the checked state to false.
  74. * Otherwise, the checked state should always be set
  75. * to true because the checked state cannot be toggled.
  76. */
  77. if (checked && (radioGroup === null || radioGroup === void 0 ? void 0 : radioGroup.allowEmptySelection)) {
  78. this.checked = false;
  79. }
  80. else {
  81. this.checked = true;
  82. }
  83. };
  84. this.onFocus = () => {
  85. this.ionFocus.emit();
  86. };
  87. this.onBlur = () => {
  88. this.ionBlur.emit();
  89. };
  90. this.checked = false;
  91. this.buttonTabindex = -1;
  92. this.color = undefined;
  93. this.name = this.inputId;
  94. this.disabled = false;
  95. this.value = undefined;
  96. this.labelPlacement = 'start';
  97. this.justify = undefined;
  98. this.alignment = undefined;
  99. }
  100. valueChanged() {
  101. /**
  102. * The new value of the radio may
  103. * match the radio group's value,
  104. * so we see if it should be checked.
  105. */
  106. this.updateState();
  107. }
  108. componentDidLoad() {
  109. /**
  110. * The value may be `undefined` if it
  111. * gets set before the radio is
  112. * rendered. This ensures that the radio
  113. * is checked if the value matches. This
  114. * happens most often when Angular is
  115. * rendering the radio.
  116. */
  117. this.updateState();
  118. }
  119. /** @internal */
  120. async setFocus(ev) {
  121. if (ev !== undefined) {
  122. ev.stopPropagation();
  123. ev.preventDefault();
  124. }
  125. this.el.focus();
  126. }
  127. /** @internal */
  128. async setButtonTabindex(value) {
  129. this.buttonTabindex = value;
  130. }
  131. connectedCallback() {
  132. if (this.value === undefined) {
  133. this.value = this.inputId;
  134. }
  135. const radioGroup = (this.radioGroup = this.el.closest('ion-radio-group'));
  136. if (radioGroup) {
  137. this.updateState();
  138. addEventListener(radioGroup, 'ionValueChange', this.updateState);
  139. }
  140. }
  141. disconnectedCallback() {
  142. const radioGroup = this.radioGroup;
  143. if (radioGroup) {
  144. removeEventListener(radioGroup, 'ionValueChange', this.updateState);
  145. this.radioGroup = null;
  146. }
  147. }
  148. get hasLabel() {
  149. return this.el.textContent !== '';
  150. }
  151. renderRadioControl() {
  152. return (h("div", { class: "radio-icon", part: "container" }, h("div", { class: "radio-inner", part: "mark" }), h("div", { class: "radio-ripple" })));
  153. }
  154. render() {
  155. const { checked, disabled, color, el, justify, labelPlacement, hasLabel, buttonTabindex, alignment } = this;
  156. const mode = getIonMode(this);
  157. const inItem = hostContext('ion-item', el);
  158. return (h(Host, { key: '8badd4aec277addc0793e14df21f73bb345e99b7', onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, {
  159. [mode]: true,
  160. 'in-item': inItem,
  161. 'radio-checked': checked,
  162. 'radio-disabled': disabled,
  163. [`radio-justify-${justify}`]: justify !== undefined,
  164. [`radio-alignment-${alignment}`]: alignment !== undefined,
  165. [`radio-label-placement-${labelPlacement}`]: true,
  166. // Focus and active styling should not apply when the radio is in an item
  167. 'ion-activatable': !inItem,
  168. 'ion-focusable': !inItem,
  169. }), 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: {
  170. 'label-text-wrapper': true,
  171. 'label-text-wrapper-hidden': !hasLabel,
  172. }, part: "label" }, h("slot", { key: '331f3dc2ce5f6ed8f124fc4560f92e0f7c668a85' })), h("div", { key: '473bd4aaf448753e385f2dda3fddc9f56379aa19', class: "native-wrapper" }, this.renderRadioControl()))));
  173. }
  174. get el() { return this; }
  175. static get watchers() { return {
  176. "value": ["valueChanged"]
  177. }; }
  178. static get style() { return {
  179. ios: IonRadioIosStyle0,
  180. md: IonRadioMdStyle0
  181. }; }
  182. }, [33, "ion-radio", {
  183. "color": [513],
  184. "name": [1],
  185. "disabled": [4],
  186. "value": [8],
  187. "labelPlacement": [1, "label-placement"],
  188. "justify": [1],
  189. "alignment": [1],
  190. "checked": [32],
  191. "buttonTabindex": [32],
  192. "setFocus": [64],
  193. "setButtonTabindex": [64]
  194. }, undefined, {
  195. "value": ["valueChanged"]
  196. }]);
  197. let radioButtonIds = 0;
  198. function defineCustomElement() {
  199. if (typeof customElements === "undefined") {
  200. return;
  201. }
  202. const components = ["ion-radio"];
  203. components.forEach(tagName => { switch (tagName) {
  204. case "ion-radio":
  205. if (!customElements.get(tagName)) {
  206. customElements.define(tagName, Radio);
  207. }
  208. break;
  209. } });
  210. }
  211. export { Radio as R, compareOptions as c, defineCustomElement as d, isOptionSelected as i };