@tylertech/forge 3.10.4 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/custom-elements.json +343 -141
  2. package/dist/app-bar/forge-app-bar.css +11 -5
  3. package/dist/button/forge-button.css +12 -6
  4. package/dist/checkbox/forge-checkbox.css +21 -17
  5. package/dist/chips/forge-chips.css +10 -4
  6. package/dist/field/forge-field.css +7 -5
  7. package/dist/floating-action-button/forge-floating-action-button.css +10 -6
  8. package/dist/forge.css +6 -1
  9. package/dist/icon-button/forge-icon-button.css +10 -4
  10. package/dist/lib.js +22 -22
  11. package/dist/lib.js.map +4 -4
  12. package/dist/list/forge-list.css +57 -3
  13. package/dist/radio/forge-radio.css +20 -12
  14. package/dist/skip-link/forge-skip-link.css +36 -32
  15. package/dist/switch/forge-switch.css +7 -5
  16. package/dist/table/forge-table.css +6 -1
  17. package/dist/vscode.html-custom-data.json +53 -43
  18. package/esm/accordion/accordion.d.ts +2 -0
  19. package/esm/accordion/accordion.js +2 -0
  20. package/esm/app-bar/app-bar/app-bar-adapter.js +2 -2
  21. package/esm/app-bar/app-bar/app-bar.d.ts +1 -1
  22. package/esm/app-bar/app-bar/app-bar.js +1 -1
  23. package/esm/app-bar/help-button/app-bar-help-button.d.ts +1 -1
  24. package/esm/app-bar/help-button/app-bar-help-button.js +1 -1
  25. package/esm/app-bar/menu-button/app-bar-menu-button.d.ts +1 -1
  26. package/esm/app-bar/menu-button/app-bar-menu-button.js +1 -1
  27. package/esm/app-bar/notification-button/app-bar-notification-button.d.ts +2 -0
  28. package/esm/app-bar/notification-button/app-bar-notification-button.js +2 -0
  29. package/esm/app-bar/profile-button/app-bar-profile-button-adapter.d.ts +2 -1
  30. package/esm/app-bar/profile-button/app-bar-profile-button-adapter.js +2 -0
  31. package/esm/app-bar/profile-button/app-bar-profile-button.d.ts +2 -0
  32. package/esm/app-bar/profile-button/app-bar-profile-button.js +2 -0
  33. package/esm/app-bar/search/app-bar-search.d.ts +2 -0
  34. package/esm/app-bar/search/app-bar-search.js +2 -0
  35. package/esm/autocomplete/autocomplete.d.ts +2 -0
  36. package/esm/autocomplete/autocomplete.js +2 -0
  37. package/esm/backdrop/backdrop.d.ts +2 -0
  38. package/esm/backdrop/backdrop.js +2 -0
  39. package/esm/badge/badge.d.ts +2 -0
  40. package/esm/badge/badge.js +2 -0
  41. package/esm/button/base/base-button-adapter.js +2 -2
  42. package/esm/button-area/button-area-adapter.js +2 -2
  43. package/esm/button-toggle/button-toggle/button-toggle-adapter.js +2 -2
  44. package/esm/button-toggle/button-toggle/button-toggle.d.ts +2 -0
  45. package/esm/button-toggle/button-toggle/button-toggle.js +2 -0
  46. package/esm/button-toggle/button-toggle-group/button-toggle-group.d.ts +1 -1
  47. package/esm/button-toggle/button-toggle-group/button-toggle-group.js +1 -1
  48. package/esm/calendar/calendar-menu/calendar-menu.js +1 -1
  49. package/esm/calendar/calendar.d.ts +2 -0
  50. package/esm/calendar/calendar.js +3 -1
  51. package/esm/card/card.d.ts +2 -0
  52. package/esm/card/card.js +2 -0
  53. package/esm/chip-field/chip-field.d.ts +2 -0
  54. package/esm/chip-field/chip-field.js +2 -0
  55. package/esm/chips/chip/chip-adapter.js +2 -2
  56. package/esm/chips/chip/chip.d.ts +2 -0
  57. package/esm/chips/chip/chip.js +2 -0
  58. package/esm/color-picker/color-picker.d.ts +2 -0
  59. package/esm/color-picker/color-picker.js +3 -1
  60. package/esm/core/mixins/interactions/moveable/with-moveable.js +5 -7
  61. package/esm/core/utils/a11y-utils.js +17 -0
  62. package/esm/core/utils/utils.d.ts +16 -0
  63. package/esm/core/utils/utils.js +75 -2
  64. package/esm/date-picker/date-picker.d.ts +2 -0
  65. package/esm/date-picker/date-picker.js +2 -0
  66. package/esm/date-range-picker/date-range-picker.d.ts +2 -0
  67. package/esm/date-range-picker/date-range-picker.js +2 -0
  68. package/esm/deprecated/button/deprecated-button.js +3 -3
  69. package/esm/deprecated/icon-button/deprecated-icon-button.js +2 -2
  70. package/esm/dialog/dialog-adapter.d.ts +6 -0
  71. package/esm/dialog/dialog-adapter.js +16 -0
  72. package/esm/dialog/dialog-core.js +4 -0
  73. package/esm/drawer/drawer/drawer.d.ts +2 -0
  74. package/esm/drawer/drawer/drawer.js +3 -1
  75. package/esm/drawer/mini-drawer/mini-drawer.d.ts +2 -0
  76. package/esm/drawer/mini-drawer/mini-drawer.js +3 -1
  77. package/esm/drawer/modal-drawer/modal-drawer.d.ts +2 -0
  78. package/esm/drawer/modal-drawer/modal-drawer.js +3 -1
  79. package/esm/expansion-panel/expansion-panel-adapter.js +1 -1
  80. package/esm/expansion-panel/expansion-panel-core.js +3 -7
  81. package/esm/field/field-adapter.js +2 -2
  82. package/esm/field/field-core.d.ts +3 -3
  83. package/esm/focus-indicator/focus-indicator-constants.d.ts +1 -11
  84. package/esm/focus-indicator/focus-indicator-constants.js +2 -17
  85. package/esm/focus-indicator/focus-indicator.d.ts +52 -25
  86. package/esm/focus-indicator/focus-indicator.js +137 -61
  87. package/esm/focus-indicator/index.d.ts +0 -2
  88. package/esm/focus-indicator/index.js +0 -2
  89. package/esm/icon-button/icon-button.d.ts +2 -0
  90. package/esm/icon-button/icon-button.js +2 -0
  91. package/esm/keyboard-shortcut/keyboard-shortcut.d.ts +2 -0
  92. package/esm/keyboard-shortcut/keyboard-shortcut.js +2 -0
  93. package/esm/list/list/list.js +1 -1
  94. package/esm/list/list-item/list-item-constants.js +1 -1
  95. package/esm/list/list-item/list-item-core.d.ts +2 -0
  96. package/esm/list/list-item/list-item-core.js +29 -6
  97. package/esm/list/list-item/list-item.d.ts +1 -0
  98. package/esm/list/list-item/list-item.js +2 -1
  99. package/esm/list-dropdown/list-dropdown-constants.d.ts +6 -0
  100. package/esm/list-dropdown/list-dropdown-constants.js +6 -1
  101. package/esm/list-dropdown/list-dropdown-utils.js +28 -1
  102. package/esm/menu/menu-core.js +2 -2
  103. package/esm/menu/menu.d.ts +2 -0
  104. package/esm/menu/menu.js +2 -0
  105. package/esm/page-state/page-state.d.ts +2 -1
  106. package/esm/page-state/page-state.js +2 -1
  107. package/esm/paginator/paginator.d.ts +2 -0
  108. package/esm/paginator/paginator.js +2 -0
  109. package/esm/popover/popover.js +1 -1
  110. package/esm/profile-card/profile-card.d.ts +2 -0
  111. package/esm/profile-card/profile-card.js +2 -0
  112. package/esm/select/core/base-select-constants.d.ts +4 -0
  113. package/esm/select/core/base-select-core.d.ts +22 -2
  114. package/esm/select/core/base-select-core.js +217 -40
  115. package/esm/select/option/option.d.ts +2 -0
  116. package/esm/select/option/option.js +2 -0
  117. package/esm/select/option-group/option-group.d.ts +2 -0
  118. package/esm/select/option-group/option-group.js +2 -0
  119. package/esm/select/select/select-constants.d.ts +5 -0
  120. package/esm/select/select/select-constants.js +5 -2
  121. package/esm/select/select/select.d.ts +14 -2
  122. package/esm/select/select/select.js +22 -1
  123. package/esm/select/select-dropdown/select-dropdown.d.ts +2 -0
  124. package/esm/select/select-dropdown/select-dropdown.js +2 -0
  125. package/esm/split-button/split-button.js +1 -1
  126. package/esm/split-view/split-view/split-view.d.ts +2 -0
  127. package/esm/split-view/split-view/split-view.js +2 -0
  128. package/esm/split-view/split-view-panel/split-view-panel.d.ts +2 -0
  129. package/esm/split-view/split-view-panel/split-view-panel.js +3 -1
  130. package/esm/stepper/step/step.d.ts +2 -0
  131. package/esm/stepper/step/step.js +3 -1
  132. package/esm/stepper/stepper/stepper.d.ts +2 -0
  133. package/esm/stepper/stepper/stepper.js +2 -0
  134. package/esm/table/table-utils.js +3 -0
  135. package/esm/table/table.d.ts +2 -0
  136. package/esm/table/table.js +4 -1
  137. package/esm/tabs/tab/tab-adapter.js +2 -2
  138. package/esm/tabs/tab/tab.d.ts +2 -0
  139. package/esm/tabs/tab/tab.js +2 -0
  140. package/esm/time-picker/time-picker.d.ts +2 -0
  141. package/esm/time-picker/time-picker.js +2 -0
  142. package/esm/toast/toast-core.js +1 -0
  143. package/esm/tooltip/tooltip-adapter.d.ts +6 -0
  144. package/esm/tooltip/tooltip-adapter.js +9 -0
  145. package/esm/tooltip/tooltip-constants.d.ts +1 -0
  146. package/esm/tooltip/tooltip-constants.js +2 -1
  147. package/esm/tooltip/tooltip-core.d.ts +20 -0
  148. package/esm/tooltip/tooltip-core.js +96 -2
  149. package/esm/tooltip/tooltip.js +1 -1
  150. package/esm/view-switcher/view/view.d.ts +2 -0
  151. package/esm/view-switcher/view/view.js +2 -0
  152. package/esm/view-switcher/view-switcher.d.ts +2 -0
  153. package/esm/view-switcher/view-switcher.js +2 -0
  154. package/package.json +4 -4
  155. package/sass/focus-indicator/focus-indicator.scss +1 -1
  156. package/sass/icon-button/forge-icon-button.scss +3 -3
  157. package/sass/list/forge-list.scss +6 -6
  158. package/sass/table/_core.scss +13 -1
  159. package/sass/tooltip/_core.scss +0 -2
  160. package/esm/focus-indicator/focus-indicator-adapter.d.ts +0 -29
  161. package/esm/focus-indicator/focus-indicator-adapter.js +0 -37
  162. package/esm/focus-indicator/focus-indicator-core.d.ts +0 -48
  163. package/esm/focus-indicator/focus-indicator-core.js +0 -129
