@momentum-design/components 0.133.4 → 0.133.6
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/browser/index.js +207 -218
- package/dist/browser/index.js.map +3 -3
- package/dist/components/formfieldgroup/formfieldgroup.component.d.ts +2 -3
- package/dist/components/formfieldgroup/formfieldgroup.component.js +13 -16
- package/dist/components/formfieldgroup/formfieldgroup.styles.js +1 -1
- package/dist/components/radio/radio.component.d.ts +27 -16
- package/dist/components/radio/radio.component.js +103 -120
- package/dist/components/radio/radio.styles.js +31 -17
- package/dist/components/radio/radio.types.d.ts +3 -2
- package/dist/components/radiogroup/radiogroup.component.d.ts +0 -2
- package/dist/components/radiogroup/radiogroup.component.js +10 -8
- package/dist/components/tab/tab.component.d.ts +4 -0
- package/dist/components/tab/tab.component.js +5 -4
- package/dist/components/tab/tab.styles.js +1 -2
- package/dist/custom-elements.json +170 -252
- package/dist/react/formfieldgroup/index.d.ts +0 -1
- package/dist/react/formfieldgroup/index.js +0 -1
- package/dist/react/radio/index.d.ts +17 -13
- package/dist/react/radio/index.js +17 -13
- package/dist/react/radiogroup/index.d.ts +0 -2
- package/dist/react/radiogroup/index.js +0 -2
- package/dist/react/tab/index.d.ts +3 -0
- package/dist/react/tab/index.js +3 -0
- package/dist/utils/mixins/DataAriaLabelMixin.d.ts +14 -0
- package/dist/utils/mixins/DataAriaLabelMixin.js +14 -0
- package/package.json +1 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { CSSResult } from 'lit';
|
|
2
2
|
import FormfieldWrapper from '../formfieldwrapper/formfieldwrapper.component';
|
|
3
|
-
declare const FormfieldGroup_base: import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/DataAriaLabelMixin").DataAriaLabelMixinInterface> & typeof FormfieldWrapper;
|
|
4
3
|
/**
|
|
5
4
|
* `mdc-formfieldgroup` component, groups the form field components together.
|
|
6
5
|
* All passed in children will have a gap of 12px (0.75rem) each applied.
|
|
@@ -38,7 +37,6 @@ declare const FormfieldGroup_base: import("../../utils/mixins/index.types").Cons
|
|
|
38
37
|
* @csspart help-text - The helper/validation text element.
|
|
39
38
|
* @csspart helper-icon - The helper/validation icon element that is displayed next to the helper/validation text.
|
|
40
39
|
* @csspart help-text-container - The container for the helper/validation icon and text elements.
|
|
41
|
-
* @csspart container - Formfieldgroup host container
|
|
42
40
|
* @csspart group-header - This contains the label and help text for the group
|
|
43
41
|
*
|
|
44
42
|
* @cssproperty --mdc-label-font-size - Font size for the label text.
|
|
@@ -51,13 +49,14 @@ declare const FormfieldGroup_base: import("../../utils/mixins/index.types").Cons
|
|
|
51
49
|
* @cssproperty --mdc-help-text-color - Color for the help text.
|
|
52
50
|
* @cssproperty --mdc-required-indicator-color - Color for the required indicator text.
|
|
53
51
|
*/
|
|
54
|
-
declare class FormfieldGroup extends
|
|
52
|
+
declare class FormfieldGroup extends FormfieldWrapper {
|
|
55
53
|
/**
|
|
56
54
|
* @internal
|
|
57
55
|
* This is used to set the role of the component as `radiogroup` if this is true and to 'group' if it is false.
|
|
58
56
|
*/
|
|
59
57
|
protected isRadio: boolean;
|
|
60
58
|
connectedCallback(): void;
|
|
59
|
+
update(changedProperties: Map<string, unknown>): void;
|
|
61
60
|
render(): import("lit-html").TemplateResult<1>;
|
|
62
61
|
static styles: Array<CSSResult>;
|
|
63
62
|
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { html } from 'lit';
|
|
2
|
-
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
3
|
-
import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
|
|
4
2
|
import { ROLE } from '../../utils/roles';
|
|
5
3
|
import FormfieldWrapper from '../formfieldwrapper/formfieldwrapper.component';
|
|
6
|
-
import { DEFAULTS as FORMFIELD_DEFAULTS } from '../formfieldwrapper/formfieldwrapper.constants';
|
|
7
4
|
import styles from './formfieldgroup.styles';
|
|
8
5
|
/**
|
|
9
6
|
* `mdc-formfieldgroup` component, groups the form field components together.
|
|
@@ -42,7 +39,6 @@ import styles from './formfieldgroup.styles';
|
|
|
42
39
|
* @csspart help-text - The helper/validation text element.
|
|
43
40
|
* @csspart helper-icon - The helper/validation icon element that is displayed next to the helper/validation text.
|
|
44
41
|
* @csspart help-text-container - The container for the helper/validation icon and text elements.
|
|
45
|
-
* @csspart container - Formfieldgroup host container
|
|
46
42
|
* @csspart group-header - This contains the label and help text for the group
|
|
47
43
|
*
|
|
48
44
|
* @cssproperty --mdc-label-font-size - Font size for the label text.
|
|
@@ -55,7 +51,7 @@ import styles from './formfieldgroup.styles';
|
|
|
55
51
|
* @cssproperty --mdc-help-text-color - Color for the help text.
|
|
56
52
|
* @cssproperty --mdc-required-indicator-color - Color for the required indicator text.
|
|
57
53
|
*/
|
|
58
|
-
class FormfieldGroup extends
|
|
54
|
+
class FormfieldGroup extends FormfieldWrapper {
|
|
59
55
|
constructor() {
|
|
60
56
|
super(...arguments);
|
|
61
57
|
/**
|
|
@@ -65,23 +61,24 @@ class FormfieldGroup extends DataAriaLabelMixin(FormfieldWrapper) {
|
|
|
65
61
|
this.isRadio = false;
|
|
66
62
|
}
|
|
67
63
|
connectedCallback() {
|
|
64
|
+
var _a;
|
|
68
65
|
super.connectedCallback();
|
|
69
66
|
this.shouldRenderLabel = false;
|
|
70
67
|
this.disabled = undefined;
|
|
68
|
+
this.role = this.isRadio ? ROLE.RADIOGROUP : ROLE.GROUP;
|
|
69
|
+
this.ariaDescription = (_a = this.helpText) !== null && _a !== void 0 ? _a : '';
|
|
70
|
+
this.ariaLabel = this.label || '';
|
|
71
|
+
}
|
|
72
|
+
update(changedProperties) {
|
|
73
|
+
super.update(changedProperties);
|
|
74
|
+
if (changedProperties.has('label') && !this.ariaLabel) {
|
|
75
|
+
this.ariaLabel = this.label || '';
|
|
76
|
+
}
|
|
71
77
|
}
|
|
72
78
|
render() {
|
|
73
|
-
var _a;
|
|
74
79
|
return html `
|
|
75
|
-
<div
|
|
76
|
-
|
|
77
|
-
role="${this.isRadio ? ROLE.RADIOGROUP : ROLE.GROUP}"
|
|
78
|
-
aria-labelledby="${FORMFIELD_DEFAULTS.HEADING_ID}"
|
|
79
|
-
aria-describedby="${ifDefined(this.helpText ? FORMFIELD_DEFAULTS.HELPER_TEXT_ID : '')}"
|
|
80
|
-
aria-label="${(_a = this.dataAriaLabel) !== null && _a !== void 0 ? _a : ''}"
|
|
81
|
-
>
|
|
82
|
-
<div part="group-header">${this.renderLabel()} ${this.renderHelperText()}</div>
|
|
83
|
-
<slot></slot>
|
|
84
|
-
</div>
|
|
80
|
+
<div part="group-header">${this.renderLabel()} ${this.renderHelperText()}</div>
|
|
81
|
+
<slot></slot>
|
|
85
82
|
`;
|
|
86
83
|
}
|
|
87
84
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CSSResult, PropertyValueMap, PropertyValues } from 'lit';
|
|
2
2
|
import FormfieldWrapper from '../formfieldwrapper/formfieldwrapper.component';
|
|
3
3
|
import { AssociatedFormControl } from '../../utils/mixins/FormInternalsMixin';
|
|
4
|
-
declare const Radio_base: import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyDownHandledMixin").KeyDownHandledMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyToActionMixin").KeyToActionInterface> & import("../../utils/mixins/index.types").Constructor<import("../../models").Component & import("../../utils/mixins/AutoFocusOnMountMixin").AutoFocusOnMountMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/FormInternalsMixin").FormInternalsMixinInterface> &
|
|
4
|
+
declare const Radio_base: import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyDownHandledMixin").KeyDownHandledMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyToActionMixin").KeyToActionInterface> & import("../../utils/mixins/index.types").Constructor<import("../../models").Component & import("../../utils/mixins/AutoFocusOnMountMixin").AutoFocusOnMountMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/FormInternalsMixin").FormInternalsMixinInterface> & typeof FormfieldWrapper;
|
|
5
5
|
/**
|
|
6
6
|
* The Radio component allows users to select a single option from a group of mutually exclusive choices.
|
|
7
7
|
* Unlike checkboxes which allow multiple selections, radio buttons ensure only one option can be selected
|
|
@@ -11,19 +11,26 @@ declare const Radio_base: import("../../utils/mixins/index.types").Constructor<i
|
|
|
11
11
|
* To create a group of radio buttons, use the `mdc-radiogroup` component or ensure all radio buttons
|
|
12
12
|
* share the same `name` attribute.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* ## Validation
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Radio component support native form validation. But it does not have default validation message.
|
|
17
|
+
* Also, `required` attribute does not render indicator (red asterisk) for the radio component.
|
|
18
|
+
*
|
|
19
|
+
* The recommended way to show validation message for radio groups is to wrap the `mdc-radio` with `mdc-radiogroup`
|
|
20
|
+
* and set the `help-text` of the `mdc-radiogroup` based on its validation state.
|
|
21
|
+
*
|
|
22
|
+
* Alternatively you can also set the `validation-message` attribute of the `mdc-radio`. This message will appear
|
|
23
|
+
* in a native tooltip when the radio is checked and invalid.
|
|
19
24
|
*
|
|
20
25
|
* ## Accessibility
|
|
26
|
+
*
|
|
21
27
|
* - Provide clear labels that describe each option
|
|
22
28
|
* - Use `data-aria-label` when a visual label is not present
|
|
23
29
|
* - Keyboard navigation: Arrow keys to move between options, Space to select, Tab to navigate groups, Enter to submit form
|
|
24
30
|
* - Group related radio buttons using the same `name` attribute or `mdc-radiogroup` component
|
|
25
31
|
*
|
|
26
32
|
* ## Styling
|
|
33
|
+
*
|
|
27
34
|
* Use the `static-radio` part to apply custom styles to the radio visual element.
|
|
28
35
|
* This part exposes the underlying [StaticRadio](?path=/docs/components-decorator-staticradio--docs) component for advanced styling.
|
|
29
36
|
*
|
|
@@ -35,20 +42,16 @@ declare const Radio_base: import("../../utils/mixins/index.types").Constructor<i
|
|
|
35
42
|
*
|
|
36
43
|
* @tagname mdc-radio
|
|
37
44
|
*
|
|
38
|
-
* @event
|
|
45
|
+
* @event input - (React: onInput) Event that gets dispatched when the radio state changes (before the change event).
|
|
46
|
+
* @event change - (React: onChange) Event that gets dispatched when the radio state changes (after the input event).
|
|
39
47
|
* @event focus - (React: onFocus) Event that gets dispatched when the radio receives focus.
|
|
40
48
|
*
|
|
41
49
|
* @csspart label - The label element.
|
|
42
50
|
* @csspart label-text - The container for the label and required indicator elements.
|
|
43
|
-
* @csspart required-indicator - The required indicator element that is displayed next to the label when the `required` property is set to true.
|
|
44
|
-
* @csspart info-icon-btn - The info icon button element that is displayed next to the label when the `toggletip-text` property is set.
|
|
45
|
-
* @csspart label-toggletip - The toggletip element that is displayed when the info icon button is clicked.
|
|
46
|
-
* @csspart help-text - The helper/validation text element.
|
|
47
|
-
* @csspart helper-icon - The helper/validation icon element that is displayed next to the helper/validation text.
|
|
48
|
-
* @csspart help-text-container - The container for the helper/validation icon and text elements.
|
|
49
|
-
* @csspart radio-input - The native radio input element that provides the interactive functionality.
|
|
50
|
-
* @csspart text-container - The container for the label and helper text elements.
|
|
51
51
|
* @csspart static-radio - The staticradio that provides the visual radio appearance.
|
|
52
|
+
*
|
|
53
|
+
* @slot indicator - Slot for the radio indicator element. If not provided, a default styled radio will be rendered.
|
|
54
|
+
* @slot label - Slot for the label of the radio.
|
|
52
55
|
*/
|
|
53
56
|
declare class Radio extends Radio_base implements AssociatedFormControl {
|
|
54
57
|
/**
|
|
@@ -57,6 +60,7 @@ declare class Radio extends Radio_base implements AssociatedFormControl {
|
|
|
57
60
|
* @default false
|
|
58
61
|
*/
|
|
59
62
|
checked: boolean;
|
|
63
|
+
constructor();
|
|
60
64
|
connectedCallback(): void;
|
|
61
65
|
protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
62
66
|
/**
|
|
@@ -92,6 +96,7 @@ declare class Radio extends Radio_base implements AssociatedFormControl {
|
|
|
92
96
|
* Handles the change event on the radio element.
|
|
93
97
|
* Unchecks all other radios in the same group and checks this radio.
|
|
94
98
|
* Dispatches the change event.
|
|
99
|
+
* @param emitClick - A boolean when click event must be emitted before other events
|
|
95
100
|
* @internal
|
|
96
101
|
*/
|
|
97
102
|
private handleChange;
|
|
@@ -104,6 +109,8 @@ declare class Radio extends Radio_base implements AssociatedFormControl {
|
|
|
104
109
|
* @internal
|
|
105
110
|
*/
|
|
106
111
|
private updateRadio;
|
|
112
|
+
private handleClick;
|
|
113
|
+
private emitCheckedChangeEvent;
|
|
107
114
|
/**
|
|
108
115
|
* Handles the keydown event on the radio element.
|
|
109
116
|
* Supports Arrow keys for navigation between radios in the same group, Space for selection, and Enter for form submission.
|
|
@@ -120,8 +127,12 @@ declare class Radio extends Radio_base implements AssociatedFormControl {
|
|
|
120
127
|
*/
|
|
121
128
|
private updateTabIndex;
|
|
122
129
|
update(changedProperties: PropertyValues): void;
|
|
123
|
-
/**
|
|
124
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Updates the aria-label of the radio element based on the label or data-aria-label attribute.
|
|
132
|
+
* @internal
|
|
133
|
+
*/
|
|
134
|
+
private updateAriaLabel;
|
|
135
|
+
setValidity(): void;
|
|
125
136
|
render(): import("lit-html").TemplateResult<1>;
|
|
126
137
|
static styles: Array<CSSResult>;
|
|
127
138
|
}
|
|
@@ -8,13 +8,10 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
/* eslint-disable no-param-reassign */
|
|
11
|
-
import { html
|
|
11
|
+
import { html } from 'lit';
|
|
12
12
|
import { property } from 'lit/decorators.js';
|
|
13
|
-
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
14
13
|
import FormfieldWrapper from '../formfieldwrapper/formfieldwrapper.component';
|
|
15
|
-
import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
|
|
16
14
|
import { FormInternalsMixin } from '../../utils/mixins/FormInternalsMixin';
|
|
17
|
-
import { DEFAULTS as FORMFIELD_DEFAULTS } from '../formfieldwrapper/formfieldwrapper.constants';
|
|
18
15
|
import { ROLE } from '../../utils/roles';
|
|
19
16
|
import { AutoFocusOnMountMixin } from '../../utils/mixins/AutoFocusOnMountMixin';
|
|
20
17
|
import { ACTIONS, KeyToActionMixin, NAV_MODES } from '../../utils/mixins/KeyToActionMixin';
|
|
@@ -29,19 +26,26 @@ import styles from './radio.styles';
|
|
|
29
26
|
* To create a group of radio buttons, use the `mdc-radiogroup` component or ensure all radio buttons
|
|
30
27
|
* share the same `name` attribute.
|
|
31
28
|
*
|
|
32
|
-
*
|
|
29
|
+
* ## Validation
|
|
33
30
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
31
|
+
* Radio component support native form validation. But it does not have default validation message.
|
|
32
|
+
* Also, `required` attribute does not render indicator (red asterisk) for the radio component.
|
|
33
|
+
*
|
|
34
|
+
* The recommended way to show validation message for radio groups is to wrap the `mdc-radio` with `mdc-radiogroup`
|
|
35
|
+
* and set the `help-text` of the `mdc-radiogroup` based on its validation state.
|
|
36
|
+
*
|
|
37
|
+
* Alternatively you can also set the `validation-message` attribute of the `mdc-radio`. This message will appear
|
|
38
|
+
* in a native tooltip when the radio is checked and invalid.
|
|
37
39
|
*
|
|
38
40
|
* ## Accessibility
|
|
41
|
+
*
|
|
39
42
|
* - Provide clear labels that describe each option
|
|
40
43
|
* - Use `data-aria-label` when a visual label is not present
|
|
41
44
|
* - Keyboard navigation: Arrow keys to move between options, Space to select, Tab to navigate groups, Enter to submit form
|
|
42
45
|
* - Group related radio buttons using the same `name` attribute or `mdc-radiogroup` component
|
|
43
46
|
*
|
|
44
47
|
* ## Styling
|
|
48
|
+
*
|
|
45
49
|
* Use the `static-radio` part to apply custom styles to the radio visual element.
|
|
46
50
|
* This part exposes the underlying [StaticRadio](?path=/docs/components-decorator-staticradio--docs) component for advanced styling.
|
|
47
51
|
*
|
|
@@ -53,49 +57,46 @@ import styles from './radio.styles';
|
|
|
53
57
|
*
|
|
54
58
|
* @tagname mdc-radio
|
|
55
59
|
*
|
|
56
|
-
* @event
|
|
60
|
+
* @event input - (React: onInput) Event that gets dispatched when the radio state changes (before the change event).
|
|
61
|
+
* @event change - (React: onChange) Event that gets dispatched when the radio state changes (after the input event).
|
|
57
62
|
* @event focus - (React: onFocus) Event that gets dispatched when the radio receives focus.
|
|
58
63
|
*
|
|
59
64
|
* @csspart label - The label element.
|
|
60
65
|
* @csspart label-text - The container for the label and required indicator elements.
|
|
61
|
-
* @csspart required-indicator - The required indicator element that is displayed next to the label when the `required` property is set to true.
|
|
62
|
-
* @csspart info-icon-btn - The info icon button element that is displayed next to the label when the `toggletip-text` property is set.
|
|
63
|
-
* @csspart label-toggletip - The toggletip element that is displayed when the info icon button is clicked.
|
|
64
|
-
* @csspart help-text - The helper/validation text element.
|
|
65
|
-
* @csspart helper-icon - The helper/validation icon element that is displayed next to the helper/validation text.
|
|
66
|
-
* @csspart help-text-container - The container for the helper/validation icon and text elements.
|
|
67
|
-
* @csspart radio-input - The native radio input element that provides the interactive functionality.
|
|
68
|
-
* @csspart text-container - The container for the label and helper text elements.
|
|
69
66
|
* @csspart static-radio - The staticradio that provides the visual radio appearance.
|
|
67
|
+
*
|
|
68
|
+
* @slot indicator - Slot for the radio indicator element. If not provided, a default styled radio will be rendered.
|
|
69
|
+
* @slot label - Slot for the label of the radio.
|
|
70
70
|
*/
|
|
71
|
-
class Radio extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(
|
|
71
|
+
class Radio extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(FormfieldWrapper)))) {
|
|
72
72
|
constructor() {
|
|
73
|
-
super(
|
|
73
|
+
super();
|
|
74
74
|
/**
|
|
75
75
|
* Determines whether the radio is checked (selected) or unchecked.
|
|
76
76
|
* Within a radio group, only one radio can be checked at a time.
|
|
77
77
|
* @default false
|
|
78
78
|
*/
|
|
79
79
|
this.checked = false;
|
|
80
|
-
|
|
81
|
-
this.
|
|
82
|
-
if (!this.label)
|
|
83
|
-
return nothing;
|
|
84
|
-
return html `<div part="text-container">${this.renderLabel()} ${this.renderHelperText()}</div>`;
|
|
85
|
-
};
|
|
80
|
+
this.addEventListener('click', this.handleClick);
|
|
81
|
+
this.addEventListener('keydown', this.handleKeyDown);
|
|
86
82
|
}
|
|
87
83
|
connectedCallback() {
|
|
88
84
|
super.connectedCallback();
|
|
89
|
-
|
|
85
|
+
this.role = ROLE.RADIO;
|
|
86
|
+
this.shouldRenderLabel = false;
|
|
87
|
+
// Radio should not contain these properties.
|
|
90
88
|
this.helpTextType = undefined;
|
|
89
|
+
this.toggletipPlacement = undefined;
|
|
90
|
+
this.toggletipStrategy = undefined;
|
|
91
|
+
this.updateAriaLabel();
|
|
91
92
|
}
|
|
92
93
|
firstUpdated(_changedProperties) {
|
|
93
94
|
this.updateTabIndex();
|
|
94
95
|
// set the element to auto focus if autoFocusOnMount is set to true
|
|
95
96
|
// before running the super method, so that the AutoFocusOnMountMixin can use it
|
|
96
97
|
// to focus the correct element
|
|
97
|
-
if (this.
|
|
98
|
-
this.elementToAutoFocus = this
|
|
98
|
+
if (this.autoFocusOnMount) {
|
|
99
|
+
this.elementToAutoFocus = this;
|
|
99
100
|
}
|
|
100
101
|
super.firstUpdated(_changedProperties);
|
|
101
102
|
}
|
|
@@ -138,12 +139,6 @@ class Radio extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(F
|
|
|
138
139
|
this.internals.setValidity({});
|
|
139
140
|
}
|
|
140
141
|
else if (this.required && !this.checked) {
|
|
141
|
-
if (this.validationMessage) {
|
|
142
|
-
this.inputElement.setCustomValidity(this.validationMessage);
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
this.inputElement.setCustomValidity('');
|
|
146
|
-
}
|
|
147
142
|
this.setValidity();
|
|
148
143
|
}
|
|
149
144
|
this.updateTabIndex();
|
|
@@ -157,15 +152,9 @@ class Radio extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(F
|
|
|
157
152
|
setGroupValidity(radios, isValid) {
|
|
158
153
|
this.updateComplete
|
|
159
154
|
.then(() => {
|
|
160
|
-
radios.forEach(radio =>
|
|
161
|
-
radio.setComponentValidity(isValid);
|
|
162
|
-
});
|
|
155
|
+
radios.forEach(radio => radio.setComponentValidity(isValid));
|
|
163
156
|
})
|
|
164
|
-
.catch(error => {
|
|
165
|
-
if (this.onerror) {
|
|
166
|
-
this.onerror(error);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
157
|
+
.catch(error => { var _a; return (_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error); });
|
|
169
158
|
}
|
|
170
159
|
/**
|
|
171
160
|
* Updates the form value to reflect the current state of the radio.
|
|
@@ -174,57 +163,43 @@ class Radio extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(F
|
|
|
174
163
|
* @internal
|
|
175
164
|
*/
|
|
176
165
|
setActualFormValue() {
|
|
177
|
-
let actualValue = '';
|
|
178
|
-
if (this.checked) {
|
|
179
|
-
actualValue = !this.value ? 'on' : this.value;
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
actualValue = null;
|
|
183
|
-
}
|
|
184
166
|
const radios = this.getAllRadiosWithinSameGroup();
|
|
185
167
|
if (this.checked) {
|
|
186
168
|
this.setGroupValidity(radios, true);
|
|
169
|
+
this.internals.setFormValue(this.value || 'on');
|
|
187
170
|
}
|
|
188
171
|
else {
|
|
189
172
|
const anyRequired = radios.some(r => r.required);
|
|
190
|
-
const anyChecked =
|
|
173
|
+
const anyChecked = radios.some(r => r.checked);
|
|
191
174
|
const isInvalid = anyRequired && !anyChecked;
|
|
192
175
|
this.setGroupValidity(radios, !isInvalid);
|
|
176
|
+
this.internals.setFormValue(null);
|
|
193
177
|
}
|
|
194
|
-
this.internals.setFormValue(actualValue);
|
|
195
178
|
}
|
|
196
179
|
/**
|
|
197
180
|
* Handles the change event on the radio element.
|
|
198
181
|
* Unchecks all other radios in the same group and checks this radio.
|
|
199
182
|
* Dispatches the change event.
|
|
183
|
+
* @param emitClick - A boolean when click event must be emitted before other events
|
|
200
184
|
* @internal
|
|
201
185
|
*/
|
|
202
|
-
handleChange() {
|
|
203
|
-
|
|
204
|
-
if (this.disabled || this.readonly || this.softDisabled)
|
|
186
|
+
handleChange(emitClick = false) {
|
|
187
|
+
if (this.checked || this.disabled || this.readonly || this.softDisabled)
|
|
205
188
|
return;
|
|
206
189
|
const radios = this.getAllRadiosWithinSameGroup();
|
|
190
|
+
// Uncheck all radios in the same group (name)
|
|
207
191
|
radios.forEach(radio => {
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Uncheck all radios in the same group (name)
|
|
211
|
-
*/
|
|
212
|
-
const radioElement = (_a = radio.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('input');
|
|
213
|
-
if (radioElement) {
|
|
214
|
-
radio.checked = false;
|
|
215
|
-
radioElement.checked = false;
|
|
216
|
-
}
|
|
192
|
+
radio.checked = false;
|
|
217
193
|
});
|
|
218
194
|
this.checked = true;
|
|
219
|
-
|
|
220
|
-
if (
|
|
221
|
-
|
|
195
|
+
// Native radio input emits click, input and change event in this order when
|
|
196
|
+
if (emitClick) {
|
|
197
|
+
super.click();
|
|
222
198
|
}
|
|
223
|
-
this.
|
|
199
|
+
this.emitCheckedChangeEvent();
|
|
224
200
|
}
|
|
225
201
|
click() {
|
|
226
|
-
|
|
227
|
-
this.handleChange();
|
|
202
|
+
this.handleChange(true);
|
|
228
203
|
}
|
|
229
204
|
/**
|
|
230
205
|
* Updates the state of the radio button at the specified index within the enabled radios.
|
|
@@ -234,9 +209,18 @@ class Radio extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(F
|
|
|
234
209
|
* @internal
|
|
235
210
|
*/
|
|
236
211
|
updateRadio(enabledRadios, index) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
212
|
+
const radio = enabledRadios[index];
|
|
213
|
+
radio.focus();
|
|
214
|
+
radio.handleChange();
|
|
215
|
+
}
|
|
216
|
+
handleClick() {
|
|
217
|
+
this.handleChange();
|
|
218
|
+
}
|
|
219
|
+
emitCheckedChangeEvent() {
|
|
220
|
+
if (this.checked) {
|
|
221
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
222
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
|
223
|
+
}
|
|
240
224
|
}
|
|
241
225
|
/**
|
|
242
226
|
* Handles the keydown event on the radio element.
|
|
@@ -256,19 +240,12 @@ class Radio extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(F
|
|
|
256
240
|
const enabledRadios = radios.filter(radio => !radio.disabled);
|
|
257
241
|
const currentIndex = enabledRadios.indexOf(this);
|
|
258
242
|
// Leave navigation between radios to the spatial navigation context if it exists
|
|
259
|
-
if (this.getKeyboardNavMode()
|
|
243
|
+
if (this.getKeyboardNavMode() === NAV_MODES.SPATIAL) {
|
|
260
244
|
if (action === ACTIONS.ENTER) {
|
|
261
245
|
this.updateRadio(enabledRadios, currentIndex);
|
|
262
246
|
this.keyDownEventHandled();
|
|
263
247
|
}
|
|
264
|
-
|
|
265
|
-
radios.forEach(radio => {
|
|
266
|
-
var _a;
|
|
267
|
-
const input = (_a = radio.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('input');
|
|
268
|
-
if (input) {
|
|
269
|
-
input.tabIndex = 0;
|
|
270
|
-
}
|
|
271
|
-
});
|
|
248
|
+
this.updateTabIndex();
|
|
272
249
|
return;
|
|
273
250
|
}
|
|
274
251
|
if (action === ACTIONS.DOWN || action === ACTIONS.RIGHT) {
|
|
@@ -297,60 +274,66 @@ class Radio extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(F
|
|
|
297
274
|
* @internal
|
|
298
275
|
*/
|
|
299
276
|
updateTabIndex() {
|
|
277
|
+
// Leave navigation between radios to the spatial navigation context if it exists
|
|
278
|
+
const isSpatialNavMode = this.getKeyboardNavMode() === NAV_MODES.SPATIAL;
|
|
300
279
|
const radios = this.getAllRadiosWithinSameGroup();
|
|
301
280
|
const checked = radios.find(radio => radio.checked);
|
|
302
281
|
const firstEnabledRadio = radios.find(radio => !radio.disabled);
|
|
303
282
|
radios.forEach(radio => {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
283
|
+
radio.tabIndex = -1;
|
|
284
|
+
if (isSpatialNavMode) {
|
|
285
|
+
radio.tabIndex = 0;
|
|
286
|
+
}
|
|
287
|
+
else if (radio === checked) {
|
|
288
|
+
radio.tabIndex = 0;
|
|
289
|
+
}
|
|
290
|
+
else if (!checked && radio === firstEnabledRadio) {
|
|
291
|
+
radio.tabIndex = 0;
|
|
314
292
|
}
|
|
315
293
|
});
|
|
316
294
|
}
|
|
317
295
|
update(changedProperties) {
|
|
318
296
|
super.update(changedProperties);
|
|
319
297
|
if (changedProperties.has('checked')) {
|
|
298
|
+
this.ariaChecked = this.checked.toString();
|
|
320
299
|
this.setActualFormValue();
|
|
321
300
|
}
|
|
301
|
+
if (changedProperties.has('label')) {
|
|
302
|
+
this.updateAriaLabel();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Updates the aria-label of the radio element based on the label or data-aria-label attribute.
|
|
307
|
+
* @internal
|
|
308
|
+
*/
|
|
309
|
+
updateAriaLabel() {
|
|
310
|
+
if (!this.ariaLabel && this.ariaLabel !== this.label) {
|
|
311
|
+
this.ariaLabel = this.label || '';
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
setValidity() {
|
|
315
|
+
if (this.required && !this.checked && this.validationMessage) {
|
|
316
|
+
this.internals.setValidity({ valueMissing: true, customError: true }, this.validationMessage, this);
|
|
317
|
+
}
|
|
318
|
+
this.internals.setValidity({});
|
|
322
319
|
}
|
|
323
320
|
render() {
|
|
324
|
-
var _a;
|
|
325
321
|
return html `
|
|
326
|
-
<
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
value="${ifDefined(this.value)}"
|
|
342
|
-
?required="${this.required}"
|
|
343
|
-
@change=${this.handleChange}
|
|
344
|
-
@keydown=${this.handleKeyDown}
|
|
345
|
-
?checked=${this.checked}
|
|
346
|
-
?readonly=${this.readonly}
|
|
347
|
-
?disabled=${this.disabled}
|
|
348
|
-
aria-checked="${this.checked}"
|
|
349
|
-
aria-describedby="${ifDefined(this.helpText ? FORMFIELD_DEFAULTS.HELPER_TEXT_ID : '')}"
|
|
350
|
-
aria-label="${(_a = this.dataAriaLabel) !== null && _a !== void 0 ? _a : ''}"
|
|
351
|
-
/>
|
|
352
|
-
</mdc-staticradio>
|
|
353
|
-
${this.renderLabelAndHelperText()}
|
|
322
|
+
<slot name="indicator">
|
|
323
|
+
<mdc-staticradio
|
|
324
|
+
part="radio-indicator"
|
|
325
|
+
role="presentation"
|
|
326
|
+
class="mdc-focus-ring"
|
|
327
|
+
?checked="${this.checked}"
|
|
328
|
+
?disabled="${this.disabled}"
|
|
329
|
+
?readonly="${this.readonly}"
|
|
330
|
+
?soft-disabled="${this.softDisabled}"
|
|
331
|
+
>
|
|
332
|
+
</mdc-staticradio>
|
|
333
|
+
</slot>
|
|
334
|
+
<div part="label-text">
|
|
335
|
+
<slot name="label">${this.renderLabelElement()}</slot>
|
|
336
|
+
</div>
|
|
354
337
|
`;
|
|
355
338
|
}
|
|
356
339
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { css } from 'lit';
|
|
2
|
-
import { hostFitContentStyles, hostFocusRingStyles } from '../../utils/styles';
|
|
2
|
+
import { focusRingBoxShadow, hostFitContentStyles, hostFocusRingStyles } from '../../utils/styles';
|
|
3
3
|
const styles = [
|
|
4
4
|
hostFitContentStyles,
|
|
5
5
|
css `
|
|
@@ -13,22 +13,8 @@ const styles = [
|
|
|
13
13
|
gap: 0.5rem;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
:host::part(radio-
|
|
17
|
-
|
|
18
|
-
opacity: 0;
|
|
19
|
-
margin: 0;
|
|
20
|
-
width: 100%;
|
|
21
|
-
height: 100%;
|
|
22
|
-
cursor: pointer;
|
|
23
|
-
z-index: 1;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
:host::part(text-container) {
|
|
27
|
-
display: flex;
|
|
28
|
-
flex-direction: column;
|
|
29
|
-
justify-content: center;
|
|
30
|
-
gap: 0.25rem;
|
|
31
|
-
flex: 1;
|
|
16
|
+
:host::part(radio-indicator) {
|
|
17
|
+
flex: none;
|
|
32
18
|
}
|
|
33
19
|
|
|
34
20
|
:host::part(label) {
|
|
@@ -37,6 +23,10 @@ const styles = [
|
|
|
37
23
|
white-space: normal;
|
|
38
24
|
}
|
|
39
25
|
|
|
26
|
+
:host::part(label-text) {
|
|
27
|
+
display: contents;
|
|
28
|
+
}
|
|
29
|
+
|
|
40
30
|
:host(:hover)::part(static-radio) {
|
|
41
31
|
--mdc-staticradio-outer-circle-background-color: var(--mds-color-theme-control-inactive-hover);
|
|
42
32
|
}
|
|
@@ -60,6 +50,30 @@ const styles = [
|
|
|
60
50
|
:host([soft-disabled]) {
|
|
61
51
|
pointer-events: none;
|
|
62
52
|
}
|
|
53
|
+
|
|
54
|
+
:host(:focus-within),
|
|
55
|
+
:host(:focus-visible) {
|
|
56
|
+
outline: none;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
:host(:focus-visible)::part(radio-indicator) {
|
|
60
|
+
outline: none;
|
|
61
|
+
}
|
|
62
|
+
:host([disabled]:focus) {
|
|
63
|
+
box-shadow: none;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
:host(:focus-within)::part(radio-indicator) {
|
|
67
|
+
position: relative;
|
|
68
|
+
box-shadow: ${focusRingBoxShadow};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* High Contrast Mode */
|
|
72
|
+
@media (forced-colors: active) {
|
|
73
|
+
:host(:focus-visible)::part(radio-indicator) {
|
|
74
|
+
outline: 0.125rem solid var(--mds-color-theme-focus-default-0);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
63
77
|
`,
|
|
64
78
|
...hostFocusRingStyles(true),
|
|
65
79
|
];
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { OverrideEventTarget, TypedCustomEvent } from
|
|
2
|
-
import type Radio from
|
|
1
|
+
import type { OverrideEventTarget, TypedCustomEvent } from '../../utils/types';
|
|
2
|
+
import type Radio from './radio.component';
|
|
3
3
|
interface Events {
|
|
4
4
|
onChangeEvent: TypedCustomEvent<Radio>;
|
|
5
5
|
onFocusEvent: OverrideEventTarget<FocusEvent, Radio>;
|
|
6
|
+
onInputEvent: OverrideEventTarget<Event, Radio>;
|
|
6
7
|
}
|
|
7
8
|
export type { Events };
|