UNPKG

14.1 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5const index = require('./index-a0a08b2a.js');
6const ionicGlobal = require('./ionic-global-06f21c1a.js');
7const helpers = require('./helpers-d381ec4d.js');
8const theme = require('./theme-30b7a575.js');
9
10const radioIosCss = ":host{--inner-border-radius:50%;display:inline-block;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:2}: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}label{left:0;top:0;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;position:absolute;width:100%;height:100%;border:0;background:transparent;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;opacity:0}[dir=rtl] label,:host-context([dir=rtl]) label{left:unset;right:unset;right:0}label::-moz-focus-inner{border:0}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{--color-checked:var(--ion-color-primary, #3880ff);width:15px;height:24px}:host(.ion-color.radio-checked) .radio-inner{border-color:var(--ion-color-base)}.item-radio.item-ios ion-label{margin-left:0}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){.item-radio.item-ios ion-label{margin-left:unset;-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:2px;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);left:-9px;top:-8px;display:block;position:absolute;width:36px;height:36px;background:var(--ion-color-primary-tint, #4c8dff);content:\"\";opacity:0.2}:host-context([dir=rtl]):host(.ion-focused) .radio-icon::after,:host-context([dir=rtl]).ion-focused .radio-icon::after{left:unset;right:unset;right:-9px}:host(.in-item){margin-left:10px;margin-right:11px;margin-top:8px;margin-bottom:8px;display:block;position:static}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){:host(.in-item){margin-left:unset;margin-right:unset;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:11px;margin-inline-end:11px}}:host(.in-item[slot=start]){margin-left:3px;margin-right:21px;margin-top:8px;margin-bottom:8px}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){:host(.in-item[slot=start]){margin-left:unset;margin-right:unset;-webkit-margin-start:3px;margin-inline-start:3px;-webkit-margin-end:21px;margin-inline-end:21px}}";
11
12const radioMdCss = ":host{--inner-border-radius:50%;display:inline-block;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:2}: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}label{left:0;top:0;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;position:absolute;width:100%;height:100%;border:0;background:transparent;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;opacity:0}[dir=rtl] label,:host-context([dir=rtl]) label{left:unset;right:unset;right:0}label::-moz-focus-inner{border:0}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{--color:var(--ion-color-step-400, #999999);--color-checked:var(--ion-color-primary, #3880ff);--border-width:2px;--border-style:solid;--border-radius:50%;width:20px;height:20px}: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){opacity:0.3}:host(.ion-focused) .radio-icon::after{border-radius:var(--inner-border-radius);left:-12px;top:-12px;display:block;position:absolute;width:36px;height:36px;background:var(--ion-color-primary-tint, #4c8dff);content:\"\";opacity:0.2}:host-context([dir=rtl]):host(.ion-focused) .radio-icon::after,:host-context([dir=rtl]).ion-focused .radio-icon::after{left:unset;right:unset;right:-12px}:host(.in-item){margin-left:0;margin-right:0;margin-top:9px;margin-bottom:9px;display:block;position:static}:host(.in-item[slot=start]){margin-left:4px;margin-right:36px;margin-top:11px;margin-bottom:10px}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){:host(.in-item[slot=start]){margin-left:unset;margin-right:unset;-webkit-margin-start:4px;margin-inline-start:4px;-webkit-margin-end:36px;margin-inline-end:36px}}";
13
14const Radio = class {
15 constructor(hostRef) {
16 index.registerInstance(this, hostRef);
17 this.ionStyle = index.createEvent(this, "ionStyle", 7);
18 this.ionFocus = index.createEvent(this, "ionFocus", 7);
19 this.ionBlur = index.createEvent(this, "ionBlur", 7);
20 this.inputId = `ion-rb-${radioButtonIds++}`;
21 this.radioGroup = null;
22 /**
23 * If `true`, the radio is selected.
24 */
25 this.checked = false;
26 /**
27 * The tabindex of the radio button.
28 * @internal
29 */
30 this.buttonTabindex = -1;
31 /**
32 * The name of the control, which is submitted with the form data.
33 */
34 this.name = this.inputId;
35 /**
36 * If `true`, the user cannot interact with the radio.
37 */
38 this.disabled = false;
39 this.updateState = () => {
40 if (this.radioGroup) {
41 this.checked = this.radioGroup.value === this.value;
42 }
43 };
44 this.onFocus = () => {
45 this.ionFocus.emit();
46 };
47 this.onBlur = () => {
48 this.ionBlur.emit();
49 };
50 }
51 /** @internal */
52 async setFocus(ev) {
53 ev.stopPropagation();
54 ev.preventDefault();
55 this.el.focus();
56 }
57 /** @internal */
58 async setButtonTabindex(value) {
59 this.buttonTabindex = value;
60 }
61 connectedCallback() {
62 if (this.value === undefined) {
63 this.value = this.inputId;
64 }
65 const radioGroup = this.radioGroup = this.el.closest('ion-radio-group');
66 if (radioGroup) {
67 this.updateState();
68 helpers.addEventListener(radioGroup, 'ionChange', this.updateState);
69 }
70 }
71 disconnectedCallback() {
72 const radioGroup = this.radioGroup;
73 if (radioGroup) {
74 helpers.removeEventListener(radioGroup, 'ionChange', this.updateState);
75 this.radioGroup = null;
76 }
77 }
78 componentWillLoad() {
79 this.emitStyle();
80 }
81 emitStyle() {
82 this.ionStyle.emit({
83 'radio-checked': this.checked,
84 'interactive-disabled': this.disabled,
85 });
86 }
87 render() {
88 const { inputId, disabled, checked, color, el, buttonTabindex } = this;
89 const mode = ionicGlobal.getIonMode(this);
90 const { label, labelId, labelText } = helpers.getAriaLabel(el, inputId);
91 return (index.h(index.Host, { "aria-checked": `${checked}`, "aria-hidden": disabled ? 'true' : null, "aria-labelledby": label ? labelId : null, role: "radio", tabindex: buttonTabindex, onFocus: this.onFocus, onBlur: this.onBlur, class: theme.createColorClasses(color, {
92 [mode]: true,
93 'in-item': theme.hostContext('ion-item', el),
94 'interactive': true,
95 'radio-checked': checked,
96 'radio-disabled': disabled,
97 }) }, index.h("div", { class: "radio-icon", part: "container" }, index.h("div", { class: "radio-inner", part: "mark" }), index.h("div", { class: "radio-ripple" })), index.h("label", { htmlFor: inputId }, labelText), index.h("input", { type: "radio", checked: checked, disabled: disabled, tabindex: "-1", id: inputId })));
98 }
99 get el() { return index.getElement(this); }
100 static get watchers() { return {
101 "color": ["emitStyle"],
102 "checked": ["emitStyle"],
103 "disabled": ["emitStyle"]
104 }; }
105};
106let radioButtonIds = 0;
107Radio.style = {
108 ios: radioIosCss,
109 md: radioMdCss
110};
111
112const RadioGroup = class {
113 constructor(hostRef) {
114 index.registerInstance(this, hostRef);
115 this.ionChange = index.createEvent(this, "ionChange", 7);
116 this.inputId = `ion-rg-${radioGroupIds++}`;
117 this.labelId = `${this.inputId}-lbl`;
118 /**
119 * If `true`, the radios can be deselected.
120 */
121 this.allowEmptySelection = false;
122 /**
123 * The name of the control, which is submitted with the form data.
124 */
125 this.name = this.inputId;
126 this.setRadioTabindex = (value) => {
127 const radios = this.getRadios();
128 // Get the first radio that is not disabled and the checked one
129 const first = radios.find(radio => !radio.disabled);
130 const checked = radios.find(radio => (radio.value === value && !radio.disabled));
131 if (!first && !checked) {
132 return;
133 }
134 // If an enabled checked radio exists, set it to be the focusable radio
135 // otherwise we default to focus the first radio
136 const focusable = checked || first;
137 for (const radio of radios) {
138 const tabindex = radio === focusable ? 0 : -1;
139 radio.setButtonTabindex(tabindex);
140 }
141 };
142 this.onClick = (ev) => {
143 ev.preventDefault();
144 const selectedRadio = ev.target && ev.target.closest('ion-radio');
145 if (selectedRadio) {
146 const currentValue = this.value;
147 const newValue = selectedRadio.value;
148 if (newValue !== currentValue) {
149 this.value = newValue;
150 }
151 else if (this.allowEmptySelection) {
152 this.value = undefined;
153 }
154 }
155 };
156 }
157 valueChanged(value) {
158 this.setRadioTabindex(value);
159 this.ionChange.emit({ value });
160 }
161 componentDidLoad() {
162 this.setRadioTabindex(this.value);
163 }
164 async connectedCallback() {
165 // Get the list header if it exists and set the id
166 // this is used to set aria-labelledby
167 const header = this.el.querySelector('ion-list-header') || this.el.querySelector('ion-item-divider');
168 if (header) {
169 const label = this.label = header.querySelector('ion-label');
170 if (label) {
171 this.labelId = label.id = this.name + '-lbl';
172 }
173 }
174 }
175 getRadios() {
176 return Array.from(this.el.querySelectorAll('ion-radio'));
177 }
178 onKeydown(ev) {
179 const inSelectPopover = !!this.el.closest('ion-select-popover');
180 if (ev.target && !this.el.contains(ev.target)) {
181 return;
182 }
183 // Get all radios inside of the radio group and then
184 // filter out disabled radios since we need to skip those
185 const radios = this.getRadios().filter(radio => !radio.disabled);
186 // Only move the radio if the current focus is in the radio group
187 if (ev.target && radios.includes(ev.target)) {
188 const index = radios.findIndex(radio => radio === ev.target);
189 const current = radios[index];
190 let next;
191 // If hitting arrow down or arrow right, move to the next radio
192 // If we're on the last radio, move to the first radio
193 if (['ArrowDown', 'ArrowRight'].includes(ev.code)) {
194 next = (index === radios.length - 1)
195 ? radios[0]
196 : radios[index + 1];
197 }
198 // If hitting arrow up or arrow left, move to the previous radio
199 // If we're on the first radio, move to the last radio
200 if (['ArrowUp', 'ArrowLeft'].includes(ev.code)) {
201 next = (index === 0)
202 ? radios[radios.length - 1]
203 : radios[index - 1];
204 }
205 if (next && radios.includes(next)) {
206 next.setFocus(ev);
207 if (!inSelectPopover) {
208 this.value = next.value;
209 }
210 }
211 // Update the radio group value when a user presses the
212 // space bar on top of a selected radio
213 if (['Space'].includes(ev.code)) {
214 this.value = (this.allowEmptySelection && this.value !== undefined)
215 ? undefined
216 : current.value;
217 // Prevent browsers from jumping
218 // to the bottom of the screen
219 ev.preventDefault();
220 }
221 }
222 }
223 render() {
224 const { label, labelId } = this;
225 const mode = ionicGlobal.getIonMode(this);
226 return (index.h(index.Host, { role: "radiogroup", "aria-labelledby": label ? labelId : null, onClick: this.onClick, class: mode }));
227 }
228 get el() { return index.getElement(this); }
229 static get watchers() { return {
230 "value": ["valueChanged"]
231 }; }
232};
233let radioGroupIds = 0;
234
235exports.ion_radio = Radio;
236exports.ion_radio_group = RadioGroup;