@tylertech/forge 3.12.0 → 3.13.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 (60) hide show
  1. package/custom-elements.json +1792 -231
  2. package/dist/lib.js +12 -12
  3. package/dist/lib.js.map +4 -4
  4. package/dist/vscode.css-custom-data.json +2 -1
  5. package/dist/vscode.html-custom-data.json +85 -1
  6. package/esm/autocomplete/autocomplete-constants.d.ts +1 -0
  7. package/esm/autocomplete/autocomplete-constants.js +1 -0
  8. package/esm/autocomplete/autocomplete-core.d.ts +6 -0
  9. package/esm/autocomplete/autocomplete-core.js +49 -12
  10. package/esm/autocomplete/autocomplete.d.ts +7 -0
  11. package/esm/autocomplete/autocomplete.js +7 -0
  12. package/esm/calendar/calendar-core.d.ts +2 -0
  13. package/esm/calendar/calendar-core.js +20 -14
  14. package/esm/core/testing/test-harness.d.ts +11 -0
  15. package/esm/core/testing/test-harness.js +14 -0
  16. package/esm/core/testing/utils.d.ts +7 -0
  17. package/esm/core/testing/utils.js +14 -0
  18. package/esm/core/utils/index.d.ts +1 -0
  19. package/esm/core/utils/index.js +1 -0
  20. package/esm/core/utils/key-action.d.ts +102 -0
  21. package/esm/core/utils/key-action.js +109 -0
  22. package/esm/expansion-panel/expansion-panel-adapter.d.ts +9 -0
  23. package/esm/expansion-panel/expansion-panel-adapter.js +31 -3
  24. package/esm/expansion-panel/expansion-panel-constants.d.ts +2 -0
  25. package/esm/expansion-panel/expansion-panel-constants.js +2 -1
  26. package/esm/expansion-panel/expansion-panel-core.d.ts +7 -0
  27. package/esm/expansion-panel/expansion-panel-core.js +30 -0
  28. package/esm/expansion-panel/expansion-panel.d.ts +11 -3
  29. package/esm/expansion-panel/expansion-panel.js +16 -3
  30. package/esm/list-dropdown/list-dropdown-core.js +1 -1
  31. package/esm/list-dropdown/list-dropdown-utils.js +1 -1
  32. package/esm/list-dropdown/list-dropdown.js +1 -1
  33. package/esm/paginator/paginator-adapter.d.ts +1 -0
  34. package/esm/paginator/paginator-adapter.js +15 -4
  35. package/esm/split-view/split-view-panel/split-view-panel.js +1 -1
  36. package/esm/tree/index.d.ts +7 -0
  37. package/esm/tree/index.js +7 -0
  38. package/esm/tree/tree/index.d.ts +7 -0
  39. package/esm/tree/tree/index.js +11 -0
  40. package/esm/tree/tree/tree-selection-controller.d.ts +104 -0
  41. package/esm/tree/tree/tree-selection-controller.js +375 -0
  42. package/esm/tree/tree/tree.d.ts +141 -0
  43. package/esm/tree/tree/tree.js +488 -0
  44. package/esm/tree/tree-item/index.d.ts +7 -0
  45. package/esm/tree/tree-item/index.js +11 -0
  46. package/esm/tree/tree-item/tree-item.d.ts +112 -0
  47. package/esm/tree/tree-item/tree-item.js +378 -0
  48. package/esm/tree/tree-utils.d.ts +154 -0
  49. package/esm/tree/tree-utils.js +315 -0
  50. package/package.json +2 -1
  51. package/sass/core/styles/tokens/tree/tree/_tokens.scss +24 -0
  52. package/sass/core/styles/tokens/tree/tree-item/_tokens.scss +39 -0
  53. package/sass/tree/tree/_core.scss +31 -0
  54. package/sass/tree/tree/_token-utils.scss +30 -0
  55. package/sass/tree/tree/index.scss +6 -0
  56. package/sass/tree/tree/tree.scss +44 -0
  57. package/sass/tree/tree-item/_core.scss +121 -0
  58. package/sass/tree/tree-item/_token-utils.scss +30 -0
  59. package/sass/tree/tree-item/index.scss +6 -0
  60. package/sass/tree/tree-item/tree-item.scss +199 -0