@@ -6,6 +6,7 @@
6
6
  import { isDefined, isDeepEqual, randomChars } from '@tylertech/forge-core';
7
7
  import { BASE_SELECT_CONSTANTS } from './base-select-constants';
8
8
  import { isSelectOptionType, SelectOptionType } from './select-utils';
9
+ import { LIST_DROPDOWN_CONSTANTS } from '../../list-dropdown/list-dropdown-constants';
9
10
  import { ListDropdownAwareCore } from '../../list-dropdown/list-dropdown-aware-core';
10
11
  export class BaseSelectCore extends ListDropdownAwareCore {
11
12
  constructor(_adapter) {
@@ -15,6 +16,7 @@ export class BaseSelectCore extends ListDropdownAwareCore {
15
16
  this._value = [];
16
17
  this._multiple = false;
17
18
  this._open = false;
19
+ this._showSelectAll = false;
18
20
  this._selectedValues = [];
19
21
  this._selectedLabels = [];
20
22
  this._selectedIndexes = [];
@@ -30,6 +32,51 @@ export class BaseSelectCore extends ListDropdownAwareCore {
30
32
  this._identifier = randomChars();
31
33
  }
32
34
  _onFocus(evt) { }
35
+ _onSelectAll() {
36
+ if (!this._multiple) {
37
+ return;
38
+ }
39
+ const currentlyAllSelected = this._selectedValues.length === this._nonDividerOptions.length;
40
+ const willSelectAll = !currentlyAllSelected;
41
+ // Emit the select all event
42
+ const newValue = willSelectAll ? this._nonDividerOptions.map(o => o.value) : [];
43
+ const cancelled = !this._adapter.emitHostEvent('forge-select-all', {
44
+ value: newValue,
45
+ isAllSelected: willSelectAll
46
+ }, true, true);
47
+ if (!cancelled) {
48
+ if (willSelectAll) {
49
+ this.selectAll();
50
+ }
51
+ else {
52
+ this.deselectAll();
53
+ }
54
+ // Emit the standard change event
55
+ const changeData = this._multiple ? [...this._selectedValues] : this._selectedValues[0];
56
+ this._adapter.emitHostEvent(BASE_SELECT_CONSTANTS.events.CHANGE, changeData, true, true);
57
+ // Update the dropdown to reflect the new selection state
58
+ if (this._open) {
59
+ // Preserve the currently active option index before updating selections
60
+ const activeOptionIndex = this._adapter.getActiveOptionIndex();
61
+ this._adapter.patchSelectedValues(this._selectedValues);
62
+ // Update the select all checkbox to reflect its new state
63
+ this._updateSelectAllState();
64
+ // Restore the active option to preserve highlighting
65
+ if (activeOptionIndex >= 0) {
66
+ this._adapter.highlightActiveOption(activeOptionIndex);
67
+ }
68
+ }
69
+ }
70
+ }
71
+ /** Updates the select all checkbox state based on current selections */
72
+ _updateSelectAllState() {
73
+ if (!this._showSelectAll || !this._multiple || !this._open) {
74
+ return;
75
+ }
76
+ const allSelected = this._selectedValues.length === this._nonDividerOptions.length;
77
+ // The select all option is always at dropdown index 0
78
+ this._adapter.toggleOptionMultiple(0, allSelected);
79
+ }
33
80
  initialize() {
34
81
  if (this._optionListenerDestructor) {
35
82
  this._optionListenerDestructor();
@@ -80,6 +127,23 @@ export class BaseSelectCore extends ListDropdownAwareCore {
80
127
  get _nonDividerOptions() {
81
128
  return this._flatOptions.filter(o => !o.divider);
82
129
  }
130
+ /** Adjusts an index from list-dropdown coordinate system to select coordinate system */
131
+ _adjustIndexFromDropdown(dropdownIndex) {
132
+ // If select all is shown and enabled, the first option (index 0) in the dropdown is select all
133
+ // So we need to subtract 1 from the dropdown index to get the actual option index
134
+ if (this._showSelectAll && this._multiple && dropdownIndex > 0) {
135
+ return dropdownIndex - 1;
136
+ }
137
+ return dropdownIndex;
138
+ }
139
+ /** Adjusts an index from select coordinate system to list-dropdown coordinate system */
140
+ _adjustIndexToDropdown(selectIndex) {
141
+ // If select all is shown and enabled, we need to add 1 to account for the select all option
142
+ if (this._showSelectAll && this._multiple) {
143
+ return selectIndex + 1;
144
+ }
145
+ return selectIndex;
146
+ }
83
147
  _initializeValue() {
84
148
  const options = (this._options.length && this._options) || this._adapter.getOptions();
85
149
  if (isDefined(this._value) && options.length) {
@@ -136,8 +200,15 @@ export class BaseSelectCore extends ListDropdownAwareCore {
136
200
  optionLimit: this._optionLimit,
137
201
  headerBuilder: this._popupHeaderBuilder,
138
202
  footerBuilder: this._popupFooterBuilder,
203
+ showSelectAll: this._showSelectAll && this._multiple,
204
+ selectAllLabel: this._selectAllLabel,
139
205
  closeCallback: () => this._closeDropdown(),
140
206
  selectCallback: (value) => {
207
+ // Handle select all option
208
+ if (value === LIST_DROPDOWN_CONSTANTS.selectAllOption.VALUE) {
209
+ this._onSelectAll();
210
+ return;
211
+ }
141
212
  const flatOptions = this._flatOptions;
142
213
  const option = flatOptions.find(o => o.value === value);
143
214
  if (option) {
@@ -148,6 +219,8 @@ export class BaseSelectCore extends ListDropdownAwareCore {
148
219
  };
149
220
  this._adapter.open(config);
150
221
  this._adapter.setDismissListener(this._dismissListener);
222
+ // Ensure select all state is synchronized after opening dropdown
223
+ this._updateSelectAllState();
151
224
  }
152
225
  /**
153
226
  * Closes the dropdown.
@@ -208,7 +281,18 @@ export class BaseSelectCore extends ListDropdownAwareCore {
208
281
  // If we're in multiselect mode, we need to toggle the selected option
209
282
  if (this._multiple) {
210
283
  const isSelected = this._selectedIndexes.includes(optionIndex);
211
- this._adapter.toggleOptionMultiple(optionIndex, isSelected);
284
+ const dropdownIndex = this._adjustIndexToDropdown(optionIndex);
285
+ this._adapter.toggleOptionMultiple(dropdownIndex, isSelected);
286
+ // Update select all checkbox state if it's visible
287
+ if (this._showSelectAll && this._open) {
288
+ // Preserve the currently active option index before updating select all state
289
+ const activeOptionIndex = this._adapter.getActiveOptionIndex();
290
+ this._updateSelectAllState();
291
+ // Restore the active option to preserve highlighting
292
+ if (activeOptionIndex >= 0) {
293
+ this._adapter.highlightActiveOption(activeOptionIndex);
294
+ }
295
+ }
212
296
  }
213
297
  this._applySelection();
214
298
  };
@@ -240,9 +324,17 @@ export class BaseSelectCore extends ListDropdownAwareCore {
240
324
  return !cancelled;
241
325
  }
242
326
  _selectActiveOption() {
243
- const activeOptionIndex = this._adapter.getActiveOptionIndex();
244
- if (activeOptionIndex >= 0 && this._nonDividerOptions[activeOptionIndex]) {
245
- this._onSelect(this._nonDividerOptions[activeOptionIndex], activeOptionIndex);
327
+ const activeDropdownIndex = this._adapter.getActiveOptionIndex();
328
+ if (activeDropdownIndex >= 0) {
329
+ // Check if the active option is the select all option
330
+ if (this._showSelectAll && this._multiple && activeDropdownIndex === 0) {
331
+ this._onSelectAll();
332
+ return;
333
+ }
334
+ const adjustedIndex = this._adjustIndexFromDropdown(activeDropdownIndex);
335
+ if (this._nonDividerOptions[adjustedIndex]) {
336
+ this._onSelect(this._nonDividerOptions[adjustedIndex], adjustedIndex);
337
+ }
246
338
  }
247
339
  }
248
340
  _tryUpdateDropdownPosition() {
@@ -366,39 +458,49 @@ export class BaseSelectCore extends ListDropdownAwareCore {
366
458
  if (this._flatOptions.length === 0) {
367
459
  return;
368
460
  }
369
- let optionIndex = 0;
461
+ let dropdownIndex = 0;
370
462
  if (this._open) {
371
- optionIndex = this._adapter.getActiveOptionIndex();
372
- if (optionIndex === -1) {
373
- optionIndex = this._getFirstSelectedOptionIndex();
463
+ dropdownIndex = this._adapter.getActiveOptionIndex();
464
+ if (dropdownIndex === -1) {
465
+ // No option is currently active, so activate the first/last highlightable option based on direction
466
+ if (isArrowUp) {
467
+ dropdownIndex = this._getLastHighlightableDropdownIndex();
468
+ }
469
+ else {
470
+ dropdownIndex = this._getFirstHighlightableDropdownIndex();
471
+ }
472
+ }
473
+ else {
474
+ // An option is already active, move to the previous/next one
475
+ if (isArrowUp) {
476
+ dropdownIndex = this._getPreviousHighlightableDropdownIndex(dropdownIndex);
477
+ }
478
+ else {
479
+ dropdownIndex = this._getNextHighlightableDropdownIndex(dropdownIndex);
480
+ }
374
481
  }
375
482
  }
376
483
  else {
377
- optionIndex = this._getFirstSelectedOptionIndex();
378
- }
379
- if (isArrowUp) {
380
- optionIndex = this._getPreviousHighlightableOptionIndex(optionIndex, this._nonDividerOptions);
381
- }
382
- else {
383
- optionIndex = this._getNextHighlightableOptionIndex(optionIndex, this._nonDividerOptions);
484
+ const firstSelectedIndex = this._getFirstSelectedOptionIndex();
485
+ dropdownIndex = firstSelectedIndex >= 0 ? this._adjustIndexToDropdown(firstSelectedIndex) : 0;
384
486
  }
385
- this._adapter.highlightActiveOption(optionIndex);
487
+ this._adapter.highlightActiveOption(dropdownIndex);
386
488
  }
387
489
  else if (isHomeKey) {
388
490
  if (this._open) {
389
491
  evt.preventDefault();
390
- this._adapter.highlightActiveOption(this._nonDividerOptions.findIndex(o => !o.disabled));
492
+ const firstDropdownIndex = this._getFirstHighlightableDropdownIndex();
493
+ if (firstDropdownIndex >= 0) {
494
+ this._adapter.highlightActiveOption(firstDropdownIndex);
495
+ }
391
496
  }
392
497
  }
393
498
  else if (isEndKey) {
394
499
  if (this._open) {
395
500
  evt.preventDefault();
396
- const options = this._nonDividerOptions;
397
- for (let i = options.length - 1; i >= 0; i--) {
398
- if (!options[i].disabled) {
399
- this._adapter.highlightActiveOption(i);
400
- break;
401
- }
501
+ const lastDropdownIndex = this._getLastHighlightableDropdownIndex();
502
+ if (lastDropdownIndex >= 0) {
503
+ this._adapter.highlightActiveOption(lastDropdownIndex);
402
504
  }
403
505
  }
404
506
  }
@@ -409,31 +511,92 @@ export class BaseSelectCore extends ListDropdownAwareCore {
409
511
  _getFirstSelectedOptionIndex() {
410
512
  return this._nonDividerOptions.findIndex(option => this._selectedValues.includes(option.value));
411
513
  }
412
- _getPreviousHighlightableOptionIndex(startIndex, options) {
514
+ /** Navigation methods that work in dropdown coordinate system (includes select all when enabled) */
515
+ _getPreviousHighlightableDropdownIndex(startIndex) {
516
+ if (!this._adapter.popupElement) {
517
+ return startIndex;
518
+ }
519
+ const listItems = this._adapter.popupElement.querySelectorAll('forge-list-item');
520
+ const totalItems = listItems.length;
521
+ if (totalItems === 0) {
522
+ return startIndex;
523
+ }
413
524
  let index = startIndex;
414
- if (index <= 0) {
415
- index = options.length - 1;
525
+ do {
526
+ if (index <= 0) {
527
+ index = totalItems - 1;
528
+ }
529
+ else {
530
+ index--;
531
+ }
532
+ const listItem = listItems[index];
533
+ if (listItem && !this._isDropdownOptionDisabled(listItem)) {
534
+ return index;
535
+ }
536
+ } while (index !== startIndex);
537
+ return startIndex;
538
+ }
539
+ _getNextHighlightableDropdownIndex(startIndex) {
540
+ if (!this._adapter.popupElement) {
541
+ return startIndex;
416
542
  }
417
- else {
418
- index--;
543
+ const listItems = this._adapter.popupElement.querySelectorAll('forge-list-item');
544
+ const totalItems = listItems.length;
545
+ if (totalItems === 0) {
546
+ return startIndex;
419
547
  }
420
- if (options[index].disabled) {
421
- return this._getPreviousHighlightableOptionIndex(index, options);
548
+ let index = startIndex;
549
+ do {
550
+ if (index === totalItems - 1) {
551
+ index = 0;
552
+ }
553
+ else {
554
+ index++;
555
+ }
556
+ const listItem = listItems[index];
557
+ if (listItem && !this._isDropdownOptionDisabled(listItem)) {
558
+ return index;
559
+ }
560
+ } while (index !== startIndex);
561
+ return startIndex;
562
+ }
563
+ _getFirstHighlightableDropdownIndex() {
564
+ if (!this._adapter.popupElement) {
565
+ return 0;
566
+ }
567
+ const listItems = this._adapter.popupElement.querySelectorAll('forge-list-item');
568
+ for (let i = 0; i < listItems.length; i++) {
569
+ const listItem = listItems[i];
570
+ if (listItem && !this._isDropdownOptionDisabled(listItem)) {
571
+ return i;
572
+ }
422
573
  }
423
- return index;
574
+ return 0;
424
575
  }
425
- _getNextHighlightableOptionIndex(startIndex, options) {
426
- let index = startIndex;
427
- if (index === options.length - 1) {
428
- index = 0;
576
+ _getLastHighlightableDropdownIndex() {
577
+ if (!this._adapter.popupElement) {
578
+ return 0;
429
579
  }
430
- else {
431
- index++;
580
+ const listItems = this._adapter.popupElement.querySelectorAll('forge-list-item');
581
+ for (let i = listItems.length - 1; i >= 0; i--) {
582
+ const listItem = listItems[i];
583
+ if (listItem && !this._isDropdownOptionDisabled(listItem)) {
584
+ return i;
585
+ }
586
+ }
587
+ return 0;
588
+ }
589
+ _isDropdownOptionDisabled(listItem) {
590
+ // Check if it's a divider (dividers are not selectable)
591
+ if (listItem.querySelector && listItem.querySelector('forge-divider')) {
592
+ return true;
432
593
  }
433
- if (options[index].disabled) {
434
- return this._getNextHighlightableOptionIndex(index, options);
594
+ // Check if the button inside is disabled
595
+ const button = listItem.querySelector('button');
596
+ if (button && button.disabled) {
597
+ return true;
435
598
  }
436
- return index;
599
+ return false;
437
600
  }
438
601
  _filter(key) {
439
602
  // This allows for typing numbers and/or characters while the select is focused to auto-select the closest match
@@ -603,4 +766,18 @@ export class BaseSelectCore extends ListDropdownAwareCore {
603
766
  set beforeValueChange(value) {
604
767
  this._beforeValueChange = value;
605
768
  }
769
+ /** Gets/sets whether to show the select all option when in multiple mode. */
770
+ get showSelectAll() {
771
+ return this._showSelectAll;
772
+ }
773
+ set showSelectAll(value) {
774
+ this._showSelectAll = value;
775
+ }
776
+ /** Gets/sets the label for the select all option. */
777
+ get selectAllLabel() {
778
+ return this._selectAllLabel;
779
+ }
780
+ set selectAllLabel(value) {
781
+ this._selectAllLabel = value;
782
+ }
606
783
  }
@@ -15,6 +15,8 @@ declare global {
15
15
  }
16
16
  /**
17
17
  * @tag forge-option
18
+ *
19
+ * @summary Options represent individual selectable items within select components and menus.
18
20
  */
19
21
  export declare class OptionComponent extends BaseComponent implements IOptionComponent {
20
22
  static get observedAttributes(): string[];
@@ -11,6 +11,8 @@ import { OPTION_CONSTANTS } from './option-constants';
11
11
  import { OptionCore } from './option-core';
12
12
  /**
13
13
  * @tag forge-option
14
+ *
15
+ * @summary Options represent individual selectable items within select components and menus.
14
16
  */
15
17
  let OptionComponent = class OptionComponent extends BaseComponent {
16
18
  static get observedAttributes() {
@@ -16,6 +16,8 @@ declare global {
16
16
  }
17
17
  /**
18
18
  * @tag forge-option-group
19
+ *
20
+ * @summary Groups related options together with an optional label within select components.
19
21
  */
20
22
  export declare class OptionGroupComponent extends BaseComponent implements IOptionGroupComponent {
21
23
  static get observedAttributes(): string[];
@@ -9,6 +9,8 @@ import { BaseComponent } from '../../core/base/base-component';
9
9
  import { OPTION_GROUP_CONSTANTS } from './option-group-constants';
10
10
  /**
11
11
  * @tag forge-option-group
12
+ *
13
+ * @summary Groups related options together with an optional label within select components.
12
14
  */
13
15
  let OptionGroupComponent = class OptionGroupComponent extends BaseComponent {
14
16
  static get observedAttributes() {
@@ -15,6 +15,8 @@ export declare const SELECT_CONSTANTS: {
15
15
  PLACEHOLDER: string;
16
16
  OBSERVE_SCROLL: string;
17
17
  OBSERVE_SCROLL_THRESHOLD: string;
18
+ SHOW_SELECT_ALL: string;
19
+ SELECT_ALL_LABEL: string;
18
20
  };
19
21
  attributes: {
20
22
  OPEN: string;
@@ -26,6 +28,8 @@ export declare const SELECT_CONSTANTS: {
26
28
  PLACEHOLDER: string;
27
29
  OBSERVE_SCROLL: string;
28
30
  OBSERVE_SCROLL_THRESHOLD: string;
31
+ SHOW_SELECT_ALL: string;
32
+ SELECT_ALL_LABEL: string;
29
33
  };
30
34
  selectors: {
31
35
  FIELD: string;
@@ -34,6 +38,7 @@ export declare const SELECT_CONSTANTS: {
34
38
  };
35
39
  events: {
36
40
  SCROLLED_BOTTOM: string;
41
+ SELECT_ALL: string;
37
42
  };
38
43
  };
39
44
  export { type FieldDensity as SelectDensityType, type FieldShape as SelectShapeType } from '../../field/base/base-field-constants';
@@ -19,13 +19,16 @@ const observedAttributes = {
19
19
  DISABLED: 'disabled',
20
20
  PLACEHOLDER: 'placeholder',
21
21
  OBSERVE_SCROLL: 'observe-scroll',
22
- OBSERVE_SCROLL_THRESHOLD: 'observe-scroll-threshold'
22
+ OBSERVE_SCROLL_THRESHOLD: 'observe-scroll-threshold',
23
+ SHOW_SELECT_ALL: 'show-select-all',
24
+ SELECT_ALL_LABEL: 'select-all-label'
23
25
  };
24
26
  const attributes = {
25
27
  ...observedAttributes
26
28
  };
27
29
  const events = {
28
- SCROLLED_BOTTOM: `${elementName}-scrolled-bottom`
30
+ SCROLLED_BOTTOM: `${elementName}-scrolled-bottom`,
31
+ SELECT_ALL: `${elementName}-select-all`
29
32
  };
30
33
  export const SELECT_CONSTANTS = {
31
34
  elementName,
@@ -12,12 +12,14 @@ import { IWithElementInternals } from '../../core/mixins/internals/with-element-
12
12
  import { IWithLabelAwareness } from '../../core/mixins/label/with-label-aware';
13
13
  import { FieldDensity, FieldLabelPosition } from '../../field';
14
14
  import { IWithBaseField } from '../../field/base/with-base-field';
15
- import { BaseSelectComponent, IBaseSelectComponent } from '../core';
15
+ import { BaseSelectComponent, IBaseSelectComponent, SelectSelectAllEventData } from '../core';
16
16
  import { SelectCore } from './select-core';
17
17
  import { IListDropdownAware } from '../../list-dropdown/list-dropdown-aware';
18
18
  export interface ISelectComponent extends IWithFormAssociation, IWithFocusable, IWithLabelAwareness, IWithElementInternals, IWithDefaultAria, IWithBaseField, IBaseSelectComponent, IListDropdownAware {
19
19
  label: string;
20
20
  placeholder: string;
21
+ showSelectAll: boolean;
22
+ selectAllLabel: string;
21
23
  setFormValue(value: FormValue | null, state?: FormValue | null | undefined): void;
22
24
  [setValidity](): void;
23
25
  }
@@ -27,15 +29,18 @@ declare global {
27
29
  }
28
30
  interface HTMLElementEventMap {
29
31
  'forge-select-scrolled-bottom': CustomEvent<void>;
32
+ 'forge-select-all': CustomEvent<SelectSelectAllEventData>;
30
33
  change: CustomEvent<any>;
31
34
  }
32
35
  }
33
36
  declare const SelectComponent_base: import("../../constants").AbstractConstructor<import("../../core/mixins/form/with-form-associated").WithFormAssociationContract> & import("../../constants").AbstractConstructor<import("../../core/mixins/label/with-label-aware").WithLabelAwarenessContract> & import("../../constants").AbstractConstructor<import("../../core/mixins/focus/with-focusable").WithFocusableContract> & import("../../constants").AbstractConstructor<import("../../core/mixins/internals/with-default-aria").WithDefaultAriaContract> & import("../../constants").AbstractConstructor<import("../../core/mixins/internals/with-element-internals").WithElementInternalsContract> & import("../../constants").AbstractConstructor<import("../../field/base/with-base-field").WithBaseFieldContract> & ((abstract new () => BaseSelectComponent<SelectCore>) & {
34
- readonly observedAttributes: string[];
37
+ get observedAttributes(): string[];
35
38
  });
36
39
  /**
37
40
  * @tag forge-select
38
41
  *
42
+ * @summary Selects present a list of options to users for single or multi-selection.
43
+ *
39
44
  * @dependency forge-field
40
45
  * @dependency forge-option
41
46
  * @dependency forge-option-group
@@ -53,9 +58,12 @@ declare const SelectComponent_base: import("../../constants").AbstractConstructo
53
58
  *
54
59
  * @event {CustomEvent<void>} forge-select-scrolled-bottom - Dispatched when the dropdown list has scrolled to the bottom.
55
60
  * @event {CustomEvent<any>} change - Dispatched when the user selects a value.
61
+ * @event {CustomEvent<SelectSelectAllEventData>} forge-select-all - Dispatched when the select all option is toggled.
56
62
  *
57
63
  * @property {string} label - Controls the label text.
58
64
  * @property {string} placeholder - Controls the placeholder text.
65
+ * @property {boolean} showSelectAll - Gets/sets whether to show the select all option when in multiple mode.
66
+ * @property {string} selectAllLabel - Gets/sets the label for the select all option.
59
67
  * @property {any} value - Gets/sets the value.
60
68
  * @property {number | number[]} selectedIndex - Gets/sets the selected index.
61
69
  * @property {ISelectOption[] | ISelectOptionGroup[]} options - Gets/sets the available options.
@@ -81,6 +89,8 @@ declare const SelectComponent_base: import("../../constants").AbstractConstructo
81
89
  *
82
90
  * @attribute {string} label - Controls the label text.
83
91
  * @attribute {string} placeholder - Controls the placeholder text.
92
+ * @attribute {boolean} show-select-all - Gets/sets whether to show the select all option when in multiple mode.
93
+ * @attribute {string} select-all-label - Gets/sets the label for the select all option.
84
94
  * @attribute {any} value - Gets/sets the value.
85
95
  * @attribute {number | number[]} selected-index - Gets/sets the selected index.
86
96
  * @attribute {boolean} multiple - Gets/sets the multiple select state.
@@ -174,6 +184,8 @@ export declare class SelectComponent extends SelectComponent_base implements ISe
174
184
  label: string;
175
185
  placeholder: string;
176
186
  readonly: boolean;
187
+ showSelectAll: boolean;
188
+ selectAllLabel: string;
177
189
  get floatLabel(): boolean;
178
190
  set floatLabel(value: boolean);
179
191
  get density(): FieldDensity;
@@ -28,11 +28,14 @@ import { SelectAdapter } from './select-adapter';
28
28
  import { SELECT_CONSTANTS } from './select-constants';
29
29
  import { SelectCore } from './select-core';
30
30
  import { ListDropdownAware } from '../../list-dropdown/list-dropdown-aware';
31
+ import { DividerComponent } from '../../divider/divider';
31
32
  const template = '<template><forge-field id=\"field\" popover-icon focus-indicator-allow-focus focus-indicator-focus-mode=\"focus\"><span id=\"select-label\" slot=\"label\"></span><slot slot=\"start\" name=\"start\"></slot><div data-forge-field-input class=\"selected-text-container\" part=\"text-container\" aria-live=\"assertive\" aria-atomic=\"true\"><slot name=\"value\"><span id=\"selected-text\" class=\"selected-text\" part=\"text\"></span></slot></div><slot slot=\"end\" name=\"end\"></slot><slot slot=\"accessory\" name=\"accessory\"></slot><slot slot=\"support-text\" name=\"support-text\"></slot><slot slot=\"support-text-end\" name=\"support-text-end\"></slot><slot slot=\"start\" name=\"leading\"></slot><slot slot=\"end\" name=\"trailing\"></slot><slot slot=\"accessory\" name=\"addon-end\"></slot><slot slot=\"support-text\" name=\"helper-text\"></slot></forge-field></template>';
32
33
  const styles = ':host{display:block;outline:0}:host([hidden]){display:none}forge-field{--_select-placeholder-color:var(--forge-select-placeholder-color, var(--forge-field-placeholder-color, var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6))))}.selected-text-container{display:grid;align-items:center;cursor:pointer}.selected-text,::slotted([slot=value]){white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.selected-text:empty::before{content:attr(placeholder);color:var(--_select-placeholder-color);font-size:inherit}:host([disabled]) .selected-text-container{cursor:not-allowed}forge-field{display:contents}';
33
34
  /**
34
35
  * @tag forge-select
35
36
  *
37
+ * @summary Selects present a list of options to users for single or multi-selection.
38
+ *
36
39
  * @dependency forge-field
37
40
  * @dependency forge-option
38
41
  * @dependency forge-option-group
@@ -50,9 +53,12 @@ const styles = ':host{display:block;outline:0}:host([hidden]){display:none}forge
50
53
  *
51
54
  * @event {CustomEvent<void>} forge-select-scrolled-bottom - Dispatched when the dropdown list has scrolled to the bottom.
52
55
  * @event {CustomEvent<any>} change - Dispatched when the user selects a value.
56
+ * @event {CustomEvent<SelectSelectAllEventData>} forge-select-all - Dispatched when the select all option is toggled.
53
57
  *
54
58
  * @property {string} label - Controls the label text.
55
59
  * @property {string} placeholder - Controls the placeholder text.
60
+ * @property {boolean} showSelectAll - Gets/sets whether to show the select all option when in multiple mode.
61
+ * @property {string} selectAllLabel - Gets/sets the label for the select all option.
56
62
  * @property {any} value - Gets/sets the value.
57
63
  * @property {number | number[]} selectedIndex - Gets/sets the selected index.
58
64
  * @property {ISelectOption[] | ISelectOptionGroup[]} options - Gets/sets the available options.
@@ -78,6 +84,8 @@ const styles = ':host{display:block;outline:0}:host([hidden]){display:none}forge
78
84
  *
79
85
  * @attribute {string} label - Controls the label text.
80
86
  * @attribute {string} placeholder - Controls the placeholder text.
87
+ * @attribute {boolean} show-select-all - Gets/sets whether to show the select all option when in multiple mode.
88
+ * @attribute {string} select-all-label - Gets/sets the label for the select all option.
81
89
  * @attribute {any} value - Gets/sets the value.
82
90
  * @attribute {number | number[]} selected-index - Gets/sets the selected index.
83
91
  * @attribute {boolean} multiple - Gets/sets the multiple select state.
@@ -194,6 +202,12 @@ let SelectComponent = class SelectComponent extends WithFormAssociation(WithLabe
194
202
  case SELECT_CONSTANTS.observedAttributes.PLACEHOLDER:
195
203
  this.placeholder = newValue;
196
204
  return;
205
+ case SELECT_CONSTANTS.observedAttributes.SHOW_SELECT_ALL:
206
+ this.showSelectAll = coerceBoolean(newValue);
207
+ return;
208
+ case SELECT_CONSTANTS.observedAttributes.SELECT_ALL_LABEL:
209
+ this.selectAllLabel = newValue;
210
+ return;
197
211
  }
198
212
  super.attributeChangedCallback(name, oldValue, newValue);
199
213
  }
@@ -274,6 +288,12 @@ __decorate([
274
288
  __decorate([
275
289
  coreProperty()
276
290
  ], SelectComponent.prototype, "readonly", void 0);
291
+ __decorate([
292
+ coreProperty()
293
+ ], SelectComponent.prototype, "showSelectAll", void 0);
294
+ __decorate([
295
+ coreProperty()
296
+ ], SelectComponent.prototype, "selectAllLabel", void 0);
277
297
  SelectComponent = __decorate([
278
298
  customElement({
279
299
  name: SELECT_CONSTANTS.elementName,
@@ -288,7 +308,8 @@ SelectComponent = __decorate([
288
308
  IconComponent,
289
309
  ScaffoldComponent,
290
310
  ToolbarComponent,
291
- IconButtonComponent
311
+ IconButtonComponent,
312
+ DividerComponent
292
313
  ]
293
314
  })
294
315
  ], SelectComponent);
@@ -20,6 +20,8 @@ declare global {
20
20
  }
21
21
  /**
22
22
  * @tag forge-select-dropdown
23
+ *
24
+ * @summary A dropdown variant of the select component that renders options in a popover.
23
25
  */
24
26
  export declare class SelectDropdownComponent extends BaseSelectComponent<SelectDropdownCore> implements ISelectDropdownComponent {
25
27
  static get observedAttributes(): string[];
@@ -22,6 +22,8 @@ const template = '<template></template>';
22
22
  const styles = ':host{display:none}';
23
23
  /**
24
24
  * @tag forge-select-dropdown
25
+ *
26
+ * @summary A dropdown variant of the select component that renders options in a popover.
25
27
  */
26
28
  let SelectDropdownComponent = class SelectDropdownComponent extends BaseSelectComponent {
27
29
  static get observedAttributes() {
@@ -11,7 +11,7 @@ import { SplitButtonAdapter } from './split-button-adapter';
11
11
  import { SPLIT_BUTTON_CONSTANTS } from './split-button-constants';
12
12
  import { SplitButtonCore } from './split-button-core';
13
13
  const template = '<template><slot></slot></template>';
14
- const styles = ':host{--_split-button-min-width:var(--forge-split-button-min-width, 0);--_split-button-gap:var(--forge-split-button-gap, var(--forge-border-thin, 1px));--_split-button-focus-indicator-offset:var(--forge-split-button-focus-indicator-offset, var(--forge-button-focus-indicator-offset, 4px));--_split-button-focus-indicator-divider-offset:var(--forge-split-button-focus-indicator-divider-offset, var(--_split-button-gap));--_split-button-focus-indicator-offset-adjusted:calc(var(--_split-button-focus-indicator-offset) + var(--_split-button-focus-indicator-divider-offset) * 2)}:host{display:inline-flex;justify-content:center;align-items:center}:host([hidden]){display:none}::slotted(*){--forge-button-min-width:var(--_split-button-min-width);--forge-button-focus-indicator-offset:var(--_split-button-focus-indicator-offset)}::slotted(:first-child){--forge-button-shape-start-end-radius:0;--forge-button-shape-end-end-radius:0;--forge-focus-indicator-shape-start-end:0;--forge-focus-indicator-shape-end-end:0;--forge-focus-indicator-offset-inline:0 var(--_split-button-focus-indicator-offset-adjusted)}::slotted(:not(:first-child):not(:last-child)){--forge-button-shape:0;--forge-focus-indicator-shape:0;--forge-focus-indicator-offset-inline:var(--_split-button-focus-indicator-offset-adjusted)}::slotted(:last-child){--forge-button-shape-start-start-radius:0;--forge-button-shape-end-start-radius:0;--forge-focus-indicator-shape-start-start:0;--forge-focus-indicator-shape-end-start:0;--forge-focus-indicator-offset-inline:var(--_split-button-focus-indicator-offset-adjusted) 0}:host([variant=outlined]){--_split-button-focus-indicator-divider-offset:var(--forge-split-button-focus-indicator-divider-offset, 0px)}:host([variant=outlined]) ::slotted(:not(:first-child)){margin-inline-start:calc(-1 * var(--_split-button-gap))}:host(:is([variant=tonal],[variant=filled],[variant=raised],:not([variant]))) ::slotted(:not(:last-child)){margin-inline-end:var(--_split-button-gap)}';
14
+ const styles = ':host{--_split-button-min-width:var(--forge-split-button-min-width, 0);--_split-button-gap:var(--forge-split-button-gap, var(--forge-border-thin, 1px));--_split-button-focus-indicator-offset:var(--forge-split-button-focus-indicator-offset, var(--forge-button-focus-indicator-offset, 4px));--_split-button-focus-indicator-divider-offset:var(--forge-split-button-focus-indicator-divider-offset, var(--_split-button-gap));--_split-button-focus-indicator-offset-adjusted:calc(var(--_split-button-focus-indicator-offset) + var(--_split-button-focus-indicator-divider-offset) * 2)}:host{display:inline-flex;justify-content:center;align-items:center}:host([hidden]){display:none}::slotted(*){--forge-button-min-width:var(--_split-button-min-width);--forge-button-focus-indicator-offset:var(--_split-button-focus-indicator-offset)}::slotted(:first-child){--forge-button-shape-start-end-radius:0;--forge-button-shape-end-end-radius:0;--forge-focus-indicator-shape-start-end:0;--forge-focus-indicator-shape-end-end:0;--forge-focus-indicator-offset-inline:0 var(--_split-button-focus-indicator-offset-adjusted)}::slotted(:not(:first-child):not(:last-child)){--forge-button-shape:0;--forge-focus-indicator-shape:0;--forge-focus-indicator-offset-inline:var(--_split-button-focus-indicator-offset-adjusted)}::slotted(:last-child){--forge-button-shape-start-start-radius:0;--forge-button-shape-end-start-radius:0;--forge-focus-indicator-shape-start-start:0;--forge-focus-indicator-shape-end-start:0;--forge-focus-indicator-offset-inline:var(--_split-button-focus-indicator-offset-adjusted) 0}:host([variant=outlined]) ::slotted(:not(:first-child)){margin-inline-start:calc(-1 * var(--_split-button-gap))}:host([variant=outlined]){--_split-button-focus-indicator-divider-offset:var(--forge-split-button-focus-indicator-divider-offset, 0px)}:host(:is([variant=tonal],[variant=filled],[variant=raised],:not([variant]))) ::slotted(:not(:last-child)){margin-inline-end:var(--_split-button-gap)}';
15
15
  /**
16
16
  * @tag forge-split-button
17
17
  *
@@ -21,6 +21,8 @@ declare global {
21
21
  }
22
22
  /**
23
23
  * @tag forge-split-view
24
+ *
25
+ * @summary Split views create resizable panels that allow users to adjust the space between content areas.
24
26
  */
25
27
  export declare class SplitViewComponent extends BaseComponent implements ISplitViewComponent {
26
28
  static get observedAttributes(): string[];
@@ -14,6 +14,8 @@ const template = '<template><div class=\"forge-split-view\" id=\"root\" part=\"r
14
14
  const styles = '.forge-split-view{display:flex;width:100%;height:100%}:host{--forge-split-view-handle-width:8px;display:block;height:100%;width:100%;overflow:hidden}:host([hidden]){display:none}:host([orientation=horizontal]) .forge-split-view{flex-direction:row}:host([orientation=vertical]) .forge-split-view{flex-direction:column}';
15
15
  /**
16
16
  * @tag forge-split-view
17
+ *
18
+ * @summary Split views create resizable panels that allow users to adjust the space between content areas.
17
19
  */
18
20
  let SplitViewComponent = class SplitViewComponent extends BaseComponent {
19
21
  static get observedAttributes() {
@@ -37,6 +37,8 @@ declare global {
37
37
  /**
38
38
  * @tag forge-split-view-panel
39
39
  *
40
+ * @summary Individual panels within split views that can be resized and collapsed.
41
+ *
40
42
  * @dependency forge-icon
41
43
  * @dependency forge-state-layer
42
44
  * @dependency forge-focus-indicator