@momentum-design/components 0.133.13 → 0.133.15

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.
@@ -3,7 +3,10 @@ import type Option from '../option';
3
3
  import { Component } from '../../models';
4
4
  declare const ListBox_base: import("../../utils/mixins/index.types").Constructor<Component & import("../../utils/mixins/ListNavigationMixin").ListNavigationMixinInterface & import("../../utils/mixins/KeyToActionMixin").KeyToActionInterface & import("../../utils/mixins/KeyDownHandledMixin").KeyDownHandledMixinInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/lifecycle/CaptureDestroyEventForChildElement").CaptureDestroyEventForChildElementInterface> & typeof Component;
5
5
  /**
6
- * listbox component presents a list of options and allows a user to select one of them.
6
+ * listbox component presents a list of options and allows a user to select one or more of them.
7
+ *
8
+ * When `multiple` is enabled, clicking/pressing Enter/Space on an option toggles its selection
9
+ * instead of replacing the current selection.
7
10
  *
8
11
  * Notes:
9
12
  * - This is a standalone listbox component. Select has its own mdc-selectlistbox component.
@@ -41,6 +44,11 @@ declare class ListBox extends ListBox_base {
41
44
  * The value attribute is used to represent the last selected option's value in the listbox.
42
45
  */
43
46
  value?: undefined | string;
47
+ /**
48
+ * When true, multiple options can be selected.
49
+ * @default false
50
+ */
51
+ multiple: boolean;
44
52
  /** @internal */
45
53
  selectedOption?: Option | null;
46
54
  /** @internal */