@@ -6071,6 +6071,7 @@
6071
6071
  {
6072
6072
  "name": "::part(scroll-container)",
6073
6073
  "description": "The scroll container element."
6074
- }
6074
+ },
6075
+ { "name": "::part(root)", "description": "The root tree element." }
6075
6076
  ]
6076
6077
  }
@@ -43,6 +43,11 @@
43
43
  "description": "Gets/sets whether the first option in the dropdown will be focused automatically when opened or not.",
44
44
  "values": []
45
45
  },
46
+ {
47
+ "name": "select-first-option-on-blur",
48
+ "description": "Determines whether the first available option should be selected automatically when blurring mid-filter.",
49
+ "values": []
50
+ },
46
51
  {
47
52
  "name": "allow-unmatched",
48
53
  "description": "Controls whether unmatched text entered by the user will stay visible an option in the dropdown is not found.",
@@ -900,7 +905,12 @@
900
905
  },
901
906
  {
902
907
  "name": "trigger",
903
- "description": "The id of the button that the expansion panel is associated with.",
908
+ "description": "The id of the button that the expansion panel should be toggled by.",
909
+ "values": []
910
+ },
911
+ {
912
+ "name": "open-icon",
913
+ "description": "The id of the `<forge-open-icon>` that the expansion panel should toggle.",
904
914
  "values": []
905
915
  }
906
916
  ],
@@ -3929,6 +3939,80 @@
3929
3939
  ],
3930
3940
  "references": []
3931
3941
  },
