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