@@ -62,8 +70,26 @@ declare class ListBox extends ListBox_base {
62
70
  private isValidItem;
63
71
  /** @internal */
64
72
  private handleClick;
73
+ /**
74
+ * Toggles the selection state of an option (for multiselect mode).
75
+ * @internal
76
+ */
77
+ private toggleOptionSelection;
65
78
  /** @internal */
66
79
  private getFirstSelectedOption;
80
+ /**
81
+ * Syncs value and selectedOption to reflect the first selected option (when multiple)
82
+ * @internal
83
+ */
84
+ private syncValueToFirstSelected;
85
+ /** @internal */
86
+ private getSelectedValues;
87
+ /**
88
+ * Override to focus the first selected option per W3C APG.
89
+ * This applies to both single-select and multi-select listboxes.
90
+ * @internal
91
+ */
92
+ protected setInitialFocus(): void;
67
93
  /**
68
94
  * Handles the updated lifecycle event.
69
95
  *
@@ -85,7 +111,7 @@ declare class ListBox extends ListBox_base {
85
111
  */
86
112
  private updateSelectedInChildOptions;
87
113
  /**
88
- * Dispatch change event when an option is selected.
114
+ * Dispatch change event when selection changes.
89
115
  */
90
116
  private fireEvents;
91
117
  render(): import("lit-html").TemplateResult<1>;
@@ -18,7 +18,10 @@ import { Component } from '../../models';
18
18
  import { ElementStore } from '../../utils/controllers/ElementStore';
19
19
  import styles from './listbox.styles';
20
20
  /**
21
- * listbox component presents a list of options and allows a user to select one of them.
21
+ * listbox component presents a list of options and allows a user to select one or more of them.
22
+ *
23
+ * When `multiple` is enabled, clicking/pressing Enter/Space on an option toggles its selection
24
+ * instead of replacing the current selection.
22
25
  *
23
26
  * Notes:
24
27
  * - This is a standalone listbox component. Select has its own mdc-selectlistbox component.
@@ -58,6 +61,11 @@ class ListBox extends ListNavigationMixin(CaptureDestroyEventForChildElement(Com
58
61
  * The value attribute is used to represent the last selected option's value in the listbox.
59
62
  */
60
63
  this.value = undefined;
64
+ /**
65
+ * When true, multiple options can be selected.
66
+ * @default false
67
+ */
68
+ this.multiple = false;
61
69
  /** @internal */
62
70
  this.itemsStore = new ElementStore(this, {
63
71
  isValidItem: this.isValidItem,
@@ -85,11 +93,20 @@ class ListBox extends ListNavigationMixin(CaptureDestroyEventForChildElement(Com
85
93
  this.itemsStore.delete(item);
86
94
  break;
87
95
  case 'selected':
88
- // When selection changed on the option
89
- this.setSelectedOption(item, false, false);
96
+ if (this.multiple) {
97
+ this.syncValueToFirstSelected();
98
+ }
99
+ else {
100
+ this.setSelectedOption(item, false, false);
101
+ }
90
102
  break;
91
103
  case 'unselected':
92
- this.handleNoSelection();
104
+ if (this.multiple) {
105
+ this.syncValueToFirstSelected();
106
+ }
107
+ else {
108
+ this.handleNoSelection();
109
+ }
93
110
  break;
94
111
  default:
95
112
  break;
@@ -117,21 +134,82 @@ class ListBox extends ListNavigationMixin(CaptureDestroyEventForChildElement(Com
117
134
  /** @internal */
118
135
  handleClick(event) {
119
136
  const target = event.target;
120
- if (this.isValidItem(target)) {
121
- this.setSelectedOption(target);
137
+ if (!this.isValidItem(target))
138
+ return;
139
+ const option = target;
140
+ if (this.multiple) {
141
+ this.toggleOptionSelection(option);
142
+ }
143
+ else {
144
+ this.setSelectedOption(option);
122
145
  }
123
146
  }
147
+ /**
148
+ * Toggles the selection state of an option (for multiselect mode).
149
+ * @internal
150
+ */
151
+ toggleOptionSelection(option) {
152
+ if (option.disabled || option.softDisabled)
153
+ return;
154
+ const isCurrentlySelected = option.hasAttribute('selected');
155
+ option.toggleAttribute('selected', !isCurrentlySelected);
156
+ this.syncValueToFirstSelected();
157
+ this.fireEvents();
158
+ }
124
159
  /** @internal */
125
160
  getFirstSelectedOption() {
126
161
  return this.itemsStore.items.find(el => el.matches('[selected]'));
127
162
  }
163
+ /**
164
+ * Syncs value and selectedOption to reflect the first selected option (when multiple)
165
+ * @internal
166
+ */
167
+ syncValueToFirstSelected() {
168
+ const firstSelected = this.getFirstSelectedOption();
169
+ this.value = firstSelected === null || firstSelected === void 0 ? void 0 : firstSelected.value;
170
+ this.selectedOption = firstSelected !== null && firstSelected !== void 0 ? firstSelected : null;
171
+ }
172
+ /** @internal */
173
+ getSelectedValues() {
174
+ return this.itemsStore.items.reduce((acc, option) => {
175
+ const { value } = option;
176
+ if (option.hasAttribute('selected') && value !== undefined) {
177
+ acc.push(value);
178
+ }
179
+ return acc;
180
+ }, []);
181
+ }
182
+ /**
183
+ * Override to focus the first selected option per W3C APG.
184
+ * This applies to both single-select and multi-select listboxes.
185
+ * @internal
186
+ */
187
+ setInitialFocus() {
188
+ const firstSelected = this.getFirstSelectedOption();
189
+ if (firstSelected) {
190
+ const index = this.itemsStore.items.indexOf(firstSelected);
191
+ if (index !== -1) {
192
+ this.resetTabIndexAndSetFocus(index, undefined, false);
193
+ return;
194
+ }
195
+ }
196
+ super.setInitialFocus();
197
+ }
128
198
  /**
129
199
  * Handles the updated lifecycle event.
130
200
  *
131
201
  * @param changedProperties - The properties that have changed since the last update.
132
202
  */
133
203
  updated(changedProperties) {
134
- if (changedProperties.has('value')) {
204
+ if (changedProperties.has('multiple')) {
205
+ if (this.multiple) {
206
+ this.setAttribute('aria-multiselectable', 'true');
207
+ }
208
+ else {
209
+ this.removeAttribute('aria-multiselectable');
210
+ }
211
+ }
212
+ if (changedProperties.has('value') && !this.multiple) {
135
213
  const newSelectedOption = this.itemsStore.items.find(option => option.value === this.value);
136
214
  if (newSelectedOption) {
137
215
  this.setSelectedOption(newSelectedOption, false);
@@ -171,12 +249,17 @@ class ListBox extends ListNavigationMixin(CaptureDestroyEventForChildElement(Com
171
249
  selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.toggleAttribute('selected', true);
172
250
  }
173
251
  /**
174
- * Dispatch change event when an option is selected.
252
+ * Dispatch change event when selection changes.
175
253
  */
176
254
  fireEvents() {
177
- if (this.selectedOption) {
178
- this.dispatchEvent(new Event('change', { composed: true, bubbles: true }));
179
- }
255
+ this.dispatchEvent(new CustomEvent('change', {
256
+ detail: {
257
+ value: this.value,
258
+ selectedValues: this.getSelectedValues(),
259
+ },
260
+ composed: true,
261
+ bubbles: true,
262
+ }));
180
263
  }
181
264
  render() {
182
265
  return html `<slot part="container"></slot>`;
@@ -191,6 +274,10 @@ __decorate([
191
274
  property({ type: String, reflect: true }),
192
275
  __metadata("design:type", Object)
193
276
  ], ListBox.prototype, "value", void 0);
277
+ __decorate([
278
+ property({ type: Boolean, reflect: true }),
279
+ __metadata("design:type", Object)
280
+ ], ListBox.prototype, "multiple", void 0);
194
281
  __decorate([
195
282
  state(),
196
283
  __metadata("design:type", Object)
@@ -1,6 +1,10 @@
1
- import { OverrideEventTarget } from '../../utils/types';
1
+ import type { TypedCustomEvent } from '../../utils/types';
2
2
  import type ListBox from './listbox.component';
3
- export type ListBoxChangeEvent = OverrideEventTarget<Event, ListBox>;
3
+ export interface ListBoxChangeEventDetail {
4
+ value: string | undefined;
5
+ selectedValues: string[];
6
+ }
7
+ export type ListBoxChangeEvent = TypedCustomEvent<ListBox, ListBoxChangeEventDetail>;
4
8
  interface Events {
5
9
  onChangeEvent: ListBoxChangeEvent;
6
10
  }
@@ -31,9 +31,14 @@ declare const Radio_base: import("../../utils/mixins/index.types").Constructor<i
31
31
  *
32
32
  * ## Styling
33
33
  *
34
- * Use the `static-radio` part to apply custom styles to the radio visual element.
34
+ * Use the `radio-indicator` part to apply custom styles to the radio visual element.
35
35
  * This part exposes the underlying [StaticRadio](?path=/docs/components-decorator-staticradio--docs) component for advanced styling.
36
36
  *
37
+ * The `indicator` slot allows replacing the default radio circle with a custom element.
38
+ * When a custom indicator is slotted, the component automatically adds the `mdc-focus-ring`
39
+ * class to the host element. This shifts the focus ring from the default static radio to the
40
+ * entire host element, ensuring keyboard focus remains visible.
41
+ *
37
42
  * @dependency mdc-button
38
43
  * @dependency mdc-icon
39
44
  * @dependency mdc-text
@@ -48,7 +53,7 @@ declare const Radio_base: import("../../utils/mixins/index.types").Constructor<i
48
53
  *
49
54
  * @csspart label - The label element.
50
55
  * @csspart label-text - The container for the label and required indicator elements.
51
- * @csspart static-radio - The staticradio that provides the visual radio appearance.
56
+ * @csspart radio-indicator - The staticradio that provides the visual radio appearance.
52
57
  *
53
58
  * @slot indicator - Slot for the radio indicator element. If not provided, a default styled radio will be rendered.
54
59
  * @slot label - Slot for the label of the radio.
@@ -133,6 +138,15 @@ declare class Radio extends Radio_base implements AssociatedFormControl {
133
138
  */
134
139
  private updateAriaLabel;
135
140
  setValidity(): void;
141
+ /**
142
+ * Handles the slotchange event on the indicator slot.
143
+ * Adds the `mdc-focus-ring` class to the host when a custom indicator
144
+ * is slotted, so the focus ring shifts from the default static radio
145
+ * to the entire host element.
146
+ *
147
+ * @internal
148
+ */
149
+ private handleIndicatorSlotChange;
136
150
  render(): import("lit-html").TemplateResult<1>;
137
151
  static styles: Array<CSSResult>;
138
152
  }
@@ -46,9 +46,14 @@ import styles from './radio.styles';
46
46
  *
47
47
  * ## Styling
48
48
  *
49
- * Use the `static-radio` part to apply custom styles to the radio visual element.
49
+ * Use the `radio-indicator` part to apply custom styles to the radio visual element.
50
50
  * This part exposes the underlying [StaticRadio](?path=/docs/components-decorator-staticradio--docs) component for advanced styling.
51
51
  *
52
+ * The `indicator` slot allows replacing the default radio circle with a custom element.
53
+ * When a custom indicator is slotted, the component automatically adds the `mdc-focus-ring`
54
+ * class to the host element. This shifts the focus ring from the default static radio to the
55
+ * entire host element, ensuring keyboard focus remains visible.
56
+ *
52
57
  * @dependency mdc-button
53
58
  * @dependency mdc-icon
54
59
  * @dependency mdc-text
@@ -63,7 +68,7 @@ import styles from './radio.styles';
63
68
  *
64
69
  * @csspart label - The label element.
65
70
  * @csspart label-text - The container for the label and required indicator elements.
66
- * @csspart static-radio - The staticradio that provides the visual radio appearance.
71
+ * @csspart radio-indicator - The staticradio that provides the visual radio appearance.
67
72
  *
68
73
  * @slot indicator - Slot for the radio indicator element. If not provided, a default styled radio will be rendered.
69
74
  * @slot label - Slot for the label of the radio.
@@ -317,9 +322,22 @@ class Radio extends KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(F
317
322
  }
318
323
  this.internals.setValidity({});
319
324
  }
325
+ /**
326
+ * Handles the slotchange event on the indicator slot.
327
+ * Adds the `mdc-focus-ring` class to the host when a custom indicator
328
+ * is slotted, so the focus ring shifts from the default static radio
329
+ * to the entire host element.
330
+ *
331
+ * @internal
332
+ */
333
+ handleIndicatorSlotChange(event) {
334
+ const slot = event.target;
335
+ const assignedNodes = slot.assignedNodes({ flatten: true }).filter(node => node.nodeType === Node.ELEMENT_NODE);
336
+ this.classList.toggle('mdc-focus-ring', assignedNodes.length > 0);
337
+ }
320
338
  render() {
321
339
  return html `
322
- <slot name="indicator">
340
+ <slot name="indicator" @slotchange=${this.handleIndicatorSlotChange}>
323
341
  <mdc-staticradio
324
342
  part="radio-indicator"
325
343
  role="presentation"
@@ -1,5 +1,5 @@
1
1
  import { css } from 'lit';
2
- import { focusRingBoxShadow, hostFitContentStyles, hostFocusRingStyles } from '../../utils/styles';
2
+ import { hostFitContentStyles, hostFocusRingStyles } from '../../utils/styles';
3
3
  const styles = [
4
4
  hostFitContentStyles,
5
5
  css `
@@ -56,25 +56,13 @@ const styles = [
56
56
  outline: none;
57
57
  }
58
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
59
  /* High Contrast Mode */
72
60
  @media (forced-colors: active) {
73
- :host(:focus-visible)::part(radio-indicator) {
61
+ :host(:focus-within)::part(radio-indicator) {
74
62
  outline: 0.125rem solid var(--mds-color-theme-focus-default-0);
75
63
  }
76
64
  }
77
65
  `,
78
- ...hostFocusRingStyles(true),
66
+ ...hostFocusRingStyles(true, 'parent-to-child'),
79
67
  ];
80
68
  export default styles;
@@ -23217,7 +23217,7 @@
23217
23217
  "declarations": [
23218
23218
  {
23219
23219
  "kind": "class",
23220
- "description": "listbox component presents a list of options and allows a user to select one of them.\n\nNotes:\n- This is a standalone listbox component. Select has its own mdc-selectlistbox component.\n- this component has name and value attributes and also emits change event,\n but it is not a form control (yet).",
23220
+ "description": "listbox component presents a list of options and allows a user to select one or more of them.\n\nWhen `multiple` is enabled, clicking/pressing Enter/Space on an option toggles its selection\ninstead of replacing the current selection.\n\nNotes:\n- This is a standalone listbox component. Select has its own mdc-selectlistbox component.\n- this component has name and value attributes and also emits change event,\n but it is not a form control (yet).",
23221
23221
  "name": "ListBox",
23222
23222
  "cssProperties": [
23223
23223
  {
@@ -23247,7 +23247,7 @@
23247
23247
  "text": "void"
23248
23248
  }
23249
23249
  },
23250
- "description": "Dispatch change event when an option is selected."
23250
+ "description": "Dispatch change event when selection changes."
23251
23251
  },
23252
23252
  {
23253
23253
  "kind": "method",
@@ -23273,6 +23273,17 @@
23273
23273
  "module": "utils/mixins/ListNavigationMixin.js"
23274
23274
  }
23275
23275
  },
23276
+ {
23277
+ "kind": "field",
23278
+ "name": "multiple",
23279
+ "type": {
23280
+ "text": "boolean"
23281
+ },
23282
+ "default": "false",
23283
+ "description": "When true, multiple options can be selected.",
23284
+ "attribute": "multiple",
23285
+ "reflects": true
23286
+ },
23276
23287
  {
23277
23288
  "kind": "field",
23278
23289
  "name": "name",
@@ -23436,7 +23447,7 @@
23436
23447
  {
23437
23448
  "name": "change",
23438
23449
  "type": {
23439
- "text": "Event"
23450
+ "text": "CustomEvent"
23440
23451
  },
23441
23452
  "description": "(React: onChange) This event is emitted when the selected item changed",
23442
23453
  "reactName": "onChange"
@@ -23460,6 +23471,15 @@
23460
23471
  "default": "undefined",
23461
23472
  "description": "The value attribute is used to represent the last selected option's value in the listbox.",
23462
23473
  "fieldName": "value"
23474
+ },
23475
+ {
23476
+ "name": "multiple",
23477
+ "type": {
23478
+ "text": "boolean"
23479
+ },
23480
+ "default": "false",
23481
+ "description": "When true, multiple options can be selected.",
23482
+ "fieldName": "multiple"
23463
23483
  }
23464
23484
  ],
23465
23485
  "mixins": [
@@ -23477,7 +23497,7 @@
23477
23497
  "module": "/src/models"
23478
23498
  },
23479
23499
  "tagName": "mdc-listbox",
23480
- "jsDoc": "/**\n * listbox component presents a list of options and allows a user to select one of them.\n *\n * Notes:\n * - This is a standalone listbox component. Select has its own mdc-selectlistbox component.\n * - this component has name and value attributes and also emits change event,\n * but it is not a form control (yet).\n *\n * @dependency mdc-list\n * @dependency mdc-icon\n * @dependency mdc-text\n * @dependency mdc-option\n * @dependency mdc-optgroup\n *\n * @tagname mdc-listbox\n *\n * @cssproperty --mdc-listbox-max-height - max height of the listbox\n *\n * @slot default - This is a default/unnamed slot, where options and optgroups are placed\n *\n * @csspart container - The container of the listbox\n *\n * @event change - (React: onChange) This event is emitted when the selected item changed\n */",
23500
+ "jsDoc": "/**\n * listbox component presents a list of options and allows a user to select one or more of them.\n *\n * When `multiple` is enabled, clicking/pressing Enter/Space on an option toggles its selection\n * instead of replacing the current selection.\n *\n * Notes:\n * - This is a standalone listbox component. Select has its own mdc-selectlistbox component.\n * - this component has name and value attributes and also emits change event,\n * but it is not a form control (yet).\n *\n * @dependency mdc-list\n * @dependency mdc-icon\n * @dependency mdc-text\n * @dependency mdc-option\n * @dependency mdc-optgroup\n *\n * @tagname mdc-listbox\n *\n * @cssproperty --mdc-listbox-max-height - max height of the listbox\n *\n * @slot default - This is a default/unnamed slot, where options and optgroups are placed\n *\n * @csspart container - The container of the listbox\n *\n * @event change - (React: onChange) This event is emitted when the selected item changed\n */",
23481
23501
  "customElement": true
23482
23502
  }
23483
23503
  ],
@@ -36486,7 +36506,7 @@
36486
36506
  "declarations": [
36487
36507
  {
36488
36508
  "kind": "class",
36489
- "description": "The Radio component allows users to select a single option from a group of mutually exclusive choices.\nUnlike checkboxes which allow multiple selections, radio buttons ensure only one option can be selected\nat a time within the same group. These are commonly used in forms, surveys, and settings where users\nneed to make a single selection from multiple options.\n\nTo create a group of radio buttons, use the `mdc-radiogroup` component or ensure all radio buttons\nshare the same `name` attribute.\n\n## Validation\n\nRadio component support native form validation. But it does not have default validation message.\nAlso, `required` attribute does not render indicator (red asterisk) for the radio component.\n\nThe recommended way to show validation message for radio groups is to wrap the `mdc-radio` with `mdc-radiogroup`\nand set the `help-text` of the `mdc-radiogroup` based on its validation state.\n\nAlternatively you can also set the `validation-message` attribute of the `mdc-radio`. This message will appear\nin a native tooltip when the radio is checked and invalid.\n\n## Accessibility\n\n- Provide clear labels that describe each option\n- Use `data-aria-label` when a visual label is not present\n- Keyboard navigation: Arrow keys to move between options, Space to select, Tab to navigate groups, Enter to submit form\n- Group related radio buttons using the same `name` attribute or `mdc-radiogroup` component\n\n## Styling\n\nUse the `static-radio` part to apply custom styles to the radio visual element.\nThis part exposes the underlying [StaticRadio](?path=/docs/components-decorator-staticradio--docs) component for advanced styling.",
36509
+ "description": "The Radio component allows users to select a single option from a group of mutually exclusive choices.\nUnlike checkboxes which allow multiple selections, radio buttons ensure only one option can be selected\nat a time within the same group. These are commonly used in forms, surveys, and settings where users\nneed to make a single selection from multiple options.\n\nTo create a group of radio buttons, use the `mdc-radiogroup` component or ensure all radio buttons\nshare the same `name` attribute.\n\n## Validation\n\nRadio component support native form validation. But it does not have default validation message.\nAlso, `required` attribute does not render indicator (red asterisk) for the radio component.\n\nThe recommended way to show validation message for radio groups is to wrap the `mdc-radio` with `mdc-radiogroup`\nand set the `help-text` of the `mdc-radiogroup` based on its validation state.\n\nAlternatively you can also set the `validation-message` attribute of the `mdc-radio`. This message will appear\nin a native tooltip when the radio is checked and invalid.\n\n## Accessibility\n\n- Provide clear labels that describe each option\n- Use `data-aria-label` when a visual label is not present\n- Keyboard navigation: Arrow keys to move between options, Space to select, Tab to navigate groups, Enter to submit form\n- Group related radio buttons using the same `name` attribute or `mdc-radiogroup` component\n\n## Styling\n\nUse the `radio-indicator` part to apply custom styles to the radio visual element.\nThis part exposes the underlying [StaticRadio](?path=/docs/components-decorator-staticradio--docs) component for advanced styling.\n\nThe `indicator` slot allows replacing the default radio circle with a custom element.\nWhen a custom indicator is slotted, the component automatically adds the `mdc-focus-ring`\nclass to the host element. This shifts the focus ring from the default static radio to the\nentire host element, ensuring keyboard focus remains visible.",
36490
36510
  "name": "Radio",
36491
36511
  "cssParts": [
36492
36512
  {
@@ -36507,7 +36527,7 @@
36507
36527
  },
36508
36528
  {
36509
36529
  "description": "The staticradio that provides the visual radio appearance.",
36510
- "name": "static-radio"
36530
+ "name": "radio-indicator"
36511
36531
  },
36512
36532
  {
36513
36533
  "description": "The required indicator element that is displayed next to the label when the `required` property is set to true.",
@@ -37231,7 +37251,7 @@
37231
37251
  "module": "/src/components/formfieldwrapper/formfieldwrapper.component"
37232
37252
  },
37233
37253
  "tagName": "mdc-radio",
37234
- "jsDoc": "/**\n * The Radio component allows users to select a single option from a group of mutually exclusive choices.\n * Unlike checkboxes which allow multiple selections, radio buttons ensure only one option can be selected\n * at a time within the same group. These are commonly used in forms, surveys, and settings where users\n * need to make a single selection from multiple options.\n *\n * To create a group of radio buttons, use the `mdc-radiogroup` component or ensure all radio buttons\n * share the same `name` attribute.\n *\n * ## Validation\n *\n * Radio component support native form validation. But it does not have default validation message.\n * Also, `required` attribute does not render indicator (red asterisk) for the radio component.\n *\n * The recommended way to show validation message for radio groups is to wrap the `mdc-radio` with `mdc-radiogroup`\n * and set the `help-text` of the `mdc-radiogroup` based on its validation state.\n *\n * Alternatively you can also set the `validation-message` attribute of the `mdc-radio`. This message will appear\n * in a native tooltip when the radio is checked and invalid.\n *\n * ## Accessibility\n *\n * - Provide clear labels that describe each option\n * - Use `data-aria-label` when a visual label is not present\n * - Keyboard navigation: Arrow keys to move between options, Space to select, Tab to navigate groups, Enter to submit form\n * - Group related radio buttons using the same `name` attribute or `mdc-radiogroup` component\n *\n * ## Styling\n *\n * Use the `static-radio` part to apply custom styles to the radio visual element.\n * This part exposes the underlying [StaticRadio](?path=/docs/components-decorator-staticradio--docs) component for advanced styling.\n *\n * @dependency mdc-button\n * @dependency mdc-icon\n * @dependency mdc-text\n * @dependency mdc-staticradio\n * @dependency mdc-toggletip\n *\n * @tagname mdc-radio\n *\n * @event input - (React: onInput) Event that gets dispatched when the radio state changes (before the change event).\n * @event change - (React: onChange) Event that gets dispatched when the radio state changes (after the input event).\n * @event focus - (React: onFocus) Event that gets dispatched when the radio receives focus.\n *\n * @csspart label - The label element.\n * @csspart label-text - The container for the label and required indicator elements.\n * @csspart static-radio - The staticradio that provides the visual radio appearance.\n *\n * @slot indicator - Slot for the radio indicator element. If not provided, a default styled radio will be rendered.\n * @slot label - Slot for the label of the radio.\n */",
37254
+ "jsDoc": "/**\n * The Radio component allows users to select a single option from a group of mutually exclusive choices.\n * Unlike checkboxes which allow multiple selections, radio buttons ensure only one option can be selected\n * at a time within the same group. These are commonly used in forms, surveys, and settings where users\n * need to make a single selection from multiple options.\n *\n * To create a group of radio buttons, use the `mdc-radiogroup` component or ensure all radio buttons\n * share the same `name` attribute.\n *\n * ## Validation\n *\n * Radio component support native form validation. But it does not have default validation message.\n * Also, `required` attribute does not render indicator (red asterisk) for the radio component.\n *\n * The recommended way to show validation message for radio groups is to wrap the `mdc-radio` with `mdc-radiogroup`\n * and set the `help-text` of the `mdc-radiogroup` based on its validation state.\n *\n * Alternatively you can also set the `validation-message` attribute of the `mdc-radio`. This message will appear\n * in a native tooltip when the radio is checked and invalid.\n *\n * ## Accessibility\n *\n * - Provide clear labels that describe each option\n * - Use `data-aria-label` when a visual label is not present\n * - Keyboard navigation: Arrow keys to move between options, Space to select, Tab to navigate groups, Enter to submit form\n * - Group related radio buttons using the same `name` attribute or `mdc-radiogroup` component\n *\n * ## Styling\n *\n * Use the `radio-indicator` part to apply custom styles to the radio visual element.\n * This part exposes the underlying [StaticRadio](?path=/docs/components-decorator-staticradio--docs) component for advanced styling.\n *\n * The `indicator` slot allows replacing the default radio circle with a custom element.\n * When a custom indicator is slotted, the component automatically adds the `mdc-focus-ring`\n * class to the host element. This shifts the focus ring from the default static radio to the\n * entire host element, ensuring keyboard focus remains visible.\n *\n * @dependency mdc-button\n * @dependency mdc-icon\n * @dependency mdc-text\n * @dependency mdc-staticradio\n * @dependency mdc-toggletip\n *\n * @tagname mdc-radio\n *\n * @event input - (React: onInput) Event that gets dispatched when the radio state changes (before the change event).\n * @event change - (React: onChange) Event that gets dispatched when the radio state changes (after the input event).\n * @event focus - (React: onFocus) Event that gets dispatched when the radio receives focus.\n *\n * @csspart label - The label element.\n * @csspart label-text - The container for the label and required indicator elements.\n * @csspart radio-indicator - The staticradio that provides the visual radio appearance.\n *\n * @slot indicator - Slot for the radio indicator element. If not provided, a default styled radio will be rendered.\n * @slot label - Slot for the label of the radio.\n */",
37235
37255
  "customElement": true,
37236
37256
  "cssProperties": [
37237
37257
  {
package/dist/index.d.ts CHANGED
@@ -118,6 +118,7 @@ import { inMemoryCache, webAPIAssetsCache } from './utils/assets-cache';
118
118
  import type { TimePickerChangeEvent, TimePickerInputEvent } from './components/timepicker/timepicker.types';
119
119
  import type { DatePickerChangeEvent, DatePickerInputEvent } from './components/datepicker/datepicker.types';
120
120
  import type { CalendarDateSelectedEvent, CalendarMonthChangedEvent } from './components/calendar/calendar.types';
121
+ import type { ListBoxChangeEvent } from './components/listbox/listbox.types';
121
122
  export { Accordion, AccordionButton, AccordionGroup, AlertChip, Animation, AnnouncementDialog, Appheader, Avatar, AvatarButton, Badge, Brandvisual, Bullet, Button, ButtonGroup, ButtonLink, Calendar, Card, CardButton, CardCheckbox, CardRadio, Checkbox, Chip, Coachmark, ControlTypeProvider, DatePicker, Dialog, Divider, FilterChip, FormfieldGroup, Icon, IconProvider, Illustration, IllustrationProvider, Input, InputChip, Link, LinkButton, Linksimple, List, Listheader, ListItem, Marker, MenuBar, MenuItem, MenuItemCheckbox, MenuItemRadio, MenuPopover, MenuSection, NavMenuItem, OptGroup, Option, Password, Popover, Presence, Progressbar, Progressspinner, Radio, RadioGroup, ResponsiveSettingsProvider, ScreenreaderAnnouncer, Searchfield, Searchpopover, Select, SelectListbox, SideNavigation, Skeleton, Spinner, StaticChip, StaticCheckbox, StaticRadio, StaticToggle, Stepper, StepperConnector, StepperItem, Tab, TabList, Text, Textarea, TimePicker, ThemeProvider, Toast, Toggle, Typewriter, ToggleTip, Tooltip, VirtualizedList, Combobox, Slider, ListBox, Banner, Buttonsimple, Verticaltablist, };
122
- export type { AvatarSize, BadgeType, ChipColorType, ButtonColor, ButtonVariant, IconButtonSize, MenuPopoverActionEvent, MenuPopoverChangeEvent, MenuSectionChangeEvent, PillButtonSize, PopoverPlacement, PresenceType, SkeletonVariant, SelectChangeEvent, SelectInputEvent, SpinnerSize, SpinnerVariant, SliderChangeEvent, TextType, TypewriterType, InputInputEvent, InputChangeEvent, InputFocusEvent, InputBlurEvent, InputClearEvent, VirtualizedListScrollEvent, TablistChangeEvent, TextareaInputEvent, TextareaChangeEvent, TextareaFocusEvent, TextareaBlurEvent, TextareaLimitExceededEvent, ToggleOnChangeEvent, CheckboxOnChangeEvent, LinkButtonSize, TimePickerChangeEvent, TimePickerInputEvent, DatePickerChangeEvent, DatePickerInputEvent, CalendarDateSelectedEvent, CalendarMonthChangedEvent, VerticaltablistChangeEvent, };
123
+ export type { AvatarSize, BadgeType, ChipColorType, ButtonColor, ButtonVariant, IconButtonSize, MenuPopoverActionEvent, MenuPopoverChangeEvent, MenuSectionChangeEvent, PillButtonSize, PopoverPlacement, PresenceType, SkeletonVariant, SelectChangeEvent, SelectInputEvent, SpinnerSize, SpinnerVariant, SliderChangeEvent, TextType, TypewriterType, InputInputEvent, InputChangeEvent, InputFocusEvent, InputBlurEvent, InputClearEvent, VirtualizedListScrollEvent, TablistChangeEvent, TextareaInputEvent, TextareaChangeEvent, TextareaFocusEvent, TextareaBlurEvent, TextareaLimitExceededEvent, ToggleOnChangeEvent, CheckboxOnChangeEvent, LinkButtonSize, TimePickerChangeEvent, TimePickerInputEvent, DatePickerChangeEvent, DatePickerInputEvent, CalendarDateSelectedEvent, CalendarMonthChangedEvent, VerticaltablistChangeEvent, ListBoxChangeEvent, };
123
124
  export { BUTTON_COLORS, BUTTON_VARIANTS, ICON_BUTTON_SIZES, inMemoryCache, PILL_BUTTON_SIZES, SKELETON_VARIANTS, webAPIAssetsCache, };
@@ -2,7 +2,10 @@ import { type EventName } from '@lit/react';
2
2
  import Component from '../../components/listbox';
3
3
  import type { Events } from '../../components/listbox/listbox.types';
4
4
  /**
5
- * listbox component presents a list of options and allows a user to select one of them.
5
+ * listbox component presents a list of options and allows a user to select one or more of them.
6
+ *
7
+ * When `multiple` is enabled, clicking/pressing Enter/Space on an option toggles its selection
8
+ * instead of replacing the current selection.
6
9
  *
7
10
  * Notes:
8
11
  * - This is a standalone listbox component. Select has its own mdc-selectlistbox component.
@@ -3,7 +3,10 @@ import { createComponent } from '@lit/react';
3
3
  import Component from '../../components/listbox';
4
4
  import { TAG_NAME } from '../../components/listbox/listbox.constants';
5
5
  /**
6
- * listbox component presents a list of options and allows a user to select one of them.
6
+ * listbox component presents a list of options and allows a user to select one or more of them.
7
+ *
8
+ * When `multiple` is enabled, clicking/pressing Enter/Space on an option toggles its selection
9
+ * instead of replacing the current selection.
7
10
  *
8
11
  * Notes:
9
12
  * - This is a standalone listbox component. Select has its own mdc-selectlistbox component.
@@ -30,9 +30,14 @@ import type { Events } from '../../components/radio/radio.types';
30
30
  *
31
31
  * ## Styling
32
32
  *
33
- * Use the `static-radio` part to apply custom styles to the radio visual element.
33
+ * Use the `radio-indicator` part to apply custom styles to the radio visual element.
34
34
  * This part exposes the underlying [StaticRadio](?path=/docs/components-decorator-staticradio--docs) component for advanced styling.
35
35
  *
36
+ * The `indicator` slot allows replacing the default radio circle with a custom element.
37
+ * When a custom indicator is slotted, the component automatically adds the `mdc-focus-ring`
38
+ * class to the host element. This shifts the focus ring from the default static radio to the
39
+ * entire host element, ensuring keyboard focus remains visible.
40
+ *
36
41
  * @dependency mdc-button
37
42
  * @dependency mdc-icon
38
43
  * @dependency mdc-text
@@ -47,7 +52,7 @@ import type { Events } from '../../components/radio/radio.types';
47
52
  *
48
53
  * @csspart label - The label element.
49
54
  * @csspart label-text - The container for the label and required indicator elements.
50
- * @csspart static-radio - The staticradio that provides the visual radio appearance.
55
+ * @csspart radio-indicator - The staticradio that provides the visual radio appearance.
51
56
  *
52
57
  * @slot indicator - Slot for the radio indicator element. If not provided, a default styled radio will be rendered.
53
58
  * @slot label - Slot for the label of the radio.
@@ -31,9 +31,14 @@ import { TAG_NAME } from '../../components/radio/radio.constants';
31
31
  *
32
32
  * ## Styling
33
33
  *
34
- * Use the `static-radio` part to apply custom styles to the radio visual element.
34
+ * Use the `radio-indicator` part to apply custom styles to the radio visual element.
35
35
  * This part exposes the underlying [StaticRadio](?path=/docs/components-decorator-staticradio--docs) component for advanced styling.
36
36
  *
37
+ * The `indicator` slot allows replacing the default radio circle with a custom element.
38
+ * When a custom indicator is slotted, the component automatically adds the `mdc-focus-ring`
39
+ * class to the host element. This shifts the focus ring from the default static radio to the
40
+ * entire host element, ensuring keyboard focus remains visible.
41
+ *
37
42
  * @dependency mdc-button
38
43
  * @dependency mdc-icon
39
44
  * @dependency mdc-text
@@ -48,7 +53,7 @@ import { TAG_NAME } from '../../components/radio/radio.constants';
48
53
  *
49
54
  * @csspart label - The label element.
50
55
  * @csspart label-text - The container for the label and required indicator elements.
51
- * @csspart static-radio - The staticradio that provides the visual radio appearance.
56
+ * @csspart radio-indicator - The staticradio that provides the visual radio appearance.
52
57
  *
53
58
  * @slot indicator - Slot for the radio indicator element. If not provided, a default styled radio will be rendered.
54
59
  * @slot label - Slot for the label of the radio.
@@ -1,5 +1,6 @@
1
+ type FocusRingClassMode = 'focus-on-child' | 'parent-to-child';
1
2
  declare const hostFitContentStyles: import("lit").CSSResult;
2
3
  declare const baseHostStyleVariables: import("lit").CSSResult;
3
4
  declare const focusRingBoxShadow: import("lit").CSSResult;
4
- declare const hostFocusRingStyles: (applyFocusRingOnClass?: boolean) => import("lit").CSSResult[];
5
+ declare const hostFocusRingStyles: (applyFocusRingOnClass?: boolean, classMode?: FocusRingClassMode) => import("lit").CSSResult[];
5
6
  export { hostFitContentStyles, hostFocusRingStyles, baseHostStyleVariables, focusRingBoxShadow };
@@ -26,8 +26,9 @@ const focusRingBoxShadow = css `0 0 0 var(--mdc-focus-ring-inner-width) var(--md
26
26
  0 0 0 var(--mdc-focus-ring-middle-width) var(--mdc-focus-ring-middle-color),
27
27
  0 0 0 var(--mdc-focus-ring-outer-width) var(--mdc-focus-ring-outer-color)
28
28
  `;
29
- const hostFocusRingStyles = (applyFocusRingOnClass = false) => {
30
- if (applyFocusRingOnClass) {
29
+ const hostFocusRingStyles = (applyFocusRingOnClass = false, classMode = 'focus-on-child') => {
30
+ if (applyFocusRingOnClass && classMode === 'focus-on-child') {
31
+ // the child is focused, the child gets the visual focus ring:
31
32
  return [
32
33
  baseHostStyleVariables,
33
34
  css `
@@ -38,6 +39,7 @@ const hostFocusRingStyles = (applyFocusRingOnClass = false) => {
38
39
  box-shadow: none;
39
40
  }
40
41
  /* Add focus ring to parent when child is focused. The parent element must have class name mdc-focus-ring */
42
+ /* .mdc-focus-ring:focus-visible, */
41
43
  .mdc-focus-ring:focus-within {
42
44
  position: relative;
43
45
  box-shadow: ${focusRingBoxShadow};
@@ -57,6 +59,31 @@ const hostFocusRingStyles = (applyFocusRingOnClass = false) => {
57
59
  `,
58
60
  ];
59
61
  }
62
+ if (applyFocusRingOnClass && classMode === 'parent-to-child') {
63
+ // the parent is focused, the child gets the visual focus ring:
64
+ return [
65
+ baseHostStyleVariables,
66
+ css `
67
+ :host([disabled]:focus) {
68
+ box-shadow: none;
69
+ }
70
+ :host(.mdc-focus-ring:focus-visible),
71
+ :host(:focus-visible) .mdc-focus-ring {
72
+ outline: none;
73
+ position: relative;
74
+ box-shadow: ${focusRingBoxShadow};
75
+ }
76
+ /* High Contrast Mode */
77
+ @media (forced-colors: active) {
78
+ :host(.mdc-focus-ring:focus-visible),
79
+ :host(:focus-visible) .mdc-focus-ring {
80
+ outline: 0.125rem solid var(--mds-color-theme-focus-default-0);
81
+ }
82
+ }
83
+ `,
84
+ ];
85
+ }
86
+ // the parent is focused, the parent gets the visual focus ring:
60
87
  return [
61
88
  baseHostStyleVariables,
62
89
  css `
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@momentum-design/components",
3
3
  "packageManager": "yarn@3.2.4",
4
- "version": "0.133.13",
4
+ "version": "0.133.15",
5
5
  "engines": {
6
6
  "node": ">=20.0.0",
7
7
  "npm": ">=8.0.0"