3942
+ {
3943
+ "name": "forge-tree",
3944
+ "description": "Trees are interactive lists that allow users to navigate through hierarchical data.\n---\n\n\n### **Events:**\n - **forge-tree-select-all** - Dispatched when the user selects all items.\n\n### **Slots:**\n - _default_ - The default slot for tree items.\n- **expand-icon** - A custom expand icon to show when an item is closed.\n- **collapse-icon** - A custom collapse icon to show when an item is open.\n\n### **CSS Parts:**\n - **root** - The root tree element.",
3945
+ "attributes": [
3946
+ {
3947
+ "name": "accordion",
3948
+ "description": "Whether opening an item closes all other items.",
3949
+ "values": []
3950
+ },
3951
+ {
3952
+ "name": "indentLines",
3953
+ "description": "Toggles the rendering of indent lines showing hierarchy.",
3954
+ "values": []
3955
+ },
3956
+ {
3957
+ "name": "mode",
3958
+ "description": "How selecting tree items is handled.",
3959
+ "values": [{ "name": "TreeMode" }]
3960
+ },
3961
+ {
3962
+ "name": "selectionFollowsFocus",
3963
+ "description": "Whether focusing an item also selects it. This takes no effect when in multiple mode.",
3964
+ "values": []
3965
+ },
3966
+ {
3967
+ "name": "disabled",
3968
+ "description": "Whether selecting items is disabled.",
3969
+ "values": []
3970
+ },
3971
+ {
3972
+ "name": "value",
3973
+ "description": "The value of all selected items.",
3974
+ "values": [{ "name": "unknown[]" }]
3975
+ }
3976
+ ],
3977
+ "references": []
3978
+ },
3979
+ {
3980
+ "name": "forge-tree-item",
3981
+ "description": "\n---\n\n\n### **Events:**\n - **forge-tree-item-update**\n- **forge-tree-item-select** - Dispatched when the user selects a tree item.\n- **forge-tree-item-open** - Dispatched when the user opens a tree item.\n- **forge-tree-item-close** - Dispatched when the user closes a tree item.",
3982
+ "attributes": [
3983
+ {
3984
+ "name": "value",
3985
+ "description": "The value of the tree item.",
3986
+ "values": []
3987
+ },
3988
+ {
3989
+ "name": "selected",
3990
+ "description": "Whether the tree item is selected.",
3991
+ "values": []
3992
+ },
3993
+ {
3994
+ "name": "open",
3995
+ "description": "Whether the tree item is expanded.",
3996
+ "values": []
3997
+ },
3998
+ {
3999
+ "name": "lazy",
4000
+ "description": "Whether the tree item supports lazy loading.",
4001
+ "values": []
4002
+ },
4003
+ {
4004
+ "name": "disabled",
4005
+ "description": "Whether the tree item is disabled.",
4006
+ "values": []
4007
+ },
4008
+ {
4009
+ "name": "openDisabled",
4010
+ "description": "Whether opening the tree item is disabled.",
4011
+ "values": []
4012
+ }
4013
+ ],
4014
+ "references": []
4015
+ },
3932
4016
  {
3933
4017
  "name": "forge-view",
3934
4018
  "description": "Represents a single view content area within a view-switcher for organizing and displaying content sections.\n---\n",
@@ -14,6 +14,7 @@ export declare const AUTOCOMPLETE_CONSTANTS: {
14
14
  DEBOUNCE: string;
15
15
  FILTER_ON_FOCUS: string;
16
16
  FILTER_FOCUS_FIRST: string;
17
+ SELECT_FIRST_OPTION_ON_BLUR: string;
17
18
  ALLOW_UNMATCHED: string;
18
19
  POPUP_TARGET: string;
19
20
  POPUP_CLASSES: string;
@@ -11,6 +11,7 @@ const attributes = {
11
11
  DEBOUNCE: 'debounce',
12
12
  FILTER_ON_FOCUS: 'filter-on-focus',
13
13
  FILTER_FOCUS_FIRST: 'filter-focus-first',
14
+ SELECT_FIRST_OPTION_ON_BLUR: 'select-first-option-on-blur',
14
15
  ALLOW_UNMATCHED: 'allow-unmatched',
15
16
  POPUP_TARGET: 'popup-target',
16
17
  POPUP_CLASSES: 'popup-classes',
@@ -14,6 +14,7 @@ export interface IAutocompleteCore extends IListDropdownAwareCore {
14
14
  debounce: number;
15
15
  filterOnFocus: boolean;
16
16
  filterFocusFirst: boolean;
17
+ selectFirstOptionOnBlur: boolean;
17
18
  allowUnmatched: boolean;
18
19
  popupTarget: string;
19
20
  filterText: string;
@@ -38,6 +39,7 @@ export declare class AutocompleteCore extends ListDropdownAwareCore implements I
38
39
  private _popupTarget;
39
40
  private _filterOnFocus;
40
41
  private _filterFocusFirst;
42
+ private _selectFirstOptionOnBlur;
41
43
  private _optionBuilder?;
42
44
  private _filter?;
43
45
  private _selectedTextBuilder;
@@ -46,6 +48,7 @@ export declare class AutocompleteCore extends ListDropdownAwareCore implements I
46
48
  private _selectedOptions;
47
49
  private _values;
48
50
  private _pendingFilterPromises;
51
+ private _selectOnBlurPending;
49
52
  private _identifier;
50
53
  private _matchKey?;
51
54
  private _filterFn;
@@ -81,6 +84,7 @@ export declare class AutocompleteCore extends ListDropdownAwareCore implements I
81
84
  private _onFocus;
82
85
  private _onBlur;
83
86
  private _applyBlur;
87
+ private _shouldSelectFirstOptionOnBlur;
84
88
  private _onInput;
85
89
  private _debounceFilter;
86
90
  private _onKeydown;
@@ -131,6 +135,8 @@ export declare class AutocompleteCore extends ListDropdownAwareCore implements I
131
135
  /** Gets/sets whether the first option in the dropdown will be focused automatically when opened or not. */
132
136
  get filterFocusFirst(): boolean;
133
137
  set filterFocusFirst(value: boolean);
138
+ get selectFirstOptionOnBlur(): boolean;
139
+ set selectFirstOptionOnBlur(value: boolean);
134
140
  /** Controls whether unmatched text entered by the user will stay visible an option in the dropdown is not found. */
135
141
  get allowUnmatched(): boolean;
136
142
  set allowUnmatched(value: boolean);
@@ -21,11 +21,13 @@ export class AutocompleteCore extends ListDropdownAwareCore {
21
21
  this._allowUnmatched = false;
22
22
  this._filterOnFocus = true;
23
23
  this._filterFocusFirst = true;
24
+ this._selectFirstOptionOnBlur = false;
24
25
  this._options = [];
25
26
  this._filterText = '';
26
27
  this._selectedOptions = [];
27
28
  this._values = [];
28
29
  this._pendingFilterPromises = [];
30
+ this._selectOnBlurPending = false;
29
31
  this._matchKey = null;
30
32
  this._clickListener = evt => this._onClick(evt);
31
33
  this._focusListener = () => this._onFocus();
@@ -147,6 +149,7 @@ export class AutocompleteCore extends ListDropdownAwareCore {
147
149
  this._adapter.emitHostEvent(AUTOCOMPLETE_CONSTANTS.events.SCROLLED_BOTTOM);
148
150
  }
149
151
  _onFocus() {
152
+ this._selectOnBlurPending = false;
150
153
  if (!this._isDropdownOpen && this._adapter.getInputValue() && !Platform.isMobile) {
151
154
  this._adapter.selectInputValue();
152
155
  }
@@ -159,11 +162,11 @@ export class AutocompleteCore extends ListDropdownAwareCore {
159
162
  this._applyBlur();
160
163
  }
161
164
  _applyBlur() {
165
+ this._selectOnBlurPending = this._shouldSelectFirstOptionOnBlur();
162
166
  if (this._isDropdownOpen) {
163
167
  this._closeDropdown();
164
168
  }
165
- // If we are in stateless mode, we don't need to do anything further
166
- if (this._mode === AutocompleteMode.Stateless) {
169
+ if (this._selectOnBlurPending || this._mode === AutocompleteMode.Stateless) {
167
170
  return;
168
171
  }
169
172
  if (!this._selectedOptions.length) {
@@ -176,6 +179,9 @@ export class AutocompleteCore extends ListDropdownAwareCore {
176
179
  this._adapter.setSelectedText(this._getSelectedText());
177
180
  }
178
181
  }
182
+ _shouldSelectFirstOptionOnBlur() {
183
+ return this._selectFirstOptionOnBlur && !this._multiple && this._mode !== AutocompleteMode.Stateless && !!this._filter && !!this._filterText;
184
+ }
179
185
  _onInput(evt) {
180
186
  if (this._selectedOptions.length && !this._multiple && (!this._adapter.getInputValue() || this._allowUnmatched) && !this._adapter.isWrappingChipField()) {
181
187
  this._selectedOptions = [];
@@ -203,10 +209,14 @@ export class AutocompleteCore extends ListDropdownAwareCore {
203
209
  // If the dropdown is open, show the spinner and execute the filter. If not open, then we need to
204
210
  // show the dropdown (which will handle showing the spinner and executing the filter for us) so
205
211
  // we use the promise from that method instead.
206
- if (this._isDropdownOpen) {
212
+ const keepDropdownClosed = this._selectOnBlurPending && !this._adapter.hasFocus();
213
+ if (this._isDropdownOpen && keepDropdownClosed) {
214
+ this._closeDropdown();
215
+ }
216
+ else if (this._isDropdownOpen) {
207
217
  this._adapter.setBusyVisibility(true);
208
218
  }
209
- else {
219
+ else if (!keepDropdownClosed) {
210
220
  this._showDropdown({ filter: false });
211
221
  }
212
222
  try {
@@ -215,6 +225,7 @@ export class AutocompleteCore extends ListDropdownAwareCore {
215
225
  catch {
216
226
  // When an exception occurs, we just flush the pending promises and clean up
217
227
  this._pendingFilterPromises = [];
228
+ this._selectOnBlurPending = false;
218
229
  if (this._isDropdownOpen) {
219
230
  this._closeDropdown();
220
231
  }
@@ -307,21 +318,38 @@ export class AutocompleteCore extends ListDropdownAwareCore {
307
318
  .catch(e => reject(`An unexpected error occurred while filtering: ${e}`)));
308
319
  }
309
320
  _onFilterComplete() {
310
- if (!this._adapter.hasFocus()) {
321
+ const shouldSelectOnBlur = this._selectFirstOptionOnBlur && this._selectOnBlurPending;
322
+ if (!this._options.length) {
323
+ this._selectOnBlurPending = false;
311
324
  if (this._isDropdownOpen) {
312
325
  this._closeDropdown();
313
326
  }
327
+ if (shouldSelectOnBlur && !this._allowUnmatched) {
328
+ this._filterText = '';
329
+ this._adapter.setSelectedText('');
330
+ this._clearValue();
331
+ }
314
332
  return;
315
333
  }
316
- if (this._options.length) {
317
- const sendFilterText = this._allowUnmatched && !this._selectedOptions.length;
318
- this._dropdownReady({ userTriggered: sendFilterText });
319
- if (this._filterFocusFirst && this._filterText) {
320
- this._adapter.activateFirstOption();
334
+ if (shouldSelectOnBlur) {
335
+ this._selectOnBlurPending = false;
336
+ const firstOption = this._flatOptions[0];
337
+ if (firstOption) {
338
+ this._onSelect(firstOption.value, false);
321
339
  }
340
+ return;
322
341
  }
323
- else {
324
- this._closeDropdown();
342
+ if (!this._adapter.hasFocus()) {
343
+ this._selectOnBlurPending = false;
344
+ if (this._isDropdownOpen) {
345
+ this._closeDropdown();
346
+ }
347
+ return;
348
+ }
349
+ const sendFilterText = this._allowUnmatched && !this._selectedOptions.length;
350
+ this._dropdownReady({ userTriggered: sendFilterText });
351
+ if (this._filterFocusFirst && this._filterText) {
352
+ this._adapter.activateFirstOption();
325
353
  }
326
354
  }
327
355
  _clearValue() {
@@ -758,6 +786,15 @@ export class AutocompleteCore extends ListDropdownAwareCore {
758
786
  this._adapter.toggleHostAttribute(AUTOCOMPLETE_CONSTANTS.attributes.FILTER_FOCUS_FIRST, this._filterFocusFirst);
759
787
  }
760
788
  }
789
+ get selectFirstOptionOnBlur() {
790
+ return this._selectFirstOptionOnBlur;
791
+ }
792
+ set selectFirstOptionOnBlur(value) {
793
+ if (this._selectFirstOptionOnBlur !== value) {
794
+ this._selectFirstOptionOnBlur = value;
795
+ this._adapter.toggleHostAttribute(AUTOCOMPLETE_CONSTANTS.attributes.SELECT_FIRST_OPTION_ON_BLUR, this._selectFirstOptionOnBlur);
796
+ }
797
+ }
761
798
  /** Controls whether unmatched text entered by the user will stay visible an option in the dropdown is not found. */
762
799
  get allowUnmatched() {
763
800
  return this._allowUnmatched;
@@ -12,6 +12,7 @@ export interface IAutocompleteComponent extends IListDropdownAware {
12
12
  debounce: number;
13
13
  filterOnFocus: boolean;
14
14
  filterFocusFirst: boolean;
15
+ selectFirstOptionOnBlur: boolean;
15
16
  allowUnmatched: boolean;
16
17
  matchKey: string | null | undefined;
17
18
  popupTarget: string;
@@ -88,6 +89,12 @@ export declare class AutocompleteComponent extends ListDropdownAware implements
88
89
  * @attribute filter-focus-first
89
90
  */
90
91
  filterFocusFirst: boolean;
92
+ /**
93
+ * Determines whether the first available option should be selected automatically when blurring mid-filter.
94
+ * @default false
95
+ * @attribute select-first-option-on-blur
96
+ */
97
+ selectFirstOptionOnBlur: boolean;
91
98
  /**
92
99
  * Controls whether unmatched text entered by the user will stay visible an option in the dropdown is not found.
93
100
  * @default false
@@ -37,6 +37,7 @@ let AutocompleteComponent = class AutocompleteComponent extends ListDropdownAwar
37
37
  AUTOCOMPLETE_CONSTANTS.attributes.DEBOUNCE,
38
38
  AUTOCOMPLETE_CONSTANTS.attributes.FILTER_ON_FOCUS,
39
39
  AUTOCOMPLETE_CONSTANTS.attributes.FILTER_FOCUS_FIRST,
40
+ AUTOCOMPLETE_CONSTANTS.attributes.SELECT_FIRST_OPTION_ON_BLUR,
40
41
  AUTOCOMPLETE_CONSTANTS.attributes.ALLOW_UNMATCHED,
41
42
  AUTOCOMPLETE_CONSTANTS.attributes.POPUP_TARGET,
42
43
  AUTOCOMPLETE_CONSTANTS.attributes.OPEN,
@@ -79,6 +80,9 @@ let AutocompleteComponent = class AutocompleteComponent extends ListDropdownAwar
79
80
  case AUTOCOMPLETE_CONSTANTS.attributes.FILTER_FOCUS_FIRST:
80
81
  this.filterFocusFirst = coerceBoolean(newValue);
81
82
  break;
83
+ case AUTOCOMPLETE_CONSTANTS.attributes.SELECT_FIRST_OPTION_ON_BLUR:
84
+ this.selectFirstOptionOnBlur = coerceBoolean(newValue);
85
+ break;
82
86
  case AUTOCOMPLETE_CONSTANTS.attributes.ALLOW_UNMATCHED:
83
87
  this.allowUnmatched = coerceBoolean(newValue);
84
88
  break;
@@ -133,6 +137,9 @@ __decorate([
133
137
  __decorate([
134
138
  coreProperty()
135
139
  ], AutocompleteComponent.prototype, "filterFocusFirst", void 0);
140
+ __decorate([
141
+ coreProperty()
142
+ ], AutocompleteComponent.prototype, "selectFirstOptionOnBlur", void 0);
136
143
  __decorate([
137
144
  coreProperty()
138
145
  ], AutocompleteComponent.prototype, "allowUnmatched", void 0);
@@ -376,11 +376,13 @@ export declare class CalendarCore implements ICalendarCore {
376
376
  /**
377
377
  * Sets the month text and attribute in the adapter.
378
378
  * @param userSelected Whether the month was explicitly selected by the user (optional)
379
+ * @param suppressEvent Whether to suppress emitting the month change event (optional)
379
380
  * */
380
381
  private _setMonth;
381
382
  /**
382
383
  * Sets the year text and attribute in the adapter.
383
384
  * @param userSelected Whether the year was explicity selected by the user (optional)
385
+ * @param suppressEvent Whether to suppress emitting the month change event (optional)
384
386
  * */
385
387
  private _setYear;
386
388
  /**
@@ -1183,7 +1183,7 @@ export class CalendarCore {
1183
1183
  else {
1184
1184
  this._month = 0;
1185
1185
  this._year += 1;
1186
- this._setYear();
1186
+ this._setYear(undefined, true);
1187
1187
  }
1188
1188
  this._setMonth();
1189
1189
  this._resetDateGrid();
@@ -1198,7 +1198,7 @@ export class CalendarCore {
1198
1198
  else {
1199
1199
  this._month = 11;
1200
1200
  this._year -= 1;
1201
- this._setYear();
1201
+ this._setYear(undefined, true);
1202
1202
  }
1203
1203
  this._setMonth();
1204
1204
  this._resetDateGrid();
@@ -1315,33 +1315,39 @@ export class CalendarCore {
1315
1315
  /**
1316
1316
  * Sets the month text and attribute in the adapter.
1317
1317
  * @param userSelected Whether the month was explicitly selected by the user (optional)
1318
+ * @param suppressEvent Whether to suppress emitting the month change event (optional)
1318
1319
  * */
1319
- _setMonth(userSelected) {
1320
+ _setMonth(userSelected, suppressEvent) {
1320
1321
  this._adapter.setMonth(this._month, this._locale);
1321
1322
  this._adapter.setHostAttribute(CALENDAR_CONSTANTS.attributes.MONTH, this._month.toString());
1322
1323
  if (this._isInitialized) {
1323
1324
  this._setNavigationButtonStates();
1324
- this._adapter.emitHostEvent(CALENDAR_CONSTANTS.events.MONTH_CHANGE, {
1325
- month: this._month,
1326
- userSelected: userSelected ?? false,
1327
- year: this._year
1328
- });
1325
+ if (!suppressEvent) {
1326
+ this._adapter.emitHostEvent(CALENDAR_CONSTANTS.events.MONTH_CHANGE, {
1327
+ month: this._month,
1328
+ userSelected: userSelected ?? false,
1329
+ year: this._year
1330
+ });
1331
+ }
1329
1332
  }
1330
1333
  }
1331
1334
  /**
1332
1335
  * Sets the year text and attribute in the adapter.
1333
1336
  * @param userSelected Whether the year was explicity selected by the user (optional)
1337
+ * @param suppressEvent Whether to suppress emitting the month change event (optional)
1334
1338
  * */
1335
- _setYear(userSelected) {
1339
+ _setYear(userSelected, suppressEvent) {
1336
1340
  this._adapter.setYear(this._year, this._locale);
1337
1341
  this._adapter.setHostAttribute(CALENDAR_CONSTANTS.attributes.YEAR, this._year.toString());
1338
1342
  if (this._isInitialized) {
1339
1343
  this._setNavigationButtonStates();
1340
- this._adapter.emitHostEvent(CALENDAR_CONSTANTS.events.MONTH_CHANGE, {
1341
- month: this._month,
1342
- userSelected: userSelected ?? false,
1343
- year: this._year
1344
- });
1344
+ if (!suppressEvent) {
1345
+ this._adapter.emitHostEvent(CALENDAR_CONSTANTS.events.MONTH_CHANGE, {
1346
+ month: this._month,
1347
+ userSelected: userSelected ?? false,
1348
+ year: this._year
1349
+ });
1350
+ }
1345
1351
  }
1346
1352
  }
1347
1353
  /**
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright Tyler Technologies, Inc.
4
+ * License: Apache-2.0
5
+ */
6
+ export declare abstract class TestHarness<T extends HTMLElement> {
7
+ element: T;
8
+ constructor(element: T);
9
+ abstract initElementRefs(): void;
10
+ invalidate(): void;
11
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @license
3
+ * Copyright Tyler Technologies, Inc.
4
+ * License: Apache-2.0
5
+ */
6
+ export class TestHarness {
7
+ constructor(element) {
8
+ this.element = element;
9
+ this.initElementRefs();
10
+ }
11
+ invalidate() {
12
+ this.initElementRefs();
13
+ }
14
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright Tyler Technologies, Inc.
4
+ * License: Apache-2.0
5
+ */
6
+ export declare function tryCleanupPopovers(): void;
7
+ export declare function isVisibleInScrollContainer(scrollContainer: HTMLElement, element: HTMLElement): boolean;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @license
3
+ * Copyright Tyler Technologies, Inc.
4
+ * License: Apache-2.0
5
+ */
6
+ export function tryCleanupPopovers() {
7
+ const popovers = Array.from(document.querySelectorAll('forge-popover'));
8
+ popovers.forEach(p => p.remove());
9
+ }
10
+ export function isVisibleInScrollContainer(scrollContainer, element) {
11
+ const elemTop = element.offsetTop - scrollContainer.offsetTop;
12
+ const elemBottom = elemTop + element.offsetHeight;
13
+ return elemTop >= scrollContainer.scrollTop && elemBottom <= scrollContainer.scrollTop + scrollContainer.offsetHeight;
14
+ }
@@ -9,6 +9,7 @@ export * from './dismissible-stack';
9
9
  export * from './event-utils';
10
10
  export * from './feature-detection';
11
11
  export * from './form-utils';
12
+ export * from './key-action';
12
13
  export * from './reflect-utils';
13
14
  export * from './svg-utils';
14
15
  export * from './time-utils';
@@ -9,6 +9,7 @@ export * from './dismissible-stack';
9
9
  export * from './event-utils';
10
10
  export * from './feature-detection';
11
11
  export * from './form-utils';
12
+ export * from './key-action';
12
13
  export * from './reflect-utils';
13
14
  export * from './svg-utils';
14
15
  export * from './time-utils';
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @license
3
+ * Copyright Tyler Technologies, Inc.
4
+ * License: Apache-2.0
5
+ */
6
+ import { ReactiveController, ReactiveControllerHost } from 'lit';
7
+ export type ModifierKey = 'alt' | 'ctrl' | 'meta' | 'shift';
8
+ export type SearchFn = (searchString: string, evt: KeyboardEvent) => void;
9
+ /**
10
+ * Defines a key combination.
11
+ */
12
+ export interface KeyCombination {
13
+ /**
14
+ * The key.
15
+ */
16
+ key: string;
17
+ /**
18
+ * One or more modifier keys that must be pressed in conjunction with the key.
19
+ */
20
+ modifier?: ModifierKey | ModifierKey[];
21
+ }
22
+ /**
23
+ * Configuration interface for a key action.
24
+ */
25
+ export interface IKeyAction {
26
+ /**
27
+ * One or more keys or key combinations that trigger the handler.
28
+ */
29
+ key: string | string[] | KeyCombination | KeyCombination[];
30
+ /**
31
+ * The function to be called when the specified key(s) are pressed.
32
+ * @param event - The keyboard event that triggered the handler.
33
+ * @returns A boolean or void. Returning `true` allows fallthrough to the next key handler.
34
+ */
35
+ handler(event: KeyboardEvent): boolean | void;
36
+ /**
37
+ * Indicates whether the handler should be called repeatedly while the key is held down.
38
+ */
39
+ allowRepeat?: boolean;
40
+ }
41
+ /**
42
+ * Configuration interface for the key action controller.
43
+ */
44
+ export interface IKeyActionControllerConfig {
45
+ /**
46
+ * An optional array of key interaction configurations.
47
+ */
48
+ actions?: IKeyAction[];
49
+ /**
50
+ * An optional search handling function. If not provided, searching is disabled.
51
+ */
52
+ searchHandler?: SearchFn;
53
+ }
54
+ /**
55
+ * A Lit controller for attaching key down actions to a component host.
56
+ *
57
+ * @example
58
+ * class ExampleComponent extends LitElement {
59
+ * private _keyActionController = new KeyActionController(this, {
60
+ * actions: [
61
+ * {
62
+ * key: ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'],
63
+ * handler: this._handleArrowKey.bind(this),
64
+ * allowRepeat: true
65
+ * },
66
+ * { key: 'Enter', handler: this._handleEnterKey.bind(this) },
67
+ * { key: { key: 'a', modifier: 'shift' }, handler: this._handleAKey.bind(this) }
68
+ * ],
69
+ * searchHandler: this.handleSearch.bind(this)
70
+ * });
71
+ *
72
+ * private _handleArrowKey(evt: KeyboardEvent): void {
73
+ * console.log(evt.key);
74
+ * }
75
+ *
76
+ * private _handleEnterKey(evt: KeyboardEvent): void {
77
+ * console.log(evt.key);
78
+ * }
79
+ *
80
+ * private _handleAKey(evt: KeyboardEvent): void {
81
+ * console.log(evt.key);
82
+ * }
83
+ *
84
+ * private _handleSearch(searchString: string): void {
85
+ * console.log(searchString);
86
+ * }
87
+ */
88
+ export declare class KeyActionController implements ReactiveController {
89
+ private static _searchTimeout;
90
+ host: ReactiveControllerHost;
91
+ actions: IKeyAction[];
92
+ searchHandler?: SearchFn;
93
+ private _searchString;
94
+ private _searchTimeout?;
95
+ private _keyDownListener;
96
+ constructor(host: ReactiveControllerHost, config?: IKeyActionControllerConfig);
97
+ hostConnected(): void;
98
+ hostDisconnected(): void;
99
+ private _handleKeyDown;
100
+ private _handleSearch;
101
+ private _eventMatchesKey;
102
+ }