@momentum-design/components 0.133.13 → 0.133.14

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
  }
@@ -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
  ],
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.
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.14",
5
5
  "engines": {
6
6
  "node": ">=20.0.0",
7
7
  "npm": ">=8.0.0"