@crowdstrike/glide-core 0.29.2 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accordion.js +240 -1
- package/dist/accordion.styles.js +13 -7
- package/dist/button-group.button.js +143 -1
- package/dist/button-group.button.styles.js +43 -15
- package/dist/button-group.js +249 -1
- package/dist/button-group.styles.js +10 -5
- package/dist/button.js +206 -1
- package/dist/button.styles.js +12 -7
- package/dist/checkbox-group.js +479 -14
- package/dist/checkbox-group.styles.js +5 -2
- package/dist/checkbox.js +519 -32
- package/dist/checkbox.styles.js +10 -5
- package/dist/drawer.js +168 -1
- package/dist/drawer.styles.js +5 -2
- package/dist/dropdown.js +2423 -123
- package/dist/dropdown.option.js +536 -1
- package/dist/dropdown.option.styles.js +5 -2
- package/dist/dropdown.styles.js +14 -7
- package/dist/form-controls-layout.js +102 -1
- package/dist/form-controls-layout.styles.js +5 -2
- package/dist/icon-button.js +139 -1
- package/dist/icon-button.styles.js +19 -7
- package/dist/icons/checked.js +28 -1
- package/dist/icons/chevron.js +21 -1
- package/dist/icons/magnifying-glass.js +23 -1
- package/dist/icons/pencil.js +21 -1
- package/dist/icons/severity-critical.js +20 -1
- package/dist/icons/severity-informational.js +20 -1
- package/dist/icons/severity-medium.js +20 -1
- package/dist/icons/x.js +21 -1
- package/dist/inline-alert.js +118 -1
- package/dist/inline-alert.styles.js +5 -2
- package/dist/input.d.ts +8 -2
- package/dist/input.js +505 -41
- package/dist/input.styles.js +25 -4
- package/dist/label.js +303 -1
- package/dist/label.styles.js +11 -5
- package/dist/library/assert-slot.js +136 -1
- package/dist/library/expect-unhandled-rejection.js +14 -1
- package/dist/library/expect-window-error.js +26 -1
- package/dist/library/final.js +18 -1
- package/dist/library/form-control.js +1 -1
- package/dist/library/localize.js +10 -1
- package/dist/library/mouse.js +35 -1
- package/dist/library/on-resize.js +24 -1
- package/dist/library/required.js +35 -1
- package/dist/library/shadow-root-mode.js +4 -1
- package/dist/library/unique-id.js +3 -1
- package/dist/link.js +92 -1
- package/dist/link.styles.js +10 -5
- package/dist/menu.d.ts +3 -2
- package/dist/menu.js +1259 -1
- package/dist/menu.styles.js +34 -17
- package/dist/modal.d.ts +4 -0
- package/dist/modal.icon-button.js +60 -1
- package/dist/modal.icon-button.styles.js +5 -2
- package/dist/modal.js +473 -1
- package/dist/modal.styles.js +71 -22
- package/dist/option.d.ts +74 -0
- package/dist/option.js +498 -0
- package/dist/option.styles.js +140 -0
- package/dist/{menu.options.d.ts → options.d.ts} +5 -6
- package/dist/options.js +130 -0
- package/dist/options.styles.js +21 -0
- package/dist/popover.js +620 -1
- package/dist/popover.styles.js +11 -5
- package/dist/radio-group.js +624 -17
- package/dist/radio-group.radio.js +211 -1
- package/dist/radio-group.radio.styles.js +9 -4
- package/dist/radio-group.styles.js +5 -2
- package/dist/slider.js +1040 -61
- package/dist/slider.styles.js +9 -4
- package/dist/spinner.js +60 -1
- package/dist/spinner.styles.js +5 -2
- package/dist/split-button.js +116 -1
- package/dist/split-button.primary-button.js +100 -1
- package/dist/split-button.primary-button.styles.js +13 -6
- package/dist/split-button.primary-link.js +102 -1
- package/dist/split-button.secondary-button.d.ts +2 -3
- package/dist/split-button.secondary-button.js +121 -1
- package/dist/split-button.secondary-button.styles.js +12 -7
- package/dist/split-button.styles.js +9 -4
- package/dist/styles/focus-outline.js +9 -3
- package/dist/styles/fonts.css +6 -1
- package/dist/styles/opacity-and-scale-animation.js +6 -3
- package/dist/styles/skeleton.js +6 -3
- package/dist/styles/variables.css +410 -1
- package/dist/styles/visually-hidden.js +6 -3
- package/dist/tab.group.js +386 -1
- package/dist/tab.group.styles.js +5 -2
- package/dist/tab.js +133 -1
- package/dist/tab.panel.js +93 -1
- package/dist/tab.panel.styles.js +11 -5
- package/dist/tab.styles.js +9 -4
- package/dist/tag.js +207 -1
- package/dist/tag.styles.js +10 -5
- package/dist/textarea.js +353 -19
- package/dist/textarea.styles.js +23 -4
- package/dist/toast.js +130 -1
- package/dist/toast.toasts.js +248 -25
- package/dist/toast.toasts.styles.js +9 -4
- package/dist/toggle.js +178 -1
- package/dist/toggle.styles.js +25 -5
- package/dist/tooltip.container.js +130 -1
- package/dist/tooltip.container.styles.js +5 -2
- package/dist/tooltip.js +484 -1
- package/dist/tooltip.styles.js +21 -5
- package/dist/translations/en.js +36 -1
- package/dist/translations/fr.js +37 -1
- package/dist/translations/ja.js +37 -1
- package/package.json +8 -12
- package/dist/menu.button.d.ts +0 -42
- package/dist/menu.button.js +0 -1
- package/dist/menu.button.styles.js +0 -32
- package/dist/menu.link.d.ts +0 -44
- package/dist/menu.link.js +0 -1
- package/dist/menu.link.styles.js +0 -35
- package/dist/menu.options.js +0 -1
- package/dist/menu.options.styles.d.ts +0 -2
- package/dist/menu.options.styles.js +0 -20
- /package/dist/{menu.button.styles.d.ts → option.styles.d.ts} +0 -0
- /package/dist/{menu.link.styles.d.ts → options.styles.d.ts} +0 -0
package/dist/radio-group.js
CHANGED
@@ -1,36 +1,309 @@
|
|
1
|
-
var __decorate
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
6
|
+
};
|
7
|
+
import './label.js';
|
8
|
+
import './tooltip.js';
|
9
|
+
import { html, LitElement } from 'lit';
|
10
|
+
import { classMap } from 'lit/directives/class-map.js';
|
11
|
+
import { createRef, ref } from 'lit/directives/ref.js';
|
12
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
13
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
14
|
+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
15
|
+
import { when } from 'lit/directives/when.js';
|
16
|
+
import packageJson from '../package.json' with { type: 'json' };
|
17
|
+
import RadioGroupRadio from './radio-group.radio.js';
|
18
|
+
import styles from './radio-group.styles.js';
|
19
|
+
import assertSlot from './library/assert-slot.js';
|
20
|
+
import shadowRootMode from './library/shadow-root-mode.js';
|
21
|
+
import final from './library/final.js';
|
22
|
+
import required from './library/required.js';
|
23
|
+
/**
|
24
|
+
* @attr {string} label
|
25
|
+
* @attr {boolean} [disabled=false]
|
26
|
+
* @attr {boolean} [hide-label=false]
|
27
|
+
* @attr {string} [name='']
|
28
|
+
* @attr {'horizontal'} [orientation='horizontal']
|
29
|
+
* @attr {boolean} [required=false]
|
30
|
+
* @attr {string} [tooltip]
|
31
|
+
* @attr {string} [value='']
|
32
|
+
*
|
33
|
+
* @readonly
|
34
|
+
* @attr {string} [version]
|
35
|
+
*
|
36
|
+
* @slot {Radio}
|
37
|
+
* @slot {Element | string} [description] - Additional information or context
|
38
|
+
*
|
39
|
+
* @fires {Event} invalid
|
40
|
+
*
|
41
|
+
* @readonly
|
42
|
+
* @prop {HTMLFormElement | null} form
|
43
|
+
*
|
44
|
+
* @readonly
|
45
|
+
* @prop {ValidityState} validity
|
46
|
+
*
|
47
|
+
* @method checkValidity
|
48
|
+
* @returns boolean
|
49
|
+
*
|
50
|
+
* @method formAssociatedCallback
|
51
|
+
* @method formResetCallback
|
52
|
+
*
|
53
|
+
* @method reportValidity
|
54
|
+
* @returns boolean
|
55
|
+
*
|
56
|
+
* @method resetValidityFeedback
|
57
|
+
*
|
58
|
+
* @method setCustomValidity
|
59
|
+
* @param {string} message
|
60
|
+
*
|
61
|
+
* @method setValidity
|
62
|
+
* @param {ValidityStateFlags} [flags]
|
63
|
+
* @param {string} [message]
|
64
|
+
*/
|
65
|
+
let RadioGroup = class RadioGroup extends LitElement {
|
66
|
+
static { this.formAssociated = true; }
|
67
|
+
static { this.shadowRootOptions = {
|
68
|
+
...LitElement.shadowRootOptions,
|
69
|
+
mode: shadowRootMode,
|
70
|
+
}; }
|
71
|
+
static { this.styles = styles; }
|
72
|
+
/**
|
73
|
+
* @default false
|
74
|
+
*/
|
75
|
+
get disabled() {
|
76
|
+
return this.#isDisabled;
|
77
|
+
}
|
78
|
+
set disabled(isDisabled) {
|
79
|
+
this.#isDisabled = isDisabled;
|
80
|
+
const lastCheckedRadio = this.#radioElements.findLast((radio) => radio.checked);
|
81
|
+
for (const radio of this.#radioElements) {
|
82
|
+
this.#isEnablingOrDisablingTheGroup = true;
|
83
|
+
radio.disabled = isDisabled;
|
84
|
+
this.#isEnablingOrDisablingTheGroup = false;
|
85
|
+
const firstEnabledRadio = this.#radioElements.find((radio) => !radio.disabled);
|
86
|
+
if (isDisabled) {
|
87
|
+
radio.tabIndex = -1;
|
88
|
+
}
|
89
|
+
else if (radio === lastCheckedRadio) {
|
90
|
+
radio.tabIndex = 0;
|
91
|
+
}
|
92
|
+
else if (!lastCheckedRadio && radio === firstEnabledRadio) {
|
93
|
+
radio.tabIndex = 0;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
if (isDisabled) {
|
97
|
+
this.#value = '';
|
98
|
+
}
|
99
|
+
else if (lastCheckedRadio) {
|
100
|
+
this.#value = lastCheckedRadio.value;
|
101
|
+
}
|
102
|
+
}
|
103
|
+
/**
|
104
|
+
* @default false
|
105
|
+
*/
|
106
|
+
get required() {
|
107
|
+
return this.#isRequired;
|
108
|
+
}
|
109
|
+
set required(isRequired) {
|
110
|
+
this.#isRequired = isRequired;
|
111
|
+
// Changes to `required` change the validity state. If no radios are
|
112
|
+
// checked and Radio Group is `required`, then Radio Group is invalid.
|
113
|
+
// However, if `required` is programmatically removed, then Radio Group
|
114
|
+
// is suddenly valid.
|
115
|
+
for (const radio of this.#radioElements) {
|
116
|
+
radio.privateRequired = isRequired;
|
117
|
+
}
|
118
|
+
}
|
119
|
+
// Intentionally not reflected to match native.
|
120
|
+
/**
|
121
|
+
* @default ''
|
122
|
+
*/
|
123
|
+
get value() {
|
124
|
+
return this.#value;
|
125
|
+
}
|
126
|
+
set value(value) {
|
127
|
+
this.#value = value;
|
128
|
+
if (this.#isSettingValueAttributeInitially) {
|
129
|
+
return;
|
130
|
+
}
|
131
|
+
for (const radio of this.#radioElements) {
|
132
|
+
// If both `value` and the radio's `value` are empty strings, the
|
133
|
+
// desired outcome from the perspective of the consumer is almost
|
134
|
+
// certainly not to check the radio.
|
135
|
+
const isChecked = Boolean(value !== '' && radio.value === value);
|
136
|
+
radio.checked = isChecked ? true : false;
|
137
|
+
radio.tabIndex = isChecked ? 0 : -1;
|
138
|
+
// We have a few options if `value` is set programmatically to include
|
139
|
+
// the value of a disabled Radio. We can throw, remove the value from
|
140
|
+
// `value`, or enable the Radio.
|
141
|
+
//
|
142
|
+
// Throwing is attractive because the inclusion of a disabled Radio
|
143
|
+
// in `value` is likely a mistake, either due to bad data or developer
|
144
|
+
// error.
|
145
|
+
//
|
146
|
+
// But we only throw in development. So the form will be submitted with
|
147
|
+
// the new `value` in production regardless if it was by mistake. By enabling
|
148
|
+
// the Radio, we at least ensure the user is aware of the fact that it'll
|
149
|
+
// be included in the submission.
|
150
|
+
if (radio.checked && radio.disabled) {
|
151
|
+
radio.disabled = false;
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
checkValidity() {
|
156
|
+
this.isCheckingValidity = true;
|
157
|
+
const isValid = this.#internals.checkValidity();
|
158
|
+
this.isCheckingValidity = false;
|
159
|
+
return isValid;
|
160
|
+
}
|
161
|
+
disconnectedCallback() {
|
162
|
+
super.disconnectedCallback();
|
163
|
+
this.form?.removeEventListener('formdata', this.#onFormdata);
|
164
|
+
}
|
165
|
+
firstUpdated() {
|
166
|
+
for (const radio of this.#radioElements) {
|
167
|
+
radio.privateRequired = this.required;
|
168
|
+
radio.tabIndex = -1;
|
169
|
+
if (this.disabled) {
|
170
|
+
radio.disabled = this.disabled;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
// There's nothing to stop developers from adding a `checked` attribute to more
|
174
|
+
// than one radio. How native handles it with `<select>` is to choose the last
|
175
|
+
// selected option. This mimics that behavior, which seems reasonable.
|
176
|
+
const lastCheckedRadio = this.#radioElements.findLast(({ checked, disabled }) => checked && !disabled);
|
177
|
+
if (lastCheckedRadio) {
|
178
|
+
this.#value = lastCheckedRadio.value;
|
179
|
+
if (!this.disabled) {
|
180
|
+
lastCheckedRadio.tabIndex = 0;
|
181
|
+
}
|
182
|
+
// When there's not a default `value` set on the group and a radio is checked
|
183
|
+
// set, we need to manually set `value` on the group. That way when `form.reset()`
|
184
|
+
// is called we know what value to reset back to.
|
185
|
+
//
|
186
|
+
// `#isSettingValueAttributeInitially` is used to guard against the `value` the
|
187
|
+
// setter unchecking every radio but the last. We try our best not to change state
|
188
|
+
// set by the consumer. If a consumer checks multiple radios, either initially or
|
189
|
+
// programmatically, we leave them checked and simply don't show them as checked to
|
190
|
+
// the user. That way what the user sees matches what gets submitted with the form.
|
191
|
+
this.#isSettingValueAttributeInitially = true;
|
192
|
+
this.setAttribute('value', lastCheckedRadio.value);
|
193
|
+
this.#isSettingValueAttributeInitially = false;
|
194
|
+
return;
|
195
|
+
}
|
196
|
+
// When `value` is set on initial render, its setter is called before
|
197
|
+
// `connectedCallback()` and thus before the default slot has any assigned
|
198
|
+
// elements. So we set it again here after the initial render is complete
|
199
|
+
// so `this.#radioElements` isn't empty.
|
200
|
+
//
|
201
|
+
// Additionally, `#onDefaultSlotChange()` is called after `firstUpdated()`
|
202
|
+
// and sets `value` based on which radios are checked. And the initial `value`
|
203
|
+
// may conflict with the one derived from which radios are checked.
|
204
|
+
//
|
205
|
+
// So we have a decision to make. On first render, do we defer to the initial
|
206
|
+
// `value` and check a radio below? Or do we defer to `#onDefaultSlotChange()`
|
207
|
+
// and let that method change `value` from its initial value based on which radios
|
208
|
+
// are checked?
|
209
|
+
//
|
210
|
+
// It's largely a toss-up. But the latter seems like the logical choice given
|
211
|
+
// `#onDefaultSlotChange()` is called after `firstUpdated()`. In other words, we
|
212
|
+
// defer to the lifecycle. `#onDefaultSlotChange()` is called second. So it gets
|
213
|
+
// to override what `value` was initially.
|
214
|
+
//
|
215
|
+
// If no radios are checked, then it's obvious that the consumer's intention is
|
216
|
+
// to check radios based on the initial `value` and that the initial `value` is
|
217
|
+
// the intended one. So we proceed.
|
218
|
+
if (!lastCheckedRadio && this.value !== '') {
|
219
|
+
const lastRadioWithMatchingValue = this.#radioElements.findLast(({ value }) => value === this.value);
|
220
|
+
if (!this.disabled && lastRadioWithMatchingValue?.disabled) {
|
221
|
+
lastRadioWithMatchingValue.disabled = false;
|
222
|
+
}
|
223
|
+
if (lastRadioWithMatchingValue) {
|
224
|
+
lastRadioWithMatchingValue.checked = true;
|
225
|
+
lastRadioWithMatchingValue.tabIndex = 0;
|
226
|
+
}
|
227
|
+
return;
|
228
|
+
}
|
229
|
+
const firstEnabledRadio = this.#radioElements.find(({ disabled }) => !disabled);
|
230
|
+
if (firstEnabledRadio) {
|
231
|
+
firstEnabledRadio.tabIndex = 0;
|
232
|
+
return;
|
233
|
+
}
|
234
|
+
}
|
235
|
+
focus(options) {
|
236
|
+
const radio = this.#radioElements.find(({ checked, disabled }) => checked && !disabled) ?? this.#radioElements.find(({ tabIndex }) => tabIndex === 0);
|
237
|
+
radio?.focus(options);
|
238
|
+
}
|
239
|
+
get form() {
|
240
|
+
return this.#internals.form;
|
241
|
+
}
|
242
|
+
get validity() {
|
243
|
+
const isChecked = this.#radioElements.some(({ checked }) => checked);
|
244
|
+
if (this.required && !isChecked && !this.disabled) {
|
245
|
+
// A validation message is required but unused because we disable native validation
|
246
|
+
// feedback. And an empty string isn't allowed. Thus a single space.
|
247
|
+
this.#internals.setValidity({ customError: Boolean(this.validityMessage), valueMissing: true }, ' ', this.#componentElementRef.value);
|
248
|
+
return this.#internals.validity;
|
249
|
+
}
|
250
|
+
if (this.required && this.#internals.validity.valueMissing && isChecked) {
|
251
|
+
this.#internals.setValidity({});
|
252
|
+
return this.#internals.validity;
|
253
|
+
}
|
254
|
+
if (this.required && this.disabled && !isChecked) {
|
255
|
+
this.#internals.setValidity({ valueMissing: true }, ' ', this.#componentElementRef.value);
|
256
|
+
return this.#internals.validity;
|
257
|
+
}
|
258
|
+
if (!this.required && this.#internals.validity.valueMissing && !isChecked) {
|
259
|
+
this.#internals.setValidity({});
|
260
|
+
return this.#internals.validity;
|
261
|
+
}
|
262
|
+
return this.#internals.validity;
|
263
|
+
}
|
264
|
+
formAssociatedCallback() {
|
265
|
+
this.form?.addEventListener('formdata', this.#onFormdata);
|
266
|
+
}
|
267
|
+
formResetCallback() {
|
268
|
+
this.value = this.getAttribute('value') ?? '';
|
269
|
+
}
|
270
|
+
render() {
|
271
|
+
return html `
|
2
272
|
<div
|
3
273
|
class="component"
|
4
|
-
@click=${this.#
|
5
|
-
@keydown=${this.#
|
6
|
-
${ref(this.#
|
274
|
+
@click=${this.#onComponentClick}
|
275
|
+
@keydown=${this.#onComponentKeydown}
|
276
|
+
${ref(this.#componentElementRef)}
|
7
277
|
>
|
8
278
|
<glide-core-private-label
|
9
279
|
label=${ifDefined(this.label)}
|
10
280
|
orientation=${this.orientation}
|
11
|
-
split=${ifDefined(this.privateSplit??
|
281
|
+
split=${ifDefined(this.privateSplit ?? undefined)}
|
12
282
|
tooltip=${ifDefined(this.tooltip)}
|
13
283
|
?disabled=${this.disabled}
|
14
|
-
?error=${this.#
|
284
|
+
?error=${this.#isShowValidationFeedback}
|
15
285
|
?hide=${this.hideLabel}
|
16
286
|
?required=${this.required}
|
17
287
|
>
|
18
288
|
<label id="label" data-test="label"> ${this.label} </label>
|
19
289
|
|
20
290
|
<div
|
21
|
-
class=${classMap({
|
291
|
+
class=${classMap({
|
292
|
+
'radio-container': true,
|
293
|
+
invalid: this.#isShowValidationFeedback,
|
294
|
+
})}
|
22
295
|
role="radiogroup"
|
23
296
|
slot="control"
|
24
297
|
aria-labelledby="label description"
|
25
298
|
>
|
26
299
|
<slot
|
27
|
-
@focusout=${this.#
|
28
|
-
@private-checked-change=${this.#
|
29
|
-
@private-disabled-change=${this.#
|
30
|
-
@private-value-change=${this.#
|
31
|
-
@slotchange=${this.#
|
300
|
+
@focusout=${this.#onRadioGroupFocusout}
|
301
|
+
@private-checked-change=${this.#onRadiosCheckedChange}
|
302
|
+
@private-disabled-change=${this.#onRadiosDisabledChange}
|
303
|
+
@private-value-change=${this.#onRadiosValueChange}
|
304
|
+
@slotchange=${this.#onDefaultSlotChange}
|
32
305
|
${assertSlot([RadioGroupRadio])}
|
33
|
-
${ref(this.#
|
306
|
+
${ref(this.#defaultSlotElementRef)}
|
34
307
|
>
|
35
308
|
<!-- @type {Radio} -->
|
36
309
|
</slot>
|
@@ -38,7 +311,10 @@ var __decorate=this&&this.__decorate||function(e,t,i,a){var s,o=arguments.length
|
|
38
311
|
|
39
312
|
<div id="description" slot="description">
|
40
313
|
<slot
|
41
|
-
class=${classMap({
|
314
|
+
class=${classMap({
|
315
|
+
'description-slot': true,
|
316
|
+
hidden: Boolean(this.#isShowValidationFeedback && this.validityMessage),
|
317
|
+
})}
|
42
318
|
name="description"
|
43
319
|
>
|
44
320
|
<!--
|
@@ -47,10 +323,341 @@ var __decorate=this&&this.__decorate||function(e,t,i,a){var s,o=arguments.length
|
|
47
323
|
-->
|
48
324
|
</slot>
|
49
325
|
|
50
|
-
${when(this.#
|
326
|
+
${when(this.#isShowValidationFeedback && this.validityMessage, () => html `<div data-test="validity-message">
|
51
327
|
${unsafeHTML(this.validityMessage)}
|
52
|
-
</div>`)
|
328
|
+
</div>`)}
|
53
329
|
</div>
|
54
330
|
</glide-core-private-label>
|
55
331
|
</div>
|
56
|
-
|
332
|
+
`;
|
333
|
+
}
|
334
|
+
reportValidity() {
|
335
|
+
this.isReportValidityOrSubmit = true;
|
336
|
+
const isValid = this.#internals.reportValidity();
|
337
|
+
// Ensures that getters referencing `this.validity.valid` are updated.
|
338
|
+
this.requestUpdate();
|
339
|
+
return isValid;
|
340
|
+
}
|
341
|
+
resetValidityFeedback() {
|
342
|
+
this.isReportValidityOrSubmit = false;
|
343
|
+
}
|
344
|
+
setCustomValidity(message) {
|
345
|
+
this.validityMessage = message;
|
346
|
+
if (message === '') {
|
347
|
+
this.#internals.setValidity({ customError: false }, '', this.#componentElementRef.value);
|
348
|
+
}
|
349
|
+
else {
|
350
|
+
this.#internals.setValidity({
|
351
|
+
customError: true,
|
352
|
+
valueMissing: this.#internals.validity.valueMissing,
|
353
|
+
}, ' ', this.#componentElementRef.value);
|
354
|
+
}
|
355
|
+
}
|
356
|
+
setValidity(flags, message) {
|
357
|
+
this.validityMessage = message;
|
358
|
+
this.#internals.setValidity(flags, ' ', this.#componentElementRef.value);
|
359
|
+
}
|
360
|
+
constructor() {
|
361
|
+
super();
|
362
|
+
this.hideLabel = false;
|
363
|
+
this.name = '';
|
364
|
+
this.orientation = 'horizontal';
|
365
|
+
this.version = packageJson.version;
|
366
|
+
this.isBlurring = false;
|
367
|
+
this.isCheckingValidity = false;
|
368
|
+
this.isReportValidityOrSubmit = false;
|
369
|
+
this.#componentElementRef = createRef();
|
370
|
+
this.#defaultSlotElementRef = createRef();
|
371
|
+
this.#isDisabled = false;
|
372
|
+
this.#isEnablingOrDisablingTheGroup = false;
|
373
|
+
this.#isRequired = false;
|
374
|
+
this.#isSettingValueAttributeInitially = false;
|
375
|
+
this.#value = '';
|
376
|
+
// An arrow function field instead of a method so `this` is closed over and
|
377
|
+
// set to the component instead of the form.
|
378
|
+
this.#onFormdata = ({ formData }) => {
|
379
|
+
if (this.name && this.value.length > 0 && !this.disabled) {
|
380
|
+
formData.append(this.name, this.value);
|
381
|
+
}
|
382
|
+
};
|
383
|
+
this.#internals = this.attachInternals();
|
384
|
+
this.addEventListener('invalid', (event) => {
|
385
|
+
event.preventDefault();
|
386
|
+
if (!this.isCheckingValidity) {
|
387
|
+
// Canceled so a native validation message isn't shown.
|
388
|
+
event?.preventDefault();
|
389
|
+
// We only want to focus the radios if the invalid event resulted from either:
|
390
|
+
// 1. Form submission
|
391
|
+
// 2. a call to reportValidity that did NOT result from the input blur event
|
392
|
+
if (this.isCheckingValidity || this.isBlurring) {
|
393
|
+
return;
|
394
|
+
}
|
395
|
+
this.isReportValidityOrSubmit = true;
|
396
|
+
const isFirstInvalidFormElement = this.form?.querySelector(':invalid') === this;
|
397
|
+
if (isFirstInvalidFormElement) {
|
398
|
+
this.focus();
|
399
|
+
}
|
400
|
+
}
|
401
|
+
});
|
402
|
+
}
|
403
|
+
#componentElementRef;
|
404
|
+
#defaultSlotElementRef;
|
405
|
+
#internals;
|
406
|
+
#isDisabled;
|
407
|
+
#isEnablingOrDisablingTheGroup;
|
408
|
+
#isRequired;
|
409
|
+
#isSettingValueAttributeInitially;
|
410
|
+
#value;
|
411
|
+
// An arrow function field instead of a method so `this` is closed over and
|
412
|
+
// set to the component instead of the form.
|
413
|
+
#onFormdata;
|
414
|
+
get #isShowValidationFeedback() {
|
415
|
+
const isInvalid = !this.disabled && !this.validity.valid && this.isReportValidityOrSubmit;
|
416
|
+
for (const radioItem of this.#radioElements) {
|
417
|
+
radioItem.privateInvalid = isInvalid;
|
418
|
+
}
|
419
|
+
return isInvalid;
|
420
|
+
}
|
421
|
+
#checkRadio(radio) {
|
422
|
+
this.#radioElements
|
423
|
+
.find(({ tabIndex }) => tabIndex === 0)
|
424
|
+
?.setAttribute('tabindex', '-1');
|
425
|
+
this.#value = radio.value;
|
426
|
+
radio.checked = true;
|
427
|
+
radio.tabIndex = 0;
|
428
|
+
radio.focus();
|
429
|
+
}
|
430
|
+
#onComponentClick(event) {
|
431
|
+
if (this.disabled) {
|
432
|
+
return;
|
433
|
+
}
|
434
|
+
// If the user clicks on a disabled radio, then attempts to use the keyboard, the
|
435
|
+
// focus would normally be stuck on the disabled element. Since the general pattern
|
436
|
+
// is for focus to follow selection, it does so here, going to the last checked
|
437
|
+
// radio.
|
438
|
+
if (event.target instanceof RadioGroupRadio && event.target.disabled) {
|
439
|
+
const selectedRadio = this.#radioElements.find(({ checked }) => checked);
|
440
|
+
selectedRadio?.focus();
|
441
|
+
return;
|
442
|
+
}
|
443
|
+
if (event.target instanceof RadioGroupRadio && !event.target.disabled) {
|
444
|
+
const radiosToUncheck = this.#radioElements.filter((radio) => radio !== event.target);
|
445
|
+
for (const radio of radiosToUncheck) {
|
446
|
+
radio.checked = false;
|
447
|
+
radio.tabIndex = -1;
|
448
|
+
}
|
449
|
+
this.#checkRadio(event.target);
|
450
|
+
}
|
451
|
+
}
|
452
|
+
#onComponentKeydown(event) {
|
453
|
+
if (this.disabled ||
|
454
|
+
(event.target instanceof RadioGroupRadio && event.target?.disabled)) {
|
455
|
+
return;
|
456
|
+
}
|
457
|
+
if (event.target instanceof RadioGroupRadio) {
|
458
|
+
switch (event.key) {
|
459
|
+
case 'Enter': {
|
460
|
+
this.form?.requestSubmit();
|
461
|
+
break;
|
462
|
+
}
|
463
|
+
case 'ArrowUp':
|
464
|
+
case 'ArrowLeft': {
|
465
|
+
event.preventDefault();
|
466
|
+
const previousEnabledRadio = this.#radioElements
|
467
|
+
.slice(0, this.#radioElements.indexOf(event.target))
|
468
|
+
.findLast((radio) => !radio.disabled);
|
469
|
+
const lastEnabledRadio = this.#radioElements.findLast((radio) => !radio.disabled);
|
470
|
+
if (previousEnabledRadio && previousEnabledRadio !== event.target) {
|
471
|
+
this.#uncheckRadio(event.target);
|
472
|
+
this.#checkRadio(previousEnabledRadio);
|
473
|
+
}
|
474
|
+
else if (lastEnabledRadio && lastEnabledRadio !== event.target) {
|
475
|
+
this.#uncheckRadio(event.target);
|
476
|
+
this.#checkRadio(lastEnabledRadio);
|
477
|
+
}
|
478
|
+
break;
|
479
|
+
}
|
480
|
+
case 'ArrowDown':
|
481
|
+
case 'ArrowRight': {
|
482
|
+
event.preventDefault();
|
483
|
+
const nextEnabledRadio = this.#radioElements.find((radio, index) => {
|
484
|
+
return (!radio.disabled &&
|
485
|
+
event.target instanceof RadioGroupRadio &&
|
486
|
+
index > this.#radioElements.indexOf(event.target));
|
487
|
+
});
|
488
|
+
const firstEnabledRadio = this.#radioElements.find((radio) => !radio.disabled);
|
489
|
+
if (nextEnabledRadio && nextEnabledRadio !== event.target) {
|
490
|
+
this.#uncheckRadio(event.target);
|
491
|
+
this.#checkRadio(nextEnabledRadio);
|
492
|
+
}
|
493
|
+
else if (firstEnabledRadio && firstEnabledRadio !== event.target) {
|
494
|
+
this.#uncheckRadio(event.target);
|
495
|
+
this.#checkRadio(firstEnabledRadio);
|
496
|
+
}
|
497
|
+
break;
|
498
|
+
}
|
499
|
+
case ' ': {
|
500
|
+
event.preventDefault();
|
501
|
+
if (!event.target.disabled && !event.target.checked) {
|
502
|
+
this.#checkRadio(event.target);
|
503
|
+
for (const radio of this.#radioElements) {
|
504
|
+
if (radio !== event.target) {
|
505
|
+
this.#uncheckRadio(radio);
|
506
|
+
}
|
507
|
+
}
|
508
|
+
}
|
509
|
+
break;
|
510
|
+
}
|
511
|
+
}
|
512
|
+
}
|
513
|
+
}
|
514
|
+
#onDefaultSlotChange() {
|
515
|
+
const lastCheckedRadio = this.#radioElements.findLast(({ checked, disabled }) => {
|
516
|
+
return checked && !disabled;
|
517
|
+
});
|
518
|
+
if (lastCheckedRadio) {
|
519
|
+
this.#value = lastCheckedRadio.value;
|
520
|
+
}
|
521
|
+
}
|
522
|
+
#onRadioGroupFocusout(event) {
|
523
|
+
// If `event.relatedTarget` is `null`, the user has clicked an element outside
|
524
|
+
// Radio Group that cannot receive focus. Otherwise, the user has either clicked
|
525
|
+
// an element outside Radio Group that can receive focus or else has tabbed away
|
526
|
+
// from Radio Group.
|
527
|
+
const isFocusLost = event.relatedTarget === null ||
|
528
|
+
(event.relatedTarget instanceof Node &&
|
529
|
+
!this.contains(event.relatedTarget));
|
530
|
+
if (isFocusLost) {
|
531
|
+
this.isBlurring = true;
|
532
|
+
this.reportValidity();
|
533
|
+
this.isBlurring = false;
|
534
|
+
}
|
535
|
+
}
|
536
|
+
get #radioElements() {
|
537
|
+
return this.#defaultSlotElementRef.value
|
538
|
+
? this.#defaultSlotElementRef.value
|
539
|
+
.assignedElements()
|
540
|
+
.filter((element) => element instanceof RadioGroupRadio)
|
541
|
+
: [];
|
542
|
+
}
|
543
|
+
#onRadiosCheckedChange(event) {
|
544
|
+
if (event.target instanceof RadioGroupRadio &&
|
545
|
+
event.target.checked &&
|
546
|
+
!event.detail.old &&
|
547
|
+
event.detail.new) {
|
548
|
+
const checkedRadio = this.#radioElements.find(({ tabIndex }) => tabIndex === 0);
|
549
|
+
if (checkedRadio && checkedRadio !== event.target) {
|
550
|
+
this.#uncheckRadio(checkedRadio);
|
551
|
+
}
|
552
|
+
this.#value = event.target.value;
|
553
|
+
event.target.tabIndex = event.target.disabled ? -1 : 0;
|
554
|
+
}
|
555
|
+
}
|
556
|
+
#onRadiosDisabledChange(event) {
|
557
|
+
if (this.#isEnablingOrDisablingTheGroup) {
|
558
|
+
return;
|
559
|
+
}
|
560
|
+
if (event.target instanceof RadioGroupRadio && event.target.disabled) {
|
561
|
+
const lastCheckedAndEnabledRadio = this.#radioElements.findLast((radio) => radio.checked && !radio.disabled);
|
562
|
+
if (event.target.checked && !lastCheckedAndEnabledRadio) {
|
563
|
+
this.#value = '';
|
564
|
+
}
|
565
|
+
else if (lastCheckedAndEnabledRadio) {
|
566
|
+
this.#value = lastCheckedAndEnabledRadio.value;
|
567
|
+
}
|
568
|
+
const nextEnabledRadio = this.#radioElements.find((radio, index) => {
|
569
|
+
return (!radio.disabled &&
|
570
|
+
event.target instanceof RadioGroupRadio &&
|
571
|
+
index > this.#radioElements.indexOf(event.target));
|
572
|
+
});
|
573
|
+
if (nextEnabledRadio && event.target.tabIndex === 0) {
|
574
|
+
nextEnabledRadio.tabIndex = 0;
|
575
|
+
event.target.tabIndex = -1;
|
576
|
+
return;
|
577
|
+
}
|
578
|
+
const firstEnabledRadio = this.#radioElements.find((radio) => !radio.disabled);
|
579
|
+
if (firstEnabledRadio && event.target.tabIndex === 0) {
|
580
|
+
firstEnabledRadio.tabIndex = 0;
|
581
|
+
event.target.tabIndex = -1;
|
582
|
+
return;
|
583
|
+
}
|
584
|
+
}
|
585
|
+
if (event.target instanceof RadioGroupRadio && !this.disabled) {
|
586
|
+
const hasTabbableRadio = this.#radioElements.some(({ tabIndex }) => tabIndex === 0);
|
587
|
+
if (!hasTabbableRadio) {
|
588
|
+
event.target.tabIndex = 0;
|
589
|
+
}
|
590
|
+
const lastCheckedAndEnabledRadio = this.#radioElements.findLast((radio) => radio.checked && !radio.disabled);
|
591
|
+
if (event.target === lastCheckedAndEnabledRadio) {
|
592
|
+
this.#value = event.target.value;
|
593
|
+
}
|
594
|
+
return;
|
595
|
+
}
|
596
|
+
}
|
597
|
+
#onRadiosValueChange(event) {
|
598
|
+
const lastCheckedAndEnabledRadio = this.#radioElements.findLast((radio) => radio.checked && !this.disabled);
|
599
|
+
if (event.target instanceof RadioGroupRadio &&
|
600
|
+
event.target.checked &&
|
601
|
+
event.detail.new &&
|
602
|
+
event.target === lastCheckedAndEnabledRadio) {
|
603
|
+
this.#value = event.target.value;
|
604
|
+
}
|
605
|
+
else if (event.target instanceof RadioGroupRadio &&
|
606
|
+
event.target.checked &&
|
607
|
+
event.target === lastCheckedAndEnabledRadio) {
|
608
|
+
this.#value = '';
|
609
|
+
}
|
610
|
+
}
|
611
|
+
#uncheckRadio(radio) {
|
612
|
+
radio.checked = false;
|
613
|
+
radio.tabIndex = -1;
|
614
|
+
}
|
615
|
+
};
|
616
|
+
__decorate([
|
617
|
+
property({ reflect: true }),
|
618
|
+
required
|
619
|
+
], RadioGroup.prototype, "label", void 0);
|
620
|
+
__decorate([
|
621
|
+
property({ reflect: true, type: Boolean })
|
622
|
+
], RadioGroup.prototype, "disabled", null);
|
623
|
+
__decorate([
|
624
|
+
property({ attribute: 'hide-label', type: Boolean })
|
625
|
+
], RadioGroup.prototype, "hideLabel", void 0);
|
626
|
+
__decorate([
|
627
|
+
property({ reflect: true, useDefault: true })
|
628
|
+
], RadioGroup.prototype, "name", void 0);
|
629
|
+
__decorate([
|
630
|
+
property({ reflect: true })
|
631
|
+
], RadioGroup.prototype, "orientation", void 0);
|
632
|
+
__decorate([
|
633
|
+
property()
|
634
|
+
], RadioGroup.prototype, "privateSplit", void 0);
|
635
|
+
__decorate([
|
636
|
+
property({ reflect: true })
|
637
|
+
], RadioGroup.prototype, "tooltip", void 0);
|
638
|
+
__decorate([
|
639
|
+
property({ reflect: true, type: Boolean })
|
640
|
+
], RadioGroup.prototype, "required", null);
|
641
|
+
__decorate([
|
642
|
+
property()
|
643
|
+
], RadioGroup.prototype, "value", null);
|
644
|
+
__decorate([
|
645
|
+
property({ reflect: true })
|
646
|
+
], RadioGroup.prototype, "version", void 0);
|
647
|
+
__decorate([
|
648
|
+
state()
|
649
|
+
], RadioGroup.prototype, "isBlurring", void 0);
|
650
|
+
__decorate([
|
651
|
+
state()
|
652
|
+
], RadioGroup.prototype, "isCheckingValidity", void 0);
|
653
|
+
__decorate([
|
654
|
+
state()
|
655
|
+
], RadioGroup.prototype, "isReportValidityOrSubmit", void 0);
|
656
|
+
__decorate([
|
657
|
+
state()
|
658
|
+
], RadioGroup.prototype, "validityMessage", void 0);
|
659
|
+
RadioGroup = __decorate([
|
660
|
+
customElement('glide-core-radio-group'),
|
661
|
+
final
|
662
|
+
], RadioGroup);
|
663
|
+
export default RadioGroup;
|