@angular/aria 21.1.0-next.1 → 21.1.0-next.3

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 (81) hide show
  1. package/fesm2022/_accordion-chunk.mjs +108 -0
  2. package/fesm2022/_accordion-chunk.mjs.map +1 -0
  3. package/fesm2022/_combobox-chunk.mjs +425 -0
  4. package/fesm2022/_combobox-chunk.mjs.map +1 -0
  5. package/fesm2022/_combobox-listbox-chunk.mjs +240 -0
  6. package/fesm2022/_combobox-listbox-chunk.mjs.map +1 -0
  7. package/fesm2022/_combobox-tree-chunk.mjs +331 -0
  8. package/fesm2022/_combobox-tree-chunk.mjs.map +1 -0
  9. package/fesm2022/_deferred-content-chunk.mjs +124 -0
  10. package/fesm2022/_deferred-content-chunk.mjs.map +1 -0
  11. package/fesm2022/_expansion-chunk.mjs +41 -0
  12. package/fesm2022/_expansion-chunk.mjs.map +1 -0
  13. package/fesm2022/_list-chunk.mjs +287 -0
  14. package/fesm2022/_list-chunk.mjs.map +1 -0
  15. package/fesm2022/_list-navigation-chunk.mjs +116 -0
  16. package/fesm2022/_list-navigation-chunk.mjs.map +1 -0
  17. package/fesm2022/_menu-chunk.mjs +515 -0
  18. package/fesm2022/_menu-chunk.mjs.map +1 -0
  19. package/fesm2022/_pointer-event-manager-chunk.mjs +54 -0
  20. package/fesm2022/_pointer-event-manager-chunk.mjs.map +1 -0
  21. package/fesm2022/_signal-like-chunk.mjs +118 -0
  22. package/fesm2022/_signal-like-chunk.mjs.map +1 -0
  23. package/fesm2022/_tabs-chunk.mjs +159 -0
  24. package/fesm2022/_tabs-chunk.mjs.map +1 -0
  25. package/fesm2022/_toolbar-widget-group-chunk.mjs +148 -0
  26. package/fesm2022/_toolbar-widget-group-chunk.mjs.map +1 -0
  27. package/fesm2022/_widget-chunk.mjs +5 -246
  28. package/fesm2022/_widget-chunk.mjs.map +1 -1
  29. package/fesm2022/accordion.mjs +73 -55
  30. package/fesm2022/accordion.mjs.map +1 -1
  31. package/fesm2022/aria.mjs +1 -1
  32. package/fesm2022/aria.mjs.map +1 -1
  33. package/fesm2022/combobox.mjs +166 -153
  34. package/fesm2022/combobox.mjs.map +1 -1
  35. package/fesm2022/grid.mjs +287 -261
  36. package/fesm2022/grid.mjs.map +1 -1
  37. package/fesm2022/listbox.mjs +211 -197
  38. package/fesm2022/listbox.mjs.map +1 -1
  39. package/fesm2022/menu.mjs +308 -286
  40. package/fesm2022/menu.mjs.map +1 -1
  41. package/fesm2022/private.mjs +15 -2329
  42. package/fesm2022/private.mjs.map +1 -1
  43. package/fesm2022/tabs.mjs +221 -199
  44. package/fesm2022/tabs.mjs.map +1 -1
  45. package/fesm2022/toolbar.mjs +64 -48
  46. package/fesm2022/toolbar.mjs.map +1 -1
  47. package/fesm2022/tree.mjs +54 -44
  48. package/fesm2022/tree.mjs.map +1 -1
  49. package/package.json +2 -2
  50. package/types/_accordion-chunk.d.ts +100 -0
  51. package/types/_combobox-chunk.d.ts +194 -0
  52. package/types/_deferred-content-chunk.d.ts +42 -0
  53. package/types/_expansion-chunk.d.ts +40 -0
  54. package/types/_grid-chunk.d.ts +43 -250
  55. package/types/_keyboard-event-manager-chunk.d.ts +68 -0
  56. package/types/_list-chunk.d.ts +211 -0
  57. package/types/_list-navigation-chunk.d.ts +119 -0
  58. package/types/_listbox-chunk.d.ts +107 -0
  59. package/types/_menu-chunk.d.ts +267 -0
  60. package/types/_pointer-event-manager-chunk.d.ts +34 -0
  61. package/types/_tabs-chunk.d.ts +153 -0
  62. package/types/_toolbar-chunk.d.ts +124 -0
  63. package/types/_tree-chunk.d.ts +185 -0
  64. package/types/accordion.d.ts +65 -54
  65. package/types/aria.d.ts +1 -1
  66. package/types/combobox.d.ts +86 -56
  67. package/types/grid.d.ts +47 -32
  68. package/types/listbox.d.ts +22 -7
  69. package/types/menu.d.ts +135 -117
  70. package/types/private.d.ts +28 -1376
  71. package/types/tabs.d.ts +109 -88
  72. package/types/toolbar.d.ts +77 -66
  73. package/types/tree.d.ts +118 -104
  74. package/_adev_assets/aria-accordion.json +0 -743
  75. package/_adev_assets/aria-combobox.json +0 -603
  76. package/_adev_assets/aria-grid.json +0 -893
  77. package/_adev_assets/aria-listbox.json +0 -540
  78. package/_adev_assets/aria-menu.json +0 -1049
  79. package/_adev_assets/aria-tabs.json +0 -880
  80. package/_adev_assets/aria-toolbar.json +0 -545
  81. package/_adev_assets/aria-tree.json +0 -853
@@ -1,2331 +1,17 @@
1
- import * as i0 from '@angular/core';
2
- import { signal, computed, model, Directive, inject, TemplateRef, ViewContainerRef, afterRenderEffect } from '@angular/core';
3
- import { PointerEventManager, KeyboardEventManager, ListFocus, ListNavigation, Modifier } from './_widget-chunk.mjs';
1
+ export { ComboboxDialogPattern, ComboboxPattern } from './_combobox-chunk.mjs';
2
+ export { ComboboxListboxPattern, ListboxPattern, OptionPattern } from './_combobox-listbox-chunk.mjs';
3
+ export { MenuBarPattern, MenuItemPattern, MenuPattern, MenuTriggerPattern } from './_menu-chunk.mjs';
4
+ export { untracked } from '@angular/core/primitives/signals';
5
+ export { computed, convertGetterSetterToWritableSignalLike, linkedSignal, signal } from './_signal-like-chunk.mjs';
6
+ export { TabListPattern, TabPanelPattern, TabPattern } from './_tabs-chunk.mjs';
7
+ export { ToolbarPattern, ToolbarWidgetGroupPattern, ToolbarWidgetPattern } from './_toolbar-widget-group-chunk.mjs';
8
+ export { AccordionGroupPattern, AccordionPanelPattern, AccordionTriggerPattern } from './_accordion-chunk.mjs';
9
+ export { ComboboxTreePattern, TreeItemPattern, TreePattern } from './_combobox-tree-chunk.mjs';
4
10
  export { GridCellPattern, GridCellWidgetPattern, GridPattern, GridRowPattern } from './_widget-chunk.mjs';
5
-
6
- class ComboboxPattern {
7
- inputs;
8
- expanded = signal(false);
9
- disabled = () => this.inputs.disabled();
10
- activeDescendant = computed(() => {
11
- const popupControls = this.inputs.popupControls();
12
- if (popupControls instanceof ComboboxDialogPattern) {
13
- return null;
14
- }
15
- return popupControls?.activeId() ?? null;
16
- });
17
- highlightedItem = signal(undefined);
18
- isDeleting = false;
19
- isFocused = signal(false);
20
- hasBeenFocused = signal(false);
21
- expandKey = computed(() => this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight');
22
- collapseKey = computed(() => this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft');
23
- popupId = computed(() => this.inputs.popupControls()?.id() || null);
24
- autocomplete = computed(() => this.inputs.filterMode() === 'highlight' ? 'both' : 'list');
25
- hasPopup = computed(() => this.inputs.popupControls()?.role() || null);
26
- readonly = computed(() => this.inputs.readonly() || this.inputs.disabled() || null);
27
- listControls = () => {
28
- const popupControls = this.inputs.popupControls();
29
- if (popupControls instanceof ComboboxDialogPattern) {
30
- return null;
31
- }
32
- return popupControls;
33
- };
34
- treeControls = () => {
35
- const popupControls = this.inputs.popupControls();
36
- if (popupControls?.role() === 'tree') {
37
- return popupControls;
38
- }
39
- return null;
40
- };
41
- keydown = computed(() => {
42
- const manager = new KeyboardEventManager();
43
- const popupControls = this.inputs.popupControls();
44
- if (!popupControls) {
45
- return manager;
46
- }
47
- if (popupControls instanceof ComboboxDialogPattern) {
48
- if (!this.expanded()) {
49
- manager.on('ArrowUp', () => this.open()).on('ArrowDown', () => this.open());
50
- if (this.readonly()) {
51
- manager.on('Enter', () => this.open()).on(' ', () => this.open());
52
- }
53
- }
54
- return manager;
55
- }
56
- if (!this.inputs.alwaysExpanded()) {
57
- manager.on('Escape', () => this.close({
58
- reset: !this.readonly()
59
- }));
60
- }
61
- if (!this.expanded()) {
62
- manager.on('ArrowDown', () => this.open({
63
- first: true
64
- })).on('ArrowUp', () => this.open({
65
- last: true
66
- }));
67
- if (this.readonly()) {
68
- manager.on('Enter', () => this.open({
69
- selected: true
70
- })).on(' ', () => this.open({
71
- selected: true
72
- }));
73
- }
74
- return manager;
75
- }
76
- manager.on('ArrowDown', () => this.next()).on('ArrowUp', () => this.prev()).on('Home', () => this.first()).on('End', () => this.last());
77
- if (this.readonly()) {
78
- manager.on(' ', () => this.select({
79
- commit: true,
80
- close: !popupControls.multi()
81
- }));
82
- }
83
- if (popupControls.role() === 'listbox') {
84
- manager.on('Enter', () => {
85
- this.select({
86
- commit: true,
87
- close: !popupControls.multi()
88
- });
89
- });
90
- }
91
- const treeControls = this.treeControls();
92
- if (treeControls?.isItemSelectable()) {
93
- manager.on('Enter', () => this.select({
94
- commit: true,
95
- close: true
96
- }));
97
- }
98
- if (treeControls?.isItemExpandable()) {
99
- manager.on(this.expandKey(), () => this.expandItem()).on(this.collapseKey(), () => this.collapseItem());
100
- if (!treeControls.isItemSelectable()) {
101
- manager.on('Enter', () => this.expandItem());
102
- }
103
- }
104
- if (treeControls?.isItemCollapsible()) {
105
- manager.on(this.collapseKey(), () => this.collapseItem());
106
- }
107
- return manager;
108
- });
109
- click = computed(() => new PointerEventManager().on(e => {
110
- if (e.target === this.inputs.inputEl()) {
111
- if (this.readonly()) {
112
- this.expanded() ? this.close() : this.open({
113
- selected: true
114
- });
115
- }
116
- }
117
- const controls = this.inputs.popupControls();
118
- if (controls instanceof ComboboxDialogPattern) {
119
- return;
120
- }
121
- const item = controls?.getItem(e);
122
- if (item) {
123
- if (controls?.role() === 'tree') {
124
- const treeControls = controls;
125
- if (treeControls.isItemExpandable(item) && !treeControls.isItemSelectable(item)) {
126
- treeControls.toggleExpansion(item);
127
- this.inputs.inputEl()?.focus();
128
- return;
129
- }
130
- }
131
- this.select({
132
- item,
133
- commit: true,
134
- close: !controls?.multi()
135
- });
136
- this.inputs.inputEl()?.focus();
137
- }
138
- }));
139
- constructor(inputs) {
140
- this.inputs = inputs;
141
- }
142
- onKeydown(event) {
143
- if (!this.inputs.disabled()) {
144
- this.keydown().handle(event);
145
- }
146
- }
147
- onClick(event) {
148
- if (!this.inputs.disabled()) {
149
- this.click().handle(event);
150
- }
151
- }
152
- onInput(event) {
153
- if (this.inputs.disabled() || this.inputs.readonly()) {
154
- return;
155
- }
156
- const inputEl = this.inputs.inputEl();
157
- if (!inputEl) {
158
- return;
159
- }
160
- const popupControls = this.inputs.popupControls();
161
- if (popupControls instanceof ComboboxDialogPattern) {
162
- return;
163
- }
164
- this.open();
165
- this.inputs.inputValue?.set(inputEl.value);
166
- this.isDeleting = event instanceof InputEvent && !!event.inputType.match(/^delete/);
167
- if (this.inputs.filterMode() === 'highlight' && !this.isDeleting) {
168
- this.highlight();
169
- }
170
- }
171
- onFocusIn() {
172
- if (this.inputs.alwaysExpanded() && !this.hasBeenFocused()) {
173
- const firstSelectedItem = this.listControls()?.getSelectedItems()[0];
174
- firstSelectedItem ? this.listControls()?.focus(firstSelectedItem) : this.first();
175
- }
176
- this.isFocused.set(true);
177
- this.hasBeenFocused.set(true);
178
- }
179
- onFocusOut(event) {
180
- if (this.inputs.disabled()) {
181
- return;
182
- }
183
- const popupControls = this.inputs.popupControls();
184
- if (popupControls instanceof ComboboxDialogPattern) {
185
- return;
186
- }
187
- if (!(event.relatedTarget instanceof HTMLElement) || !this.inputs.containerEl()?.contains(event.relatedTarget)) {
188
- this.isFocused.set(false);
189
- if (this.readonly()) {
190
- this.close();
191
- return;
192
- }
193
- if (this.inputs.filterMode() !== 'manual') {
194
- this.commit();
195
- } else {
196
- const item = popupControls?.items().find(i => i.searchTerm() === this.inputs.inputEl()?.value);
197
- if (item) {
198
- this.select({
199
- item
200
- });
201
- }
202
- }
203
- this.close();
204
- }
205
- }
206
- firstMatch = computed(() => {
207
- if (this.listControls()?.role() === 'listbox') {
208
- return this.listControls()?.items()[0];
209
- }
210
- return this.listControls()?.items().find(i => i.value() === this.inputs.firstMatch());
211
- });
212
- onFilter() {
213
- if (this.readonly()) {
214
- return;
215
- }
216
- const popupControls = this.inputs.popupControls();
217
- if (popupControls instanceof ComboboxDialogPattern) {
218
- return;
219
- }
220
- const isInitialRender = !this.inputs.inputValue?.().length && !this.isDeleting;
221
- if (isInitialRender) {
222
- return;
223
- }
224
- if (!this.isFocused()) {
225
- return;
226
- }
227
- if (this.inputs.popupControls()?.role() === 'tree') {
228
- const treeControls = this.inputs.popupControls();
229
- this.inputs.inputValue?.().length ? treeControls.expandAll() : treeControls.collapseAll();
230
- }
231
- const item = this.firstMatch();
232
- if (!item) {
233
- popupControls?.clearSelection();
234
- popupControls?.unfocus();
235
- return;
236
- }
237
- popupControls?.focus(item);
238
- if (this.inputs.filterMode() !== 'manual') {
239
- this.select({
240
- item
241
- });
242
- }
243
- if (this.inputs.filterMode() === 'highlight' && !this.isDeleting) {
244
- this.highlight();
245
- }
246
- }
247
- highlight() {
248
- const inputEl = this.inputs.inputEl();
249
- const selectedItems = this.listControls()?.getSelectedItems();
250
- const item = selectedItems?.[0];
251
- if (!inputEl || !item) {
252
- return;
253
- }
254
- const isHighlightable = item.searchTerm().toLowerCase().startsWith(this.inputs.inputValue().toLowerCase());
255
- if (isHighlightable) {
256
- inputEl.value = this.inputs.inputValue() + item.searchTerm().slice(this.inputs.inputValue().length);
257
- inputEl.setSelectionRange(this.inputs.inputValue().length, item.searchTerm().length);
258
- this.highlightedItem.set(item);
259
- }
260
- }
261
- close(opts) {
262
- const popupControls = this.inputs.popupControls();
263
- if (this.inputs.alwaysExpanded()) {
264
- return;
265
- }
266
- if (popupControls instanceof ComboboxDialogPattern) {
267
- this.expanded.set(false);
268
- return;
269
- }
270
- if (this.readonly()) {
271
- this.expanded.set(false);
272
- popupControls?.unfocus();
273
- return;
274
- }
275
- if (!opts?.reset) {
276
- if (this.inputs.filterMode() === 'manual') {
277
- if (!this.listControls()?.items().some(i => i.searchTerm() === this.inputs.inputEl()?.value)) {
278
- this.listControls()?.clearSelection();
279
- }
280
- }
281
- this.expanded.set(false);
282
- popupControls?.unfocus();
283
- return;
284
- }
285
- if (!this.expanded()) {
286
- this.inputs.inputValue?.set('');
287
- popupControls?.clearSelection();
288
- const inputEl = this.inputs.inputEl();
289
- if (inputEl) {
290
- inputEl.value = '';
291
- }
292
- } else if (this.expanded()) {
293
- this.expanded.set(false);
294
- const selectedItem = popupControls?.getSelectedItems()?.[0];
295
- if (selectedItem?.searchTerm() !== this.inputs.inputValue()) {
296
- popupControls?.clearSelection();
297
- }
298
- return;
299
- }
300
- this.close();
301
- if (!this.readonly()) {
302
- popupControls?.clearSelection();
303
- }
304
- }
305
- open(nav) {
306
- this.expanded.set(true);
307
- const popupControls = this.inputs.popupControls();
308
- if (popupControls instanceof ComboboxDialogPattern) {
309
- return;
310
- }
311
- const inputEl = this.inputs.inputEl();
312
- if (inputEl && this.inputs.filterMode() === 'highlight') {
313
- const isHighlighting = inputEl.selectionStart !== inputEl.value.length;
314
- this.inputs.inputValue?.set(inputEl.value.slice(0, inputEl.selectionStart || 0));
315
- if (!isHighlighting) {
316
- this.highlightedItem.set(undefined);
317
- }
318
- }
319
- if (nav?.first) {
320
- this.first();
321
- }
322
- if (nav?.last) {
323
- this.last();
324
- }
325
- if (nav?.selected) {
326
- const selectedItem = popupControls?.items().find(i => popupControls?.getSelectedItems().includes(i));
327
- if (selectedItem) {
328
- popupControls?.focus(selectedItem);
329
- }
330
- }
331
- }
332
- next() {
333
- this._navigate(() => this.listControls()?.next());
334
- }
335
- prev() {
336
- this._navigate(() => this.listControls()?.prev());
337
- }
338
- first() {
339
- this._navigate(() => this.listControls()?.first());
340
- }
341
- last() {
342
- this._navigate(() => this.listControls()?.last());
343
- }
344
- collapseItem() {
345
- const controls = this.inputs.popupControls();
346
- this._navigate(() => controls?.collapseItem());
347
- }
348
- expandItem() {
349
- const controls = this.inputs.popupControls();
350
- this._navigate(() => controls?.expandItem());
351
- }
352
- select(opts = {}) {
353
- const controls = this.listControls();
354
- if (opts.item) {
355
- controls?.focus(opts.item, {
356
- focusElement: false
357
- });
358
- }
359
- controls?.multi() ? controls.toggle(opts.item) : controls?.select(opts.item);
360
- if (opts.commit) {
361
- this.commit();
362
- }
363
- if (opts.close) {
364
- this.close();
365
- }
366
- }
367
- commit() {
368
- const inputEl = this.inputs.inputEl();
369
- const selectedItems = this.listControls()?.getSelectedItems();
370
- if (!inputEl) {
371
- return;
372
- }
373
- inputEl.value = selectedItems?.map(i => i.searchTerm()).join(', ') || '';
374
- this.inputs.inputValue?.set(inputEl.value);
375
- if (this.inputs.filterMode() === 'highlight' && !this.readonly()) {
376
- const length = inputEl.value.length;
377
- inputEl.setSelectionRange(length, length);
378
- }
379
- }
380
- _navigate(operation) {
381
- operation();
382
- if (this.inputs.filterMode() !== 'manual') {
383
- this.select();
384
- }
385
- if (this.inputs.filterMode() === 'highlight') {
386
- const selectedItem = this.listControls()?.getSelectedItems()[0];
387
- if (!selectedItem) {
388
- return;
389
- }
390
- if (selectedItem === this.highlightedItem()) {
391
- this.highlight();
392
- } else {
393
- const inputEl = this.inputs.inputEl();
394
- inputEl.value = selectedItem?.searchTerm();
395
- }
396
- }
397
- }
398
- }
399
- class ComboboxDialogPattern {
400
- inputs;
401
- id = () => this.inputs.id();
402
- role = () => 'dialog';
403
- keydown = computed(() => {
404
- return new KeyboardEventManager().on('Escape', () => this.inputs.combobox.close());
405
- });
406
- constructor(inputs) {
407
- this.inputs = inputs;
408
- }
409
- onKeydown(event) {
410
- this.keydown().handle(event);
411
- }
412
- onClick(event) {
413
- if (event.target === this.inputs.element()) {
414
- this.inputs.combobox.close();
415
- }
416
- }
417
- }
418
-
419
- class ListSelection {
420
- inputs;
421
- rangeStartIndex = signal(0);
422
- rangeEndIndex = signal(0);
423
- selectedItems = computed(() => this.inputs.items().filter(item => this.inputs.values().includes(item.value())));
424
- constructor(inputs) {
425
- this.inputs = inputs;
426
- }
427
- select(item, opts = {
428
- anchor: true
429
- }) {
430
- item = item ?? this.inputs.focusManager.inputs.activeItem();
431
- if (!item || item.disabled() || !item.selectable() || this.inputs.values().includes(item.value())) {
432
- return;
433
- }
434
- if (!this.inputs.multi()) {
435
- this.deselectAll();
436
- }
437
- const index = this.inputs.items().findIndex(i => i === item);
438
- if (opts.anchor) {
439
- this.beginRangeSelection(index);
440
- }
441
- this.inputs.values.update(values => values.concat(item.value()));
442
- }
443
- deselect(item) {
444
- item = item ?? this.inputs.focusManager.inputs.activeItem();
445
- if (item && !item.disabled() && item.selectable()) {
446
- this.inputs.values.update(values => values.filter(value => value !== item.value()));
447
- }
448
- }
449
- toggle(item) {
450
- item = item ?? this.inputs.focusManager.inputs.activeItem();
451
- if (item) {
452
- this.inputs.values().includes(item.value()) ? this.deselect(item) : this.select(item);
453
- }
454
- }
455
- toggleOne() {
456
- const item = this.inputs.focusManager.inputs.activeItem();
457
- if (item) {
458
- this.inputs.values().includes(item.value()) ? this.deselect() : this.selectOne();
459
- }
460
- }
461
- selectAll() {
462
- if (!this.inputs.multi()) {
463
- return;
464
- }
465
- for (const item of this.inputs.items()) {
466
- this.select(item, {
467
- anchor: false
468
- });
469
- }
470
- this.beginRangeSelection();
471
- }
472
- deselectAll() {
473
- for (const value of this.inputs.values()) {
474
- const item = this.inputs.items().find(i => i.value() === value);
475
- item ? this.deselect(item) : this.inputs.values.update(values => values.filter(v => v !== value));
476
- }
477
- }
478
- toggleAll() {
479
- const selectableValues = this.inputs.items().filter(i => !i.disabled() && i.selectable()).map(i => i.value());
480
- selectableValues.every(i => this.inputs.values().includes(i)) ? this.deselectAll() : this.selectAll();
481
- }
482
- selectOne() {
483
- const item = this.inputs.focusManager.inputs.activeItem();
484
- if (item && (item.disabled() || !item.selectable())) {
485
- return;
486
- }
487
- this.deselectAll();
488
- if (this.inputs.values().length > 0 && !this.inputs.multi()) {
489
- return;
490
- }
491
- this.select();
492
- }
493
- selectRange(opts = {
494
- anchor: true
495
- }) {
496
- const isStartOfRange = this.inputs.focusManager.prevActiveIndex() === this.rangeStartIndex();
497
- if (isStartOfRange && opts.anchor) {
498
- this.beginRangeSelection(this.inputs.focusManager.prevActiveIndex());
499
- }
500
- const itemsInRange = this._getItemsFromIndex(this.rangeStartIndex());
501
- const itemsOutOfRange = this._getItemsFromIndex(this.rangeEndIndex()).filter(i => !itemsInRange.includes(i));
502
- for (const item of itemsOutOfRange) {
503
- this.deselect(item);
504
- }
505
- for (const item of itemsInRange) {
506
- this.select(item, {
507
- anchor: false
508
- });
509
- }
510
- if (itemsInRange.length) {
511
- const item = itemsInRange.pop();
512
- const index = this.inputs.items().findIndex(i => i === item);
513
- this.rangeEndIndex.set(index);
514
- }
515
- }
516
- beginRangeSelection(index = this.inputs.focusManager.activeIndex()) {
517
- this.rangeStartIndex.set(index);
518
- this.rangeEndIndex.set(index);
519
- }
520
- _getItemsFromIndex(index) {
521
- if (index === -1) {
522
- return [];
523
- }
524
- const upper = Math.max(this.inputs.focusManager.activeIndex(), index);
525
- const lower = Math.min(this.inputs.focusManager.activeIndex(), index);
526
- const items = [];
527
- for (let i = lower; i <= upper; i++) {
528
- items.push(this.inputs.items()[i]);
529
- }
530
- if (this.inputs.focusManager.activeIndex() < index) {
531
- return items.reverse();
532
- }
533
- return items;
534
- }
535
- }
536
-
537
- class ListTypeahead {
538
- inputs;
539
- timeout;
540
- focusManager;
541
- isTyping = computed(() => this._query().length > 0);
542
- _query = signal('');
543
- _startIndex = signal(undefined);
544
- constructor(inputs) {
545
- this.inputs = inputs;
546
- this.focusManager = inputs.focusManager;
547
- }
548
- search(char) {
549
- if (char.length !== 1) {
550
- return false;
551
- }
552
- if (!this.isTyping() && char === ' ') {
553
- return false;
554
- }
555
- if (this._startIndex() === undefined) {
556
- this._startIndex.set(this.focusManager.activeIndex());
557
- }
558
- clearTimeout(this.timeout);
559
- this._query.update(q => q + char.toLowerCase());
560
- const item = this._getItem();
561
- if (item) {
562
- this.focusManager.focus(item);
563
- }
564
- this.timeout = setTimeout(() => {
565
- this._query.set('');
566
- this._startIndex.set(undefined);
567
- }, this.inputs.typeaheadDelay());
568
- return true;
569
- }
570
- _getItem() {
571
- let items = this.focusManager.inputs.items();
572
- const after = items.slice(this._startIndex() + 1);
573
- const before = items.slice(0, this._startIndex());
574
- items = after.concat(before);
575
- items.push(this.inputs.items()[this._startIndex()]);
576
- const focusableItems = [];
577
- for (const item of items) {
578
- if (this.focusManager.isFocusable(item)) {
579
- focusableItems.push(item);
580
- }
581
- }
582
- return focusableItems.find(i => i.searchTerm().toLowerCase().startsWith(this._query()));
583
- }
584
- }
585
-
586
- class List {
587
- inputs;
588
- navigationBehavior;
589
- selectionBehavior;
590
- typeaheadBehavior;
591
- focusBehavior;
592
- disabled = computed(() => this.focusBehavior.isListDisabled());
593
- activeDescendant = computed(() => this.focusBehavior.getActiveDescendant());
594
- tabIndex = computed(() => this.focusBehavior.getListTabIndex());
595
- activeIndex = computed(() => this.focusBehavior.activeIndex());
596
- _anchorIndex = signal(0);
597
- _wrap = signal(true);
598
- constructor(inputs) {
599
- this.inputs = inputs;
600
- this.focusBehavior = new ListFocus(inputs);
601
- this.selectionBehavior = new ListSelection({
602
- ...inputs,
603
- focusManager: this.focusBehavior
604
- });
605
- this.typeaheadBehavior = new ListTypeahead({
606
- ...inputs,
607
- focusManager: this.focusBehavior
608
- });
609
- this.navigationBehavior = new ListNavigation({
610
- ...inputs,
611
- focusManager: this.focusBehavior,
612
- wrap: computed(() => this._wrap() && this.inputs.wrap())
613
- });
614
- }
615
- getItemTabindex(item) {
616
- return this.focusBehavior.getItemTabIndex(item);
617
- }
618
- first(opts) {
619
- this._navigate(opts, () => this.navigationBehavior.first(opts));
620
- }
621
- last(opts) {
622
- this._navigate(opts, () => this.navigationBehavior.last(opts));
623
- }
624
- next(opts) {
625
- this._navigate(opts, () => this.navigationBehavior.next(opts));
626
- }
627
- prev(opts) {
628
- this._navigate(opts, () => this.navigationBehavior.prev(opts));
629
- }
630
- goto(item, opts) {
631
- this._navigate(opts, () => this.navigationBehavior.goto(item, opts));
632
- }
633
- unfocus() {
634
- this.inputs.activeItem.set(undefined);
635
- }
636
- anchor(index) {
637
- this._anchorIndex.set(index);
638
- }
639
- search(char, opts) {
640
- this._navigate(opts, () => this.typeaheadBehavior.search(char));
641
- }
642
- isTyping() {
643
- return this.typeaheadBehavior.isTyping();
644
- }
645
- select(item) {
646
- this.selectionBehavior.select(item);
647
- }
648
- selectOne() {
649
- this.selectionBehavior.selectOne();
650
- }
651
- deselect(item) {
652
- this.selectionBehavior.deselect(item);
653
- }
654
- deselectAll() {
655
- this.selectionBehavior.deselectAll();
656
- }
657
- toggle(item) {
658
- this.selectionBehavior.toggle(item);
659
- }
660
- toggleOne() {
661
- this.selectionBehavior.toggleOne();
662
- }
663
- toggleAll() {
664
- this.selectionBehavior.toggleAll();
665
- }
666
- isFocusable(item) {
667
- return this.focusBehavior.isFocusable(item);
668
- }
669
- updateSelection(opts = {
670
- anchor: true
671
- }) {
672
- if (opts.toggle) {
673
- this.selectionBehavior.toggle();
674
- }
675
- if (opts.select) {
676
- this.selectionBehavior.select();
677
- }
678
- if (opts.selectOne) {
679
- this.selectionBehavior.selectOne();
680
- }
681
- if (opts.selectRange) {
682
- this.selectionBehavior.selectRange();
683
- }
684
- if (!opts.anchor) {
685
- this.anchor(this.selectionBehavior.rangeStartIndex());
686
- }
687
- }
688
- _navigate(opts = {}, operation) {
689
- if (opts?.selectRange) {
690
- this._wrap.set(false);
691
- this.selectionBehavior.rangeStartIndex.set(this._anchorIndex());
692
- }
693
- const moved = operation();
694
- if (moved) {
695
- this.updateSelection(opts);
696
- }
697
- this._wrap.set(true);
698
- }
699
- }
700
-
701
- class ListboxPattern {
702
- inputs;
703
- listBehavior;
704
- orientation;
705
- disabled = computed(() => this.listBehavior.disabled());
706
- readonly;
707
- tabIndex = computed(() => this.listBehavior.tabIndex());
708
- activeDescendant = computed(() => this.listBehavior.activeDescendant());
709
- multi;
710
- setsize = computed(() => this.inputs.items().length);
711
- followFocus = computed(() => this.inputs.selectionMode() === 'follow');
712
- wrap = signal(true);
713
- prevKey = computed(() => {
714
- if (this.inputs.orientation() === 'vertical') {
715
- return 'ArrowUp';
716
- }
717
- return this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
718
- });
719
- nextKey = computed(() => {
720
- if (this.inputs.orientation() === 'vertical') {
721
- return 'ArrowDown';
722
- }
723
- return this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
724
- });
725
- dynamicSpaceKey = computed(() => this.listBehavior.isTyping() ? '' : ' ');
726
- typeaheadRegexp = /^.$/;
727
- keydown = computed(() => {
728
- const manager = new KeyboardEventManager();
729
- if (this.readonly()) {
730
- return manager.on(this.prevKey, () => this.listBehavior.prev()).on(this.nextKey, () => this.listBehavior.next()).on('Home', () => this.listBehavior.first()).on('End', () => this.listBehavior.last()).on(this.typeaheadRegexp, e => this.listBehavior.search(e.key));
731
- }
732
- if (!this.followFocus()) {
733
- manager.on(this.prevKey, () => this.listBehavior.prev()).on(this.nextKey, () => this.listBehavior.next()).on('Home', () => this.listBehavior.first()).on('End', () => this.listBehavior.last()).on(this.typeaheadRegexp, e => this.listBehavior.search(e.key));
734
- }
735
- if (this.followFocus()) {
736
- manager.on(this.prevKey, () => this.listBehavior.prev({
737
- selectOne: true
738
- })).on(this.nextKey, () => this.listBehavior.next({
739
- selectOne: true
740
- })).on('Home', () => this.listBehavior.first({
741
- selectOne: true
742
- })).on('End', () => this.listBehavior.last({
743
- selectOne: true
744
- })).on(this.typeaheadRegexp, e => this.listBehavior.search(e.key, {
745
- selectOne: true
746
- }));
747
- }
748
- if (this.inputs.multi()) {
749
- manager.on(Modifier.Any, 'Shift', () => this.listBehavior.anchor(this.listBehavior.activeIndex())).on(Modifier.Shift, this.prevKey, () => this.listBehavior.prev({
750
- selectRange: true
751
- })).on(Modifier.Shift, this.nextKey, () => this.listBehavior.next({
752
- selectRange: true
753
- })).on([Modifier.Ctrl | Modifier.Shift, Modifier.Meta | Modifier.Shift], 'Home', () => this.listBehavior.first({
754
- selectRange: true,
755
- anchor: false
756
- })).on([Modifier.Ctrl | Modifier.Shift, Modifier.Meta | Modifier.Shift], 'End', () => this.listBehavior.last({
757
- selectRange: true,
758
- anchor: false
759
- })).on(Modifier.Shift, 'Enter', () => this.listBehavior.updateSelection({
760
- selectRange: true,
761
- anchor: false
762
- })).on(Modifier.Shift, this.dynamicSpaceKey, () => this.listBehavior.updateSelection({
763
- selectRange: true,
764
- anchor: false
765
- }));
766
- }
767
- if (!this.followFocus() && this.inputs.multi()) {
768
- manager.on(this.dynamicSpaceKey, () => this.listBehavior.toggle()).on('Enter', () => this.listBehavior.toggle()).on([Modifier.Ctrl, Modifier.Meta], 'A', () => this.listBehavior.toggleAll());
769
- }
770
- if (!this.followFocus() && !this.inputs.multi()) {
771
- manager.on(this.dynamicSpaceKey, () => this.listBehavior.toggleOne());
772
- manager.on('Enter', () => this.listBehavior.toggleOne());
773
- }
774
- if (this.inputs.multi() && this.followFocus()) {
775
- manager.on([Modifier.Ctrl, Modifier.Meta], this.prevKey, () => this.listBehavior.prev()).on([Modifier.Ctrl, Modifier.Meta], this.nextKey, () => this.listBehavior.next()).on([Modifier.Ctrl, Modifier.Meta], ' ', () => this.listBehavior.toggle()).on([Modifier.Ctrl, Modifier.Meta], 'Enter', () => this.listBehavior.toggle()).on([Modifier.Ctrl, Modifier.Meta], 'Home', () => this.listBehavior.first()).on([Modifier.Ctrl, Modifier.Meta], 'End', () => this.listBehavior.last()).on([Modifier.Ctrl, Modifier.Meta], 'A', () => {
776
- this.listBehavior.toggleAll();
777
- this.listBehavior.select();
778
- });
779
- }
780
- return manager;
781
- });
782
- pointerdown = computed(() => {
783
- const manager = new PointerEventManager();
784
- if (this.readonly()) {
785
- return manager.on(e => this.listBehavior.goto(this._getItem(e)));
786
- }
787
- if (this.multi()) {
788
- manager.on(Modifier.Shift, e => this.listBehavior.goto(this._getItem(e), {
789
- selectRange: true
790
- }));
791
- }
792
- if (!this.multi() && this.followFocus()) {
793
- return manager.on(e => this.listBehavior.goto(this._getItem(e), {
794
- selectOne: true
795
- }));
796
- }
797
- if (!this.multi() && !this.followFocus()) {
798
- return manager.on(e => this.listBehavior.goto(this._getItem(e), {
799
- toggle: true
800
- }));
801
- }
802
- if (this.multi() && this.followFocus()) {
803
- return manager.on(e => this.listBehavior.goto(this._getItem(e), {
804
- selectOne: true
805
- })).on(Modifier.Ctrl, e => this.listBehavior.goto(this._getItem(e), {
806
- toggle: true
807
- }));
808
- }
809
- if (this.multi() && !this.followFocus()) {
810
- return manager.on(e => this.listBehavior.goto(this._getItem(e), {
811
- toggle: true
812
- }));
813
- }
814
- return manager;
815
- });
816
- constructor(inputs) {
817
- this.inputs = inputs;
818
- this.readonly = inputs.readonly;
819
- this.orientation = inputs.orientation;
820
- this.multi = inputs.multi;
821
- this.listBehavior = new List(inputs);
822
- }
823
- validate() {
824
- const violations = [];
825
- if (!this.inputs.multi() && this.inputs.values().length > 1) {
826
- violations.push(`A single-select listbox should not have multiple selected options. Selected options: ${this.inputs.values().join(', ')}`);
827
- }
828
- return violations;
829
- }
830
- onKeydown(event) {
831
- if (!this.disabled()) {
832
- this.keydown().handle(event);
833
- }
834
- }
835
- onPointerdown(event) {
836
- if (!this.disabled()) {
837
- this.pointerdown().handle(event);
838
- }
839
- }
840
- setDefaultState() {
841
- let firstItem = null;
842
- for (const item of this.inputs.items()) {
843
- if (this.listBehavior.isFocusable(item)) {
844
- if (!firstItem) {
845
- firstItem = item;
846
- }
847
- if (item.selected()) {
848
- this.inputs.activeItem.set(item);
849
- return;
850
- }
851
- }
852
- }
853
- if (firstItem) {
854
- this.inputs.activeItem.set(firstItem);
855
- }
856
- }
857
- _getItem(e) {
858
- if (!(e.target instanceof HTMLElement)) {
859
- return;
860
- }
861
- const element = e.target.closest('[role="option"]');
862
- return this.inputs.items().find(i => i.element() === element);
863
- }
864
- }
865
-
866
- class OptionPattern {
867
- id;
868
- value;
869
- index = computed(() => this.listbox()?.inputs.items().indexOf(this) ?? -1);
870
- active = computed(() => this.listbox()?.inputs.activeItem() === this);
871
- selected = computed(() => this.listbox()?.inputs.values().includes(this.value()));
872
- selectable = () => true;
873
- disabled;
874
- searchTerm;
875
- listbox;
876
- tabIndex = computed(() => this.listbox()?.listBehavior.getItemTabindex(this));
877
- element;
878
- constructor(args) {
879
- this.id = args.id;
880
- this.value = args.value;
881
- this.listbox = args.listbox;
882
- this.element = args.element;
883
- this.disabled = args.disabled;
884
- this.searchTerm = args.searchTerm;
885
- }
886
- }
887
-
888
- class ComboboxListboxPattern extends ListboxPattern {
889
- inputs;
890
- id = computed(() => this.inputs.id());
891
- role = computed(() => 'listbox');
892
- activeId = computed(() => this.listBehavior.activeDescendant());
893
- items = computed(() => this.inputs.items());
894
- tabIndex = () => -1;
895
- multi = computed(() => {
896
- return this.inputs.combobox()?.readonly() ? this.inputs.multi() : false;
897
- });
898
- constructor(inputs) {
899
- if (inputs.combobox()) {
900
- inputs.focusMode = () => 'activedescendant';
901
- inputs.element = inputs.combobox().inputs.inputEl;
902
- }
903
- super(inputs);
904
- this.inputs = inputs;
905
- }
906
- onKeydown(_) {}
907
- onPointerdown(_) {}
908
- setDefaultState() {}
909
- focus = (item, opts) => {
910
- this.listBehavior.goto(item, opts);
911
- };
912
- getActiveItem = () => this.inputs.activeItem();
913
- next = () => this.listBehavior.next();
914
- prev = () => this.listBehavior.prev();
915
- last = () => this.listBehavior.last();
916
- first = () => this.listBehavior.first();
917
- unfocus = () => this.listBehavior.unfocus();
918
- select = item => this.listBehavior.select(item);
919
- toggle = item => this.listBehavior.toggle(item);
920
- clearSelection = () => this.listBehavior.deselectAll();
921
- getItem = e => this._getItem(e);
922
- getSelectedItems = () => {
923
- const items = [];
924
- for (const value of this.inputs.values()) {
925
- const item = this.items().find(i => i.value() === value);
926
- if (item) {
927
- items.push(item);
928
- }
929
- }
930
- return items;
931
- };
932
- setValue = value => this.inputs.values.set(value ? [value] : []);
933
- }
934
-
935
- class MenuPattern {
936
- inputs;
937
- id;
938
- role = () => 'menu';
939
- disabled = () => this.inputs.disabled();
940
- visible = computed(() => this.inputs.parent() ? !!this.inputs.parent()?.expanded() : true);
941
- listBehavior;
942
- isFocused = signal(false);
943
- hasBeenFocused = signal(false);
944
- hasBeenHovered = signal(false);
945
- _openTimeout;
946
- _closeTimeout;
947
- tabIndex = () => this.listBehavior.tabIndex();
948
- shouldFocus = computed(() => {
949
- const root = this.root();
950
- if (root instanceof MenuTriggerPattern) {
951
- return true;
952
- }
953
- if (root instanceof MenuBarPattern || root instanceof MenuPattern) {
954
- return root.isFocused();
955
- }
956
- return false;
957
- });
958
- _expandKey = computed(() => {
959
- return this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
960
- });
961
- _collapseKey = computed(() => {
962
- return this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
963
- });
964
- dynamicSpaceKey = computed(() => this.listBehavior.isTyping() ? '' : ' ');
965
- typeaheadRegexp = /^.$/;
966
- root = computed(() => {
967
- const parent = this.inputs.parent();
968
- if (!parent) {
969
- return this;
970
- }
971
- if (parent instanceof MenuTriggerPattern) {
972
- return parent;
973
- }
974
- const grandparent = parent.inputs.parent();
975
- if (grandparent instanceof MenuBarPattern) {
976
- return grandparent;
977
- }
978
- return grandparent?.root();
979
- });
980
- keydownManager = computed(() => {
981
- return new KeyboardEventManager().on('ArrowDown', () => this.next()).on('ArrowUp', () => this.prev()).on('Home', () => this.first()).on('End', () => this.last()).on('Enter', () => this.trigger()).on('Escape', () => this.closeAll()).on(this._expandKey, () => this.expand()).on(this._collapseKey, () => this.collapse()).on(this.dynamicSpaceKey, () => this.trigger()).on(this.typeaheadRegexp, e => this.listBehavior.search(e.key));
982
- });
983
- constructor(inputs) {
984
- this.inputs = inputs;
985
- this.id = inputs.id;
986
- this.listBehavior = new List({
987
- ...inputs,
988
- values: signal([])
989
- });
990
- }
991
- setDefaultState() {
992
- if (!this.inputs.parent()) {
993
- this.listBehavior.goto(this.inputs.items()[0], {
994
- focusElement: false
995
- });
996
- }
997
- }
998
- onKeydown(event) {
999
- this.keydownManager().handle(event);
1000
- }
1001
- onMouseOver(event) {
1002
- if (!this.visible()) {
1003
- return;
1004
- }
1005
- this.hasBeenHovered.set(true);
1006
- const item = this.inputs.items().find(i => i.element()?.contains(event.target));
1007
- if (!item) {
1008
- return;
1009
- }
1010
- const parent = this.inputs.parent();
1011
- const activeItem = this?.inputs.activeItem();
1012
- if (parent instanceof MenuItemPattern) {
1013
- const grandparent = parent.inputs.parent();
1014
- if (grandparent instanceof MenuPattern) {
1015
- grandparent._clearTimeouts();
1016
- grandparent.listBehavior.goto(parent, {
1017
- focusElement: false
1018
- });
1019
- }
1020
- }
1021
- if (activeItem && activeItem !== item) {
1022
- this._closeItem(activeItem);
1023
- }
1024
- if (item.expanded()) {
1025
- this._clearCloseTimeout();
1026
- }
1027
- this._openItem(item);
1028
- this.listBehavior.goto(item, {
1029
- focusElement: this.shouldFocus()
1030
- });
1031
- }
1032
- _closeItem(item) {
1033
- this._clearOpenTimeout();
1034
- if (!this._closeTimeout) {
1035
- this._closeTimeout = setTimeout(() => {
1036
- item.close();
1037
- this._closeTimeout = undefined;
1038
- }, this.inputs.expansionDelay());
1039
- }
1040
- }
1041
- _openItem(item) {
1042
- this._clearOpenTimeout();
1043
- this._openTimeout = setTimeout(() => {
1044
- item.open();
1045
- this._openTimeout = undefined;
1046
- }, this.inputs.expansionDelay());
1047
- }
1048
- onMouseOut(event) {
1049
- this._clearOpenTimeout();
1050
- if (this.isFocused()) {
1051
- return;
1052
- }
1053
- const root = this.root();
1054
- const parent = this.inputs.parent();
1055
- const relatedTarget = event.relatedTarget;
1056
- if (!root || !parent || parent instanceof MenuTriggerPattern) {
1057
- return;
1058
- }
1059
- const grandparent = parent.inputs.parent();
1060
- if (!grandparent || grandparent instanceof MenuBarPattern) {
1061
- return;
1062
- }
1063
- if (!grandparent.inputs.element()?.contains(relatedTarget)) {
1064
- parent.close();
1065
- }
1066
- }
1067
- onClick(event) {
1068
- const relatedTarget = event.target;
1069
- const item = this.inputs.items().find(i => i.element()?.contains(relatedTarget));
1070
- if (item) {
1071
- item.open();
1072
- this.listBehavior.goto(item);
1073
- this.submit(item);
1074
- }
1075
- }
1076
- onFocusIn() {
1077
- this.isFocused.set(true);
1078
- this.hasBeenFocused.set(true);
1079
- }
1080
- onFocusOut(event) {
1081
- const parent = this.inputs.parent();
1082
- const parentEl = parent?.inputs.element();
1083
- const relatedTarget = event.relatedTarget;
1084
- if (!relatedTarget) {
1085
- this.isFocused.set(false);
1086
- this.inputs.parent()?.close({
1087
- refocus: true
1088
- });
1089
- }
1090
- if (parent instanceof MenuItemPattern) {
1091
- const grandparent = parent.inputs.parent();
1092
- const siblings = grandparent?.inputs.items().filter(i => i !== parent);
1093
- const item = siblings?.find(i => i.element()?.contains(relatedTarget));
1094
- if (item) {
1095
- return;
1096
- }
1097
- }
1098
- if (this.visible() && !parentEl?.contains(relatedTarget) && !this.inputs.element()?.contains(relatedTarget)) {
1099
- this.isFocused.set(false);
1100
- this.inputs.parent()?.close();
1101
- }
1102
- }
1103
- prev() {
1104
- this.inputs.activeItem()?.close();
1105
- this.listBehavior.prev();
1106
- }
1107
- next() {
1108
- this.inputs.activeItem()?.close();
1109
- this.listBehavior.next();
1110
- }
1111
- first() {
1112
- this.inputs.activeItem()?.close();
1113
- this.listBehavior.first();
1114
- }
1115
- last() {
1116
- this.inputs.activeItem()?.close();
1117
- this.listBehavior.last();
1118
- }
1119
- trigger() {
1120
- this.inputs.activeItem()?.hasPopup() ? this.inputs.activeItem()?.open({
1121
- first: true
1122
- }) : this.submit();
1123
- }
1124
- submit(item = this.inputs.activeItem()) {
1125
- const root = this.root();
1126
- if (item && !item.disabled()) {
1127
- const isMenu = root instanceof MenuPattern;
1128
- const isMenuBar = root instanceof MenuBarPattern;
1129
- const isMenuTrigger = root instanceof MenuTriggerPattern;
1130
- if (!item.submenu() && isMenuTrigger) {
1131
- root.close({
1132
- refocus: true
1133
- });
1134
- }
1135
- if (!item.submenu() && isMenuBar) {
1136
- root.close();
1137
- root?.inputs.onSelect?.(item.value());
1138
- }
1139
- if (!item.submenu() && isMenu) {
1140
- root.inputs.activeItem()?.close({
1141
- refocus: true
1142
- });
1143
- root?.inputs.onSelect?.(item.value());
1144
- }
1145
- }
1146
- }
1147
- collapse() {
1148
- const root = this.root();
1149
- const parent = this.inputs.parent();
1150
- if (parent instanceof MenuItemPattern && !(parent.inputs.parent() instanceof MenuBarPattern)) {
1151
- parent.close({
1152
- refocus: true
1153
- });
1154
- } else if (root instanceof MenuBarPattern) {
1155
- root.prev();
1156
- }
1157
- }
1158
- expand() {
1159
- const root = this.root();
1160
- const activeItem = this.inputs.activeItem();
1161
- if (activeItem?.submenu()) {
1162
- activeItem.open({
1163
- first: true
1164
- });
1165
- } else if (root instanceof MenuBarPattern) {
1166
- root.next();
1167
- }
1168
- }
1169
- close() {
1170
- this.inputs.parent()?.close();
1171
- }
1172
- closeAll() {
1173
- const root = this.root();
1174
- if (root instanceof MenuTriggerPattern) {
1175
- root.close({
1176
- refocus: true
1177
- });
1178
- }
1179
- if (root instanceof MenuBarPattern) {
1180
- root.close();
1181
- }
1182
- if (root instanceof MenuPattern) {
1183
- root.inputs.activeItem()?.close({
1184
- refocus: true
1185
- });
1186
- }
1187
- }
1188
- _clearTimeouts() {
1189
- this._clearOpenTimeout();
1190
- this._clearCloseTimeout();
1191
- }
1192
- _clearOpenTimeout() {
1193
- if (this._openTimeout) {
1194
- clearTimeout(this._openTimeout);
1195
- this._openTimeout = undefined;
1196
- }
1197
- }
1198
- _clearCloseTimeout() {
1199
- if (this._closeTimeout) {
1200
- clearTimeout(this._closeTimeout);
1201
- this._closeTimeout = undefined;
1202
- }
1203
- }
1204
- }
1205
- class MenuBarPattern {
1206
- inputs;
1207
- listBehavior;
1208
- tabIndex = () => this.listBehavior.tabIndex();
1209
- _nextKey = computed(() => {
1210
- return this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
1211
- });
1212
- _previousKey = computed(() => {
1213
- return this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
1214
- });
1215
- dynamicSpaceKey = computed(() => this.listBehavior.isTyping() ? '' : ' ');
1216
- typeaheadRegexp = /^.$/;
1217
- isFocused = signal(false);
1218
- hasBeenFocused = signal(false);
1219
- disabled = () => this.inputs.disabled();
1220
- keydownManager = computed(() => {
1221
- return new KeyboardEventManager().on(this._nextKey, () => this.next()).on(this._previousKey, () => this.prev()).on('End', () => this.listBehavior.last()).on('Home', () => this.listBehavior.first()).on('Enter', () => this.inputs.activeItem()?.open({
1222
- first: true
1223
- })).on('ArrowUp', () => this.inputs.activeItem()?.open({
1224
- last: true
1225
- })).on('ArrowDown', () => this.inputs.activeItem()?.open({
1226
- first: true
1227
- })).on(this.dynamicSpaceKey, () => this.inputs.activeItem()?.open({
1228
- first: true
1229
- })).on(this.typeaheadRegexp, e => this.listBehavior.search(e.key));
1230
- });
1231
- constructor(inputs) {
1232
- this.inputs = inputs;
1233
- this.listBehavior = new List(inputs);
1234
- }
1235
- setDefaultState() {
1236
- this.inputs.activeItem.set(this.inputs.items()[0]);
1237
- }
1238
- onKeydown(event) {
1239
- this.keydownManager().handle(event);
1240
- }
1241
- onClick(event) {
1242
- const item = this.inputs.items().find(i => i.element()?.contains(event.target));
1243
- if (!item) {
1244
- return;
1245
- }
1246
- this.goto(item);
1247
- item.expanded() ? item.close() : item.open();
1248
- }
1249
- onMouseOver(event) {
1250
- const item = this.inputs.items().find(i => i.element()?.contains(event.target));
1251
- if (item) {
1252
- this.goto(item, {
1253
- focusElement: this.isFocused()
1254
- });
1255
- }
1256
- }
1257
- onFocusIn() {
1258
- this.isFocused.set(true);
1259
- this.hasBeenFocused.set(true);
1260
- }
1261
- onFocusOut(event) {
1262
- const relatedTarget = event.relatedTarget;
1263
- if (!this.inputs.element()?.contains(relatedTarget)) {
1264
- this.isFocused.set(false);
1265
- this.close();
1266
- }
1267
- }
1268
- goto(item, opts) {
1269
- const prevItem = this.inputs.activeItem();
1270
- this.listBehavior.goto(item, opts);
1271
- if (prevItem?.expanded()) {
1272
- prevItem?.close();
1273
- this.inputs.activeItem()?.open();
1274
- }
1275
- if (item === prevItem) {
1276
- if (item.expanded() && item.submenu()?.inputs.activeItem()) {
1277
- item.submenu()?.inputs.activeItem()?.close();
1278
- item.submenu()?.listBehavior.unfocus();
1279
- }
1280
- }
1281
- }
1282
- next() {
1283
- const prevItem = this.inputs.activeItem();
1284
- this.listBehavior.next();
1285
- if (prevItem?.expanded()) {
1286
- prevItem?.close();
1287
- this.inputs.activeItem()?.open({
1288
- first: true
1289
- });
1290
- }
1291
- }
1292
- prev() {
1293
- const prevItem = this.inputs.activeItem();
1294
- this.listBehavior.prev();
1295
- if (prevItem?.expanded()) {
1296
- prevItem?.close();
1297
- this.inputs.activeItem()?.open({
1298
- first: true
1299
- });
1300
- }
1301
- }
1302
- close() {
1303
- this.inputs.activeItem()?.close({
1304
- refocus: this.isFocused()
1305
- });
1306
- }
1307
- }
1308
- class MenuTriggerPattern {
1309
- inputs;
1310
- expanded = signal(false);
1311
- hasBeenFocused = signal(false);
1312
- role = () => 'button';
1313
- hasPopup = () => true;
1314
- menu;
1315
- tabIndex = computed(() => this.expanded() && this.menu()?.inputs.activeItem() ? -1 : 0);
1316
- disabled = () => this.inputs.disabled();
1317
- keydownManager = computed(() => {
1318
- return new KeyboardEventManager().on(' ', () => this.open({
1319
- first: true
1320
- })).on('Enter', () => this.open({
1321
- first: true
1322
- })).on('ArrowDown', () => this.open({
1323
- first: true
1324
- })).on('ArrowUp', () => this.open({
1325
- last: true
1326
- })).on('Escape', () => this.close({
1327
- refocus: true
1328
- }));
1329
- });
1330
- constructor(inputs) {
1331
- this.inputs = inputs;
1332
- this.menu = this.inputs.menu;
1333
- }
1334
- onKeydown(event) {
1335
- if (!this.inputs.disabled()) {
1336
- this.keydownManager().handle(event);
1337
- }
1338
- }
1339
- onClick() {
1340
- if (!this.inputs.disabled()) {
1341
- this.expanded() ? this.close() : this.open({
1342
- first: true
1343
- });
1344
- }
1345
- }
1346
- onFocusIn() {
1347
- this.hasBeenFocused.set(true);
1348
- }
1349
- onFocusOut(event) {
1350
- const element = this.inputs.element();
1351
- const relatedTarget = event.relatedTarget;
1352
- if (this.expanded() && !element?.contains(relatedTarget) && !this.inputs.menu()?.inputs.element()?.contains(relatedTarget)) {
1353
- this.close();
1354
- }
1355
- }
1356
- open(opts) {
1357
- this.expanded.set(true);
1358
- if (opts?.first) {
1359
- this.inputs.menu()?.first();
1360
- } else if (opts?.last) {
1361
- this.inputs.menu()?.last();
1362
- }
1363
- }
1364
- close(opts = {}) {
1365
- this.expanded.set(false);
1366
- this.menu()?.listBehavior.unfocus();
1367
- if (opts.refocus) {
1368
- this.inputs.element()?.focus();
1369
- }
1370
- let menuitems = this.inputs.menu()?.inputs.items() ?? [];
1371
- while (menuitems.length) {
1372
- const menuitem = menuitems.pop();
1373
- menuitem?._expanded.set(false);
1374
- menuitem?.inputs.parent()?.listBehavior.unfocus();
1375
- menuitems = menuitems.concat(menuitem?.submenu()?.inputs.items() ?? []);
1376
- }
1377
- }
1378
- }
1379
- class MenuItemPattern {
1380
- inputs;
1381
- value;
1382
- id;
1383
- disabled = () => this.inputs.parent()?.disabled() || this.inputs.disabled();
1384
- searchTerm;
1385
- element;
1386
- active = computed(() => this.inputs.parent()?.inputs.activeItem() === this);
1387
- hasBeenFocused = signal(false);
1388
- tabIndex = computed(() => {
1389
- if (this.submenu() && this.submenu()?.inputs.activeItem()) {
1390
- return -1;
1391
- }
1392
- return this.inputs.parent()?.listBehavior.getItemTabindex(this) ?? -1;
1393
- });
1394
- index = computed(() => this.inputs.parent()?.inputs.items().indexOf(this) ?? -1);
1395
- expanded = computed(() => this.submenu() ? this._expanded() : null);
1396
- _expanded = signal(false);
1397
- controls = signal(undefined);
1398
- role = () => 'menuitem';
1399
- hasPopup = computed(() => !!this.submenu());
1400
- submenu;
1401
- selectable;
1402
- constructor(inputs) {
1403
- this.inputs = inputs;
1404
- this.id = inputs.id;
1405
- this.value = inputs.value;
1406
- this.element = inputs.element;
1407
- this.submenu = this.inputs.submenu;
1408
- this.searchTerm = inputs.searchTerm;
1409
- this.selectable = computed(() => !this.submenu());
1410
- }
1411
- open(opts) {
1412
- if (this.disabled()) {
1413
- return;
1414
- }
1415
- this._expanded.set(true);
1416
- if (opts?.first) {
1417
- this.submenu()?.first();
1418
- }
1419
- if (opts?.last) {
1420
- this.submenu()?.last();
1421
- }
1422
- }
1423
- close(opts = {}) {
1424
- this._expanded.set(false);
1425
- if (opts.refocus) {
1426
- this.inputs.parent()?.listBehavior.goto(this);
1427
- }
1428
- let menuitems = this.inputs.submenu()?.inputs.items() ?? [];
1429
- while (menuitems.length) {
1430
- const menuitem = menuitems.pop();
1431
- menuitem?._expanded.set(false);
1432
- menuitem?.inputs.parent()?.listBehavior.unfocus();
1433
- menuitems = menuitems.concat(menuitem?.submenu()?.inputs.items() ?? []);
1434
- const parent = menuitem?.inputs.parent();
1435
- if (parent instanceof MenuPattern) {
1436
- parent._clearTimeouts();
1437
- }
1438
- }
1439
- }
1440
- onFocusIn() {
1441
- this.hasBeenFocused.set(true);
1442
- }
1443
- }
1444
-
1445
- function convertGetterSetterToWritableSignalLike(getter, setter) {
1446
- return Object.assign(getter, {
1447
- set: setter,
1448
- update: updateCallback => setter(updateCallback(getter()))
1449
- });
1450
- }
1451
-
1452
- class ListExpansion {
1453
- inputs;
1454
- constructor(inputs) {
1455
- this.inputs = inputs;
1456
- }
1457
- open(item) {
1458
- if (!this.isExpandable(item)) return false;
1459
- if (item.expanded()) return false;
1460
- if (!this.inputs.multiExpandable()) {
1461
- this.closeAll();
1462
- }
1463
- item.expanded.set(true);
1464
- return true;
1465
- }
1466
- close(item) {
1467
- if (!this.isExpandable(item)) return false;
1468
- item.expanded.set(false);
1469
- return true;
1470
- }
1471
- toggle(item) {
1472
- return item.expanded() ? this.close(item) : this.open(item);
1473
- }
1474
- openAll() {
1475
- if (this.inputs.multiExpandable()) {
1476
- for (const item of this.inputs.items()) {
1477
- this.open(item);
1478
- }
1479
- }
1480
- }
1481
- closeAll() {
1482
- for (const item of this.inputs.items()) {
1483
- this.close(item);
1484
- }
1485
- }
1486
- isExpandable(item) {
1487
- return !this.inputs.disabled() && !item.disabled() && item.expandable();
1488
- }
1489
- }
1490
-
1491
- class LabelControl {
1492
- inputs;
1493
- label = computed(() => this.inputs.label?.());
1494
- labelledBy = computed(() => {
1495
- const label = this.label();
1496
- const labelledBy = this.inputs.labelledBy?.();
1497
- const defaultLabelledBy = this.inputs.defaultLabelledBy();
1498
- if (labelledBy && labelledBy.length > 0) {
1499
- return labelledBy;
1500
- }
1501
- if (label) {
1502
- return [];
1503
- }
1504
- return defaultLabelledBy;
1505
- });
1506
- constructor(inputs) {
1507
- this.inputs = inputs;
1508
- }
1509
- }
1510
-
1511
- class TabPattern {
1512
- inputs;
1513
- id = () => this.inputs.id();
1514
- index = computed(() => this.inputs.tablist().inputs.items().indexOf(this));
1515
- value = () => this.inputs.value();
1516
- disabled = () => this.inputs.disabled();
1517
- element = () => this.inputs.element();
1518
- expandable = () => true;
1519
- expanded;
1520
- active = computed(() => this.inputs.tablist().inputs.activeItem() === this);
1521
- selected = computed(() => this.inputs.tablist().selectedTab() === this);
1522
- tabIndex = computed(() => this.inputs.tablist().focusBehavior.getItemTabIndex(this));
1523
- controls = computed(() => this.inputs.tabpanel()?.id());
1524
- constructor(inputs) {
1525
- this.inputs = inputs;
1526
- this.expanded = inputs.expanded;
1527
- }
1528
- open() {
1529
- return this.inputs.tablist().open(this);
1530
- }
1531
- }
1532
- class TabPanelPattern {
1533
- inputs;
1534
- id = () => this.inputs.id();
1535
- value = () => this.inputs.value();
1536
- labelManager;
1537
- hidden = computed(() => this.inputs.tab()?.expanded() === false);
1538
- tabIndex = computed(() => this.hidden() ? -1 : 0);
1539
- labelledBy = computed(() => this.labelManager.labelledBy().length > 0 ? this.labelManager.labelledBy().join(' ') : undefined);
1540
- constructor(inputs) {
1541
- this.inputs = inputs;
1542
- this.labelManager = new LabelControl({
1543
- ...inputs,
1544
- defaultLabelledBy: computed(() => this.inputs.tab() ? [this.inputs.tab().id()] : [])
1545
- });
1546
- }
1547
- }
1548
- class TabListPattern {
1549
- inputs;
1550
- focusBehavior;
1551
- navigationBehavior;
1552
- expansionBehavior;
1553
- activeTab = () => this.inputs.activeItem();
1554
- selectedTab = signal(undefined);
1555
- orientation = () => this.inputs.orientation();
1556
- disabled = () => this.inputs.disabled();
1557
- tabIndex = computed(() => this.focusBehavior.getListTabIndex());
1558
- activeDescendant = computed(() => this.focusBehavior.getActiveDescendant());
1559
- followFocus = computed(() => this.inputs.selectionMode() === 'follow');
1560
- prevKey = computed(() => {
1561
- if (this.inputs.orientation() === 'vertical') {
1562
- return 'ArrowUp';
1563
- }
1564
- return this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
1565
- });
1566
- nextKey = computed(() => {
1567
- if (this.inputs.orientation() === 'vertical') {
1568
- return 'ArrowDown';
1569
- }
1570
- return this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
1571
- });
1572
- keydown = computed(() => {
1573
- return new KeyboardEventManager().on(this.prevKey, () => this._navigate(() => this.navigationBehavior.prev(), this.followFocus())).on(this.nextKey, () => this._navigate(() => this.navigationBehavior.next(), this.followFocus())).on('Home', () => this._navigate(() => this.navigationBehavior.first(), this.followFocus())).on('End', () => this._navigate(() => this.navigationBehavior.last(), this.followFocus())).on(' ', () => this.open()).on('Enter', () => this.open());
1574
- });
1575
- pointerdown = computed(() => {
1576
- return new PointerEventManager().on(e => this._navigate(() => this.navigationBehavior.goto(this._getItem(e)), true));
1577
- });
1578
- constructor(inputs) {
1579
- this.inputs = inputs;
1580
- this.focusBehavior = new ListFocus(inputs);
1581
- this.navigationBehavior = new ListNavigation({
1582
- ...inputs,
1583
- focusManager: this.focusBehavior
1584
- });
1585
- this.expansionBehavior = new ListExpansion({
1586
- ...inputs,
1587
- multiExpandable: () => false
1588
- });
1589
- }
1590
- setDefaultState() {
1591
- let firstItem;
1592
- for (const item of this.inputs.items()) {
1593
- if (!this.focusBehavior.isFocusable(item)) continue;
1594
- if (firstItem === undefined) {
1595
- firstItem = item;
1596
- }
1597
- if (item.selected()) {
1598
- this.inputs.activeItem.set(item);
1599
- return;
1600
- }
1601
- }
1602
- if (firstItem !== undefined) {
1603
- this.inputs.activeItem.set(firstItem);
1604
- }
1605
- }
1606
- onKeydown(event) {
1607
- if (!this.disabled()) {
1608
- this.keydown().handle(event);
1609
- }
1610
- }
1611
- onPointerdown(event) {
1612
- if (!this.disabled()) {
1613
- this.pointerdown().handle(event);
1614
- }
1615
- }
1616
- open(tab) {
1617
- tab ??= this.activeTab();
1618
- if (typeof tab === 'string') {
1619
- tab = this.inputs.items().find(t => t.value() === tab);
1620
- }
1621
- if (tab === undefined) return false;
1622
- const success = this.expansionBehavior.open(tab);
1623
- if (success) {
1624
- this.selectedTab.set(tab);
1625
- }
1626
- return success;
1627
- }
1628
- _navigate(op, shouldExpand = false) {
1629
- const success = op();
1630
- if (success && shouldExpand) {
1631
- this.open();
1632
- }
1633
- }
1634
- _getItem(e) {
1635
- if (!(e.target instanceof HTMLElement)) {
1636
- return;
1637
- }
1638
- const element = e.target.closest('[role="tab"]');
1639
- return this.inputs.items().find(i => i.element() === element);
1640
- }
1641
- }
1642
-
1643
- class ToolbarPattern {
1644
- inputs;
1645
- listBehavior;
1646
- orientation;
1647
- softDisabled;
1648
- disabled = computed(() => this.listBehavior.disabled());
1649
- tabIndex = computed(() => this.listBehavior.tabIndex());
1650
- activeDescendant = computed(() => this.listBehavior.activeDescendant());
1651
- activeItem = () => this.listBehavior.inputs.activeItem();
1652
- _prevKey = computed(() => {
1653
- if (this.inputs.orientation() === 'vertical') {
1654
- return 'ArrowUp';
1655
- }
1656
- return this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
1657
- });
1658
- _nextKey = computed(() => {
1659
- if (this.inputs.orientation() === 'vertical') {
1660
- return 'ArrowDown';
1661
- }
1662
- return this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
1663
- });
1664
- _altPrevKey = computed(() => {
1665
- if (this.inputs.orientation() === 'vertical') {
1666
- return this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
1667
- }
1668
- return 'ArrowUp';
1669
- });
1670
- _altNextKey = computed(() => {
1671
- if (this.inputs.orientation() === 'vertical') {
1672
- return this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
1673
- }
1674
- return 'ArrowDown';
1675
- });
1676
- _keydown = computed(() => {
1677
- const manager = new KeyboardEventManager();
1678
- return manager.on(this._nextKey, () => this.listBehavior.next()).on(this._prevKey, () => this.listBehavior.prev()).on(this._altNextKey, () => this._groupNext()).on(this._altPrevKey, () => this._groupPrev()).on(' ', () => this.select()).on('Enter', () => this.select()).on('Home', () => this.listBehavior.first()).on('End', () => this.listBehavior.last());
1679
- });
1680
- _groupNext() {
1681
- const currGroup = this.inputs.activeItem()?.group();
1682
- const nextGroup = this.listBehavior.navigationBehavior.peekNext()?.group();
1683
- if (!currGroup) {
1684
- return;
1685
- }
1686
- if (currGroup !== nextGroup) {
1687
- this.listBehavior.goto(this.listBehavior.navigationBehavior.peekFirst(currGroup.inputs.items()));
1688
- return;
1689
- }
1690
- this.listBehavior.next();
1691
- }
1692
- _groupPrev() {
1693
- const currGroup = this.inputs.activeItem()?.group();
1694
- const nextGroup = this.listBehavior.navigationBehavior.peekPrev()?.group();
1695
- if (!currGroup) {
1696
- return;
1697
- }
1698
- if (currGroup !== nextGroup) {
1699
- this.listBehavior.goto(this.listBehavior.navigationBehavior.peekLast(currGroup.inputs.items()));
1700
- return;
1701
- }
1702
- this.listBehavior.prev();
1703
- }
1704
- _goto(e) {
1705
- const item = this.inputs.getItem(e.target);
1706
- if (item) {
1707
- this.listBehavior.goto(item);
1708
- this.select();
1709
- }
1710
- }
1711
- select() {
1712
- const group = this.inputs.activeItem()?.group();
1713
- if (!group?.multi()) {
1714
- group?.inputs.items().forEach(i => this.listBehavior.deselect(i));
1715
- }
1716
- this.listBehavior.toggle();
1717
- }
1718
- constructor(inputs) {
1719
- this.inputs = inputs;
1720
- this.orientation = inputs.orientation;
1721
- this.softDisabled = inputs.softDisabled;
1722
- this.listBehavior = new List({
1723
- ...inputs,
1724
- multi: () => true,
1725
- focusMode: () => 'roving',
1726
- selectionMode: () => 'explicit',
1727
- typeaheadDelay: () => 0
1728
- });
1729
- }
1730
- onKeydown(event) {
1731
- if (this.disabled()) return;
1732
- this._keydown().handle(event);
1733
- }
1734
- onPointerdown(event) {
1735
- event.preventDefault();
1736
- }
1737
- onClick(event) {
1738
- if (this.disabled()) return;
1739
- this._goto(event);
1740
- }
1741
- setDefaultState() {
1742
- const firstItem = this.listBehavior.navigationBehavior.peekFirst(this.inputs.items());
1743
- if (firstItem) {
1744
- this.inputs.activeItem.set(firstItem);
1745
- }
1746
- }
1747
- validate() {
1748
- const violations = [];
1749
- return violations;
1750
- }
1751
- }
1752
-
1753
- class ToolbarWidgetPattern {
1754
- inputs;
1755
- id = () => this.inputs.id();
1756
- element = () => this.inputs.element();
1757
- disabled = () => this.inputs.disabled() || this.group()?.disabled() || false;
1758
- group = () => this.inputs.group();
1759
- toolbar = () => this.inputs.toolbar();
1760
- tabIndex = computed(() => this.toolbar().listBehavior.getItemTabindex(this));
1761
- searchTerm = () => '';
1762
- value = () => this.inputs.value();
1763
- selectable = () => true;
1764
- index = computed(() => this.toolbar().inputs.items().indexOf(this) ?? -1);
1765
- selected = computed(() => this.toolbar().listBehavior.inputs.values().includes(this.value()));
1766
- active = computed(() => this.toolbar().activeItem() === this);
1767
- constructor(inputs) {
1768
- this.inputs = inputs;
1769
- }
1770
- }
1771
-
1772
- class ToolbarWidgetGroupPattern {
1773
- inputs;
1774
- disabled = () => this.inputs.disabled();
1775
- toolbar = () => this.inputs.toolbar();
1776
- multi = () => this.inputs.multi();
1777
- searchTerm = () => '';
1778
- value = () => '';
1779
- selectable = () => true;
1780
- element = () => undefined;
1781
- constructor(inputs) {
1782
- this.inputs = inputs;
1783
- }
1784
- }
1785
-
1786
- const focusMode = () => 'roving';
1787
- class AccordionGroupPattern {
1788
- inputs;
1789
- navigationBehavior;
1790
- focusBehavior;
1791
- expansionBehavior;
1792
- constructor(inputs) {
1793
- this.inputs = inputs;
1794
- this.focusBehavior = new ListFocus({
1795
- ...inputs,
1796
- focusMode
1797
- });
1798
- this.navigationBehavior = new ListNavigation({
1799
- ...inputs,
1800
- focusMode,
1801
- focusManager: this.focusBehavior
1802
- });
1803
- this.expansionBehavior = new ListExpansion({
1804
- ...inputs
1805
- });
1806
- }
1807
- prevKey = computed(() => {
1808
- if (this.inputs.orientation() === 'vertical') {
1809
- return 'ArrowUp';
1810
- }
1811
- return this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
1812
- });
1813
- nextKey = computed(() => {
1814
- if (this.inputs.orientation() === 'vertical') {
1815
- return 'ArrowDown';
1816
- }
1817
- return this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
1818
- });
1819
- keydown = computed(() => {
1820
- return new KeyboardEventManager().on(this.prevKey, () => this.navigationBehavior.prev()).on(this.nextKey, () => this.navigationBehavior.next()).on('Home', () => this.navigationBehavior.first()).on('End', () => this.navigationBehavior.last()).on(' ', () => this.toggle()).on('Enter', () => this.toggle());
1821
- });
1822
- pointerdown = computed(() => {
1823
- return new PointerEventManager().on(e => {
1824
- const item = this.inputs.getItem(e.target);
1825
- if (!item) return;
1826
- this.navigationBehavior.goto(item);
1827
- this.expansionBehavior.toggle(item);
1828
- });
1829
- });
1830
- onKeydown(event) {
1831
- this.keydown().handle(event);
1832
- }
1833
- onPointerdown(event) {
1834
- this.pointerdown().handle(event);
1835
- }
1836
- onFocus(event) {
1837
- const item = this.inputs.getItem(event.target);
1838
- if (!item) return;
1839
- if (!this.focusBehavior.isFocusable(item)) return;
1840
- this.focusBehavior.focus(item);
1841
- }
1842
- toggle() {
1843
- const activeItem = this.inputs.activeItem();
1844
- if (activeItem === undefined) return;
1845
- this.expansionBehavior.toggle(activeItem);
1846
- }
1847
- }
1848
- class AccordionTriggerPattern {
1849
- inputs;
1850
- id = () => this.inputs.id();
1851
- element = () => this.inputs.element();
1852
- expandable = () => true;
1853
- expanded;
1854
- active = computed(() => this.inputs.accordionGroup().inputs.activeItem() === this);
1855
- controls = computed(() => this.inputs.accordionPanel()?.inputs.id());
1856
- tabIndex = computed(() => this.inputs.accordionGroup().focusBehavior.isFocusable(this) ? 0 : -1);
1857
- disabled = computed(() => this.inputs.disabled() || this.inputs.accordionGroup().inputs.disabled());
1858
- hardDisabled = computed(() => this.disabled() && !this.inputs.accordionGroup().inputs.softDisabled());
1859
- index = computed(() => this.inputs.accordionGroup().inputs.items().indexOf(this));
1860
- constructor(inputs) {
1861
- this.inputs = inputs;
1862
- this.expanded = inputs.expanded;
1863
- }
1864
- open() {
1865
- this.inputs.accordionGroup().expansionBehavior.open(this);
1866
- }
1867
- close() {
1868
- this.inputs.accordionGroup().expansionBehavior.close(this);
1869
- }
1870
- toggle() {
1871
- this.inputs.accordionGroup().expansionBehavior.toggle(this);
1872
- }
1873
- }
1874
- class AccordionPanelPattern {
1875
- inputs;
1876
- id;
1877
- accordionTrigger;
1878
- hidden;
1879
- constructor(inputs) {
1880
- this.inputs = inputs;
1881
- this.id = inputs.id;
1882
- this.accordionTrigger = inputs.accordionTrigger;
1883
- this.hidden = computed(() => inputs.accordionTrigger()?.expanded() === false);
1884
- }
1885
- }
1886
-
1887
- class TreeItemPattern {
1888
- inputs;
1889
- id = () => this.inputs.id();
1890
- value = () => this.inputs.value();
1891
- element = () => this.inputs.element();
1892
- disabled = () => this.inputs.disabled();
1893
- searchTerm = () => this.inputs.searchTerm();
1894
- tree = () => this.inputs.tree();
1895
- parent = () => this.inputs.parent();
1896
- children = () => this.inputs.children();
1897
- index = computed(() => this.tree().visibleItems().indexOf(this));
1898
- expansionBehavior;
1899
- expandable = () => this.inputs.hasChildren();
1900
- selectable = () => this.inputs.selectable();
1901
- expanded;
1902
- level = computed(() => this.parent().level() + 1);
1903
- visible = computed(() => this.parent().expanded() && this.parent().visible());
1904
- setsize = computed(() => this.parent().children().length);
1905
- posinset = computed(() => this.parent().children().indexOf(this) + 1);
1906
- active = computed(() => this.tree().activeItem() === this);
1907
- tabIndex = computed(() => this.tree().listBehavior.getItemTabindex(this));
1908
- selected = computed(() => {
1909
- if (this.tree().nav()) {
1910
- return undefined;
1911
- }
1912
- if (!this.selectable()) {
1913
- return undefined;
1914
- }
1915
- return this.tree().values().includes(this.value());
1916
- });
1917
- current = computed(() => {
1918
- if (!this.tree().nav()) {
1919
- return undefined;
1920
- }
1921
- if (!this.selectable()) {
1922
- return undefined;
1923
- }
1924
- return this.tree().values().includes(this.value()) ? this.tree().currentType() : undefined;
1925
- });
1926
- constructor(inputs) {
1927
- this.inputs = inputs;
1928
- this.expanded = inputs.expanded;
1929
- this.expansionBehavior = new ListExpansion({
1930
- ...inputs,
1931
- multiExpandable: () => true,
1932
- items: this.children,
1933
- disabled: computed(() => this.tree()?.disabled() ?? false)
1934
- });
1935
- }
1936
- }
1937
- class TreePattern {
1938
- inputs;
1939
- listBehavior;
1940
- expansionBehavior;
1941
- level = () => 0;
1942
- expanded = () => true;
1943
- visible = () => true;
1944
- tabIndex = computed(() => this.listBehavior.tabIndex());
1945
- activeDescendant = computed(() => this.listBehavior.activeDescendant());
1946
- children = computed(() => this.inputs.allItems().filter(item => item.level() === this.level() + 1));
1947
- visibleItems = computed(() => this.inputs.allItems().filter(item => item.visible()));
1948
- followFocus = computed(() => this.inputs.selectionMode() === 'follow');
1949
- isRtl = computed(() => this.inputs.textDirection() === 'rtl');
1950
- prevKey = computed(() => {
1951
- if (this.inputs.orientation() === 'vertical') {
1952
- return 'ArrowUp';
1953
- }
1954
- return this.isRtl() ? 'ArrowRight' : 'ArrowLeft';
1955
- });
1956
- nextKey = computed(() => {
1957
- if (this.inputs.orientation() === 'vertical') {
1958
- return 'ArrowDown';
1959
- }
1960
- return this.isRtl() ? 'ArrowLeft' : 'ArrowRight';
1961
- });
1962
- collapseKey = computed(() => {
1963
- if (this.inputs.orientation() === 'horizontal') {
1964
- return 'ArrowUp';
1965
- }
1966
- return this.isRtl() ? 'ArrowRight' : 'ArrowLeft';
1967
- });
1968
- expandKey = computed(() => {
1969
- if (this.inputs.orientation() === 'horizontal') {
1970
- return 'ArrowDown';
1971
- }
1972
- return this.isRtl() ? 'ArrowLeft' : 'ArrowRight';
1973
- });
1974
- dynamicSpaceKey = computed(() => this.listBehavior.isTyping() ? '' : ' ');
1975
- typeaheadRegexp = /^.$/;
1976
- keydown = computed(() => {
1977
- const manager = new KeyboardEventManager();
1978
- const list = this.listBehavior;
1979
- manager.on(this.prevKey, () => list.prev({
1980
- selectOne: this.followFocus()
1981
- })).on(this.nextKey, () => list.next({
1982
- selectOne: this.followFocus()
1983
- })).on('Home', () => list.first({
1984
- selectOne: this.followFocus()
1985
- })).on('End', () => list.last({
1986
- selectOne: this.followFocus()
1987
- })).on(this.typeaheadRegexp, e => list.search(e.key, {
1988
- selectOne: this.followFocus()
1989
- })).on(this.expandKey, () => this.expand({
1990
- selectOne: this.followFocus()
1991
- })).on(this.collapseKey, () => this.collapse({
1992
- selectOne: this.followFocus()
1993
- })).on(Modifier.Shift, '*', () => this.expandSiblings());
1994
- if (this.inputs.multi()) {
1995
- manager.on(Modifier.Any, 'Shift', () => list.anchor(this.listBehavior.activeIndex())).on(Modifier.Shift, this.prevKey, () => list.prev({
1996
- selectRange: true
1997
- })).on(Modifier.Shift, this.nextKey, () => list.next({
1998
- selectRange: true
1999
- })).on([Modifier.Ctrl | Modifier.Shift, Modifier.Meta | Modifier.Shift], 'Home', () => list.first({
2000
- selectRange: true,
2001
- anchor: false
2002
- })).on([Modifier.Ctrl | Modifier.Shift, Modifier.Meta | Modifier.Shift], 'End', () => list.last({
2003
- selectRange: true,
2004
- anchor: false
2005
- })).on(Modifier.Shift, 'Enter', () => list.updateSelection({
2006
- selectRange: true,
2007
- anchor: false
2008
- })).on(Modifier.Shift, this.dynamicSpaceKey, () => list.updateSelection({
2009
- selectRange: true,
2010
- anchor: false
2011
- }));
2012
- }
2013
- if (!this.followFocus() && this.inputs.multi()) {
2014
- manager.on(this.dynamicSpaceKey, () => list.toggle()).on('Enter', () => list.toggle(), {
2015
- preventDefault: !this.nav()
2016
- }).on([Modifier.Ctrl, Modifier.Meta], 'A', () => list.toggleAll());
2017
- }
2018
- if (!this.followFocus() && !this.inputs.multi()) {
2019
- manager.on(this.dynamicSpaceKey, () => list.selectOne());
2020
- manager.on('Enter', () => list.selectOne(), {
2021
- preventDefault: !this.nav()
2022
- });
2023
- }
2024
- if (this.inputs.multi() && this.followFocus()) {
2025
- manager.on([Modifier.Ctrl, Modifier.Meta], this.prevKey, () => list.prev()).on([Modifier.Ctrl, Modifier.Meta], this.nextKey, () => list.next()).on([Modifier.Ctrl, Modifier.Meta], this.expandKey, () => this.expand()).on([Modifier.Ctrl, Modifier.Meta], this.collapseKey, () => this.collapse()).on([Modifier.Ctrl, Modifier.Meta], ' ', () => list.toggle()).on([Modifier.Ctrl, Modifier.Meta], 'Enter', () => list.toggle()).on([Modifier.Ctrl, Modifier.Meta], 'Home', () => list.first()).on([Modifier.Ctrl, Modifier.Meta], 'End', () => list.last()).on([Modifier.Ctrl, Modifier.Meta], 'A', () => {
2026
- list.toggleAll();
2027
- list.select();
2028
- });
2029
- }
2030
- return manager;
2031
- });
2032
- pointerdown = computed(() => {
2033
- const manager = new PointerEventManager();
2034
- if (this.multi()) {
2035
- manager.on(Modifier.Shift, e => this.goto(e, {
2036
- selectRange: true
2037
- }));
2038
- }
2039
- if (!this.multi()) {
2040
- return manager.on(e => this.goto(e, {
2041
- selectOne: true
2042
- }));
2043
- }
2044
- if (this.multi() && this.followFocus()) {
2045
- return manager.on(e => this.goto(e, {
2046
- selectOne: true
2047
- })).on(Modifier.Ctrl, e => this.goto(e, {
2048
- toggle: true
2049
- }));
2050
- }
2051
- if (this.multi() && !this.followFocus()) {
2052
- return manager.on(e => this.goto(e, {
2053
- toggle: true
2054
- }));
2055
- }
2056
- return manager;
2057
- });
2058
- id = () => this.inputs.id();
2059
- element = () => this.inputs.element();
2060
- nav = () => this.inputs.nav();
2061
- currentType = () => this.inputs.currentType();
2062
- allItems = () => this.inputs.allItems();
2063
- focusMode = () => this.inputs.focusMode();
2064
- disabled = () => this.inputs.disabled();
2065
- activeItem;
2066
- softDisabled = () => this.inputs.softDisabled();
2067
- wrap = () => this.inputs.wrap();
2068
- orientation = () => this.inputs.orientation();
2069
- textDirection = () => this.textDirection();
2070
- multi = computed(() => this.nav() ? false : this.inputs.multi());
2071
- selectionMode = () => this.inputs.selectionMode();
2072
- typeaheadDelay = () => this.inputs.typeaheadDelay();
2073
- values;
2074
- constructor(inputs) {
2075
- this.inputs = inputs;
2076
- this.activeItem = inputs.activeItem;
2077
- this.values = inputs.values;
2078
- this.listBehavior = new List({
2079
- ...inputs,
2080
- items: this.visibleItems,
2081
- multi: this.multi
2082
- });
2083
- this.expansionBehavior = new ListExpansion({
2084
- multiExpandable: () => true,
2085
- items: this.children,
2086
- disabled: this.disabled
2087
- });
2088
- }
2089
- setDefaultState() {
2090
- let firstItem;
2091
- for (const item of this.allItems()) {
2092
- if (!item.visible()) continue;
2093
- if (!this.listBehavior.isFocusable(item)) continue;
2094
- if (firstItem === undefined) {
2095
- firstItem = item;
2096
- }
2097
- if (item.selected()) {
2098
- this.activeItem.set(item);
2099
- return;
2100
- }
2101
- }
2102
- if (firstItem !== undefined) {
2103
- this.activeItem.set(firstItem);
2104
- }
2105
- }
2106
- onKeydown(event) {
2107
- if (!this.disabled()) {
2108
- this.keydown().handle(event);
2109
- }
2110
- }
2111
- onPointerdown(event) {
2112
- if (!this.disabled()) {
2113
- this.pointerdown().handle(event);
2114
- }
2115
- }
2116
- goto(e, opts) {
2117
- const item = this._getItem(e);
2118
- if (!item) return;
2119
- this.listBehavior.goto(item, opts);
2120
- this.toggleExpansion(item);
2121
- }
2122
- toggleExpansion(item) {
2123
- item ??= this.activeItem();
2124
- if (!item || !this.listBehavior.isFocusable(item)) return;
2125
- if (!item.expandable()) return;
2126
- if (item.expanded()) {
2127
- this.collapse();
2128
- } else {
2129
- this.expansionBehavior.open(item);
2130
- }
2131
- }
2132
- expand(opts) {
2133
- const item = this.activeItem();
2134
- if (!item || !this.listBehavior.isFocusable(item)) return;
2135
- if (item.expandable() && !item.expanded()) {
2136
- this.expansionBehavior.open(item);
2137
- } else if (item.expanded() && item.children().some(item => this.listBehavior.isFocusable(item))) {
2138
- this.listBehavior.next(opts);
2139
- }
2140
- }
2141
- expandSiblings(item) {
2142
- item ??= this.activeItem();
2143
- const siblings = item?.parent()?.children();
2144
- siblings?.forEach(item => this.expansionBehavior.open(item));
2145
- }
2146
- collapse(opts) {
2147
- const item = this.activeItem();
2148
- if (!item || !this.listBehavior.isFocusable(item)) return;
2149
- if (item.expandable() && item.expanded()) {
2150
- this.expansionBehavior.close(item);
2151
- } else if (item.parent() && item.parent() !== this) {
2152
- const parentItem = item.parent();
2153
- if (parentItem instanceof TreeItemPattern && this.listBehavior.isFocusable(parentItem)) {
2154
- this.listBehavior.goto(parentItem, opts);
2155
- }
2156
- }
2157
- }
2158
- _getItem(event) {
2159
- if (!(event.target instanceof HTMLElement)) {
2160
- return;
2161
- }
2162
- const element = event.target.closest('[role="treeitem"]');
2163
- return this.inputs.allItems().find(i => i.element() === element);
2164
- }
2165
- }
2166
-
2167
- class ComboboxTreePattern extends TreePattern {
2168
- inputs;
2169
- isItemCollapsible = () => this.inputs.activeItem()?.parent() instanceof TreeItemPattern;
2170
- role = () => 'tree';
2171
- activeId = computed(() => this.listBehavior.activeDescendant());
2172
- getActiveItem = () => this.inputs.activeItem();
2173
- items = computed(() => this.inputs.allItems());
2174
- tabIndex = () => -1;
2175
- constructor(inputs) {
2176
- if (inputs.combobox()) {
2177
- inputs.multi = () => false;
2178
- inputs.focusMode = () => 'activedescendant';
2179
- inputs.element = inputs.combobox().inputs.inputEl;
2180
- }
2181
- super(inputs);
2182
- this.inputs = inputs;
2183
- }
2184
- onKeydown(_) {}
2185
- onPointerdown(_) {}
2186
- setDefaultState() {}
2187
- focus = item => this.listBehavior.goto(item);
2188
- next = () => this.listBehavior.next();
2189
- prev = () => this.listBehavior.prev();
2190
- last = () => this.listBehavior.last();
2191
- first = () => this.listBehavior.first();
2192
- unfocus = () => this.listBehavior.unfocus();
2193
- select = item => this.listBehavior.select(item);
2194
- toggle = item => this.listBehavior.toggle(item);
2195
- clearSelection = () => this.listBehavior.deselectAll();
2196
- getItem = e => this._getItem(e);
2197
- getSelectedItems = () => this.inputs.allItems().filter(item => item.selected());
2198
- setValue = value => this.inputs.values.set(value ? [value] : []);
2199
- expandItem = () => this.expand();
2200
- collapseItem = () => this.collapse();
2201
- isItemExpandable(item = this.inputs.activeItem()) {
2202
- return item ? item.expandable() : false;
2203
- }
2204
- expandAll = () => this.items().forEach(item => this.expansionBehavior.open(item));
2205
- collapseAll = () => this.items().forEach(item => item.expansionBehavior.close(item));
2206
- isItemSelectable = (item = this.inputs.activeItem()) => {
2207
- return item ? item.selectable() : false;
2208
- };
2209
- }
2210
-
2211
- class DeferredContentAware {
2212
- contentVisible = signal(false, ...(ngDevMode ? [{
2213
- debugName: "contentVisible"
2214
- }] : []));
2215
- preserveContent = model(false, ...(ngDevMode ? [{
2216
- debugName: "preserveContent"
2217
- }] : []));
2218
- static ɵfac = i0.ɵɵngDeclareFactory({
2219
- minVersion: "12.0.0",
2220
- version: "21.0.0",
2221
- ngImport: i0,
2222
- type: DeferredContentAware,
2223
- deps: [],
2224
- target: i0.ɵɵFactoryTarget.Directive
2225
- });
2226
- static ɵdir = i0.ɵɵngDeclareDirective({
2227
- minVersion: "17.1.0",
2228
- version: "21.0.0",
2229
- type: DeferredContentAware,
2230
- isStandalone: true,
2231
- inputs: {
2232
- preserveContent: {
2233
- classPropertyName: "preserveContent",
2234
- publicName: "preserveContent",
2235
- isSignal: true,
2236
- isRequired: false,
2237
- transformFunction: null
2238
- }
2239
- },
2240
- outputs: {
2241
- preserveContent: "preserveContentChange"
2242
- },
2243
- ngImport: i0
2244
- });
2245
- }
2246
- i0.ɵɵngDeclareClassMetadata({
2247
- minVersion: "12.0.0",
2248
- version: "21.0.0",
2249
- ngImport: i0,
2250
- type: DeferredContentAware,
2251
- decorators: [{
2252
- type: Directive
2253
- }],
2254
- propDecorators: {
2255
- preserveContent: [{
2256
- type: i0.Input,
2257
- args: [{
2258
- isSignal: true,
2259
- alias: "preserveContent",
2260
- required: false
2261
- }]
2262
- }, {
2263
- type: i0.Output,
2264
- args: ["preserveContentChange"]
2265
- }]
2266
- }
2267
- });
2268
- class DeferredContent {
2269
- _deferredContentAware = inject(DeferredContentAware, {
2270
- optional: true
2271
- });
2272
- _templateRef = inject(TemplateRef);
2273
- _viewContainerRef = inject(ViewContainerRef);
2274
- _currentViewRef = null;
2275
- _isRendered = false;
2276
- deferredContentAware = signal(this._deferredContentAware, ...(ngDevMode ? [{
2277
- debugName: "deferredContentAware"
2278
- }] : []));
2279
- constructor() {
2280
- afterRenderEffect(() => {
2281
- if (this.deferredContentAware()?.contentVisible()) {
2282
- if (!this._isRendered) {
2283
- this._destroyContent();
2284
- this._currentViewRef = this._viewContainerRef.createEmbeddedView(this._templateRef);
2285
- this._isRendered = true;
2286
- }
2287
- } else if (!this.deferredContentAware()?.preserveContent()) {
2288
- this._destroyContent();
2289
- this._isRendered = false;
2290
- }
2291
- });
2292
- }
2293
- ngOnDestroy() {
2294
- this._destroyContent();
2295
- }
2296
- _destroyContent() {
2297
- const ref = this._currentViewRef;
2298
- if (ref && !ref.destroyed) {
2299
- ref.destroy();
2300
- this._currentViewRef = null;
2301
- }
2302
- }
2303
- static ɵfac = i0.ɵɵngDeclareFactory({
2304
- minVersion: "12.0.0",
2305
- version: "21.0.0",
2306
- ngImport: i0,
2307
- type: DeferredContent,
2308
- deps: [],
2309
- target: i0.ɵɵFactoryTarget.Directive
2310
- });
2311
- static ɵdir = i0.ɵɵngDeclareDirective({
2312
- minVersion: "14.0.0",
2313
- version: "21.0.0",
2314
- type: DeferredContent,
2315
- isStandalone: true,
2316
- ngImport: i0
2317
- });
2318
- }
2319
- i0.ɵɵngDeclareClassMetadata({
2320
- minVersion: "12.0.0",
2321
- version: "21.0.0",
2322
- ngImport: i0,
2323
- type: DeferredContent,
2324
- decorators: [{
2325
- type: Directive
2326
- }],
2327
- ctorParameters: () => []
2328
- });
2329
-
2330
- export { AccordionGroupPattern, AccordionPanelPattern, AccordionTriggerPattern, ComboboxDialogPattern, ComboboxListboxPattern, ComboboxPattern, ComboboxTreePattern, DeferredContent, DeferredContentAware, ListboxPattern, MenuBarPattern, MenuItemPattern, MenuPattern, MenuTriggerPattern, OptionPattern, TabListPattern, TabPanelPattern, TabPattern, ToolbarPattern, ToolbarWidgetGroupPattern, ToolbarWidgetPattern, TreeItemPattern, TreePattern, convertGetterSetterToWritableSignalLike };
11
+ export { DeferredContent, DeferredContentAware } from './_deferred-content-chunk.mjs';
12
+ import './_pointer-event-manager-chunk.mjs';
13
+ import './_list-chunk.mjs';
14
+ import './_list-navigation-chunk.mjs';
15
+ import './_expansion-chunk.mjs';
16
+ import '@angular/core';
2331
17
  //# sourceMappingURL=private.mjs.map