@angular/aria 21.0.0-rc.0 → 21.0.0-rc.2

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 (48) hide show
  1. package/_adev_assets/aria-accordion.json +443 -59
  2. package/_adev_assets/aria-combobox.json +345 -37
  3. package/_adev_assets/aria-grid.json +408 -71
  4. package/_adev_assets/aria-listbox.json +115 -35
  5. package/_adev_assets/aria-menu.json +492 -167
  6. package/_adev_assets/aria-tabs.json +272 -88
  7. package/_adev_assets/aria-toolbar.json +151 -133
  8. package/_adev_assets/aria-tree.json +182 -35
  9. package/fesm2022/_widget-chunk.mjs +643 -190
  10. package/fesm2022/_widget-chunk.mjs.map +1 -1
  11. package/fesm2022/accordion.mjs +129 -77
  12. package/fesm2022/accordion.mjs.map +1 -1
  13. package/fesm2022/aria.mjs +1 -1
  14. package/fesm2022/aria.mjs.map +1 -1
  15. package/fesm2022/combobox.mjs +140 -27
  16. package/fesm2022/combobox.mjs.map +1 -1
  17. package/fesm2022/grid.mjs +254 -68
  18. package/fesm2022/grid.mjs.map +1 -1
  19. package/fesm2022/listbox.mjs +54 -44
  20. package/fesm2022/listbox.mjs.map +1 -1
  21. package/fesm2022/menu.mjs +270 -108
  22. package/fesm2022/menu.mjs.map +1 -1
  23. package/fesm2022/private.mjs +752 -785
  24. package/fesm2022/private.mjs.map +1 -1
  25. package/fesm2022/tabs.mjs +101 -71
  26. package/fesm2022/tabs.mjs.map +1 -1
  27. package/fesm2022/toolbar.mjs +87 -64
  28. package/fesm2022/toolbar.mjs.map +1 -1
  29. package/fesm2022/tree.mjs +105 -60
  30. package/fesm2022/tree.mjs.map +1 -1
  31. package/package.json +2 -10
  32. package/types/_grid-chunk.d.ts +326 -83
  33. package/types/accordion.d.ts +134 -35
  34. package/types/combobox.d.ts +146 -13
  35. package/types/grid.d.ts +159 -32
  36. package/types/listbox.d.ts +59 -28
  37. package/types/menu.d.ts +151 -55
  38. package/types/private.d.ts +449 -567
  39. package/types/tabs.d.ts +121 -41
  40. package/types/toolbar.d.ts +74 -51
  41. package/types/tree.d.ts +116 -45
  42. package/_adev_assets/aria-radio-group.json +0 -389
  43. package/fesm2022/deferred-content.mjs +0 -99
  44. package/fesm2022/deferred-content.mjs.map +0 -1
  45. package/fesm2022/radio-group.mjs +0 -338
  46. package/fesm2022/radio-group.mjs.map +0 -1
  47. package/types/deferred-content.d.ts +0 -38
  48. package/types/radio-group.d.ts +0 -84
@@ -1,4 +1,4 @@
1
- import { computed, signal, linkedSignal } from '@angular/core';
1
+ import { signal, computed, linkedSignal, untracked } from '@angular/core';
2
2
 
3
3
  var Modifier;
4
4
  (function (Modifier) {
@@ -46,23 +46,28 @@ class KeyboardEventManager extends EventManager {
46
46
  const {
47
47
  modifiers,
48
48
  key,
49
- handler
49
+ handler,
50
+ options
50
51
  } = this._normalizeInputs(...args);
51
52
  this.configs.push({
52
53
  handler: handler,
53
54
  matcher: event => this._isMatch(event, key, modifiers),
54
- ...this.options
55
+ ...this.options,
56
+ ...options
55
57
  });
56
58
  return this;
57
59
  }
58
60
  _normalizeInputs(...args) {
59
- const key = args.length === 3 ? args[1] : args[0];
60
- const handler = args.length === 3 ? args[2] : args[1];
61
- const modifiers = args.length === 3 ? args[0] : Modifier.None;
61
+ const withModifiers = Array.isArray(args[0]) || args[0] in Modifier;
62
+ const modifiers = withModifiers ? args[0] : Modifier.None;
63
+ const key = withModifiers ? args[1] : args[0];
64
+ const handler = withModifiers ? args[2] : args[1];
65
+ const options = withModifiers ? args[3] : args[2];
62
66
  return {
63
67
  key: key,
64
68
  handler: handler,
65
- modifiers: modifiers
69
+ modifiers: modifiers,
70
+ options: options ?? {}
66
71
  };
67
72
  }
68
73
  _isMatch(event, key, modifiers) {
@@ -127,10 +132,121 @@ class PointerEventManager extends EventManager {
127
132
  }
128
133
  }
129
134
 
135
+ class ListFocus {
136
+ inputs;
137
+ prevActiveItem = signal(undefined);
138
+ prevActiveIndex = computed(() => {
139
+ return this.prevActiveItem() ? this.inputs.items().indexOf(this.prevActiveItem()) : -1;
140
+ });
141
+ activeIndex = computed(() => {
142
+ return this.inputs.activeItem() ? this.inputs.items().indexOf(this.inputs.activeItem()) : -1;
143
+ });
144
+ constructor(inputs) {
145
+ this.inputs = inputs;
146
+ }
147
+ isListDisabled() {
148
+ return this.inputs.disabled() || this.inputs.items().every(i => i.disabled());
149
+ }
150
+ getActiveDescendant() {
151
+ if (this.isListDisabled()) {
152
+ return undefined;
153
+ }
154
+ if (this.inputs.focusMode() === 'roving') {
155
+ return undefined;
156
+ }
157
+ return this.inputs.activeItem()?.id() ?? undefined;
158
+ }
159
+ getListTabIndex() {
160
+ if (this.isListDisabled()) {
161
+ return 0;
162
+ }
163
+ return this.inputs.focusMode() === 'activedescendant' ? 0 : -1;
164
+ }
165
+ getItemTabIndex(item) {
166
+ if (this.isListDisabled()) {
167
+ return -1;
168
+ }
169
+ if (this.inputs.focusMode() === 'activedescendant') {
170
+ return -1;
171
+ }
172
+ return this.inputs.activeItem() === item ? 0 : -1;
173
+ }
174
+ focus(item, opts) {
175
+ if (this.isListDisabled() || !this.isFocusable(item)) {
176
+ return false;
177
+ }
178
+ this.prevActiveItem.set(this.inputs.activeItem());
179
+ this.inputs.activeItem.set(item);
180
+ if (opts?.focusElement || opts?.focusElement === undefined) {
181
+ this.inputs.focusMode() === 'roving' ? item.element()?.focus() : this.inputs.element()?.focus();
182
+ }
183
+ return true;
184
+ }
185
+ isFocusable(item) {
186
+ return !item.disabled() || this.inputs.softDisabled();
187
+ }
188
+ }
189
+
190
+ class ListNavigation {
191
+ inputs;
192
+ constructor(inputs) {
193
+ this.inputs = inputs;
194
+ }
195
+ goto(item, opts) {
196
+ return item ? this.inputs.focusManager.focus(item, opts) : false;
197
+ }
198
+ next(opts) {
199
+ return this._advance(1, opts);
200
+ }
201
+ peekNext() {
202
+ return this._peek(1);
203
+ }
204
+ prev(opts) {
205
+ return this._advance(-1, opts);
206
+ }
207
+ peekPrev() {
208
+ return this._peek(-1);
209
+ }
210
+ first(opts) {
211
+ const item = this.peekFirst();
212
+ return item ? this.goto(item, opts) : false;
213
+ }
214
+ last(opts) {
215
+ const item = this.peekLast();
216
+ return item ? this.goto(item, opts) : false;
217
+ }
218
+ peekFirst(items = this.inputs.items()) {
219
+ return items.find(i => this.inputs.focusManager.isFocusable(i));
220
+ }
221
+ peekLast(items = this.inputs.items()) {
222
+ for (let i = items.length - 1; i >= 0; i--) {
223
+ if (this.inputs.focusManager.isFocusable(items[i])) {
224
+ return items[i];
225
+ }
226
+ }
227
+ return;
228
+ }
229
+ _advance(delta, opts) {
230
+ const item = this._peek(delta);
231
+ return item ? this.goto(item, opts) : false;
232
+ }
233
+ _peek(delta) {
234
+ const items = this.inputs.items();
235
+ const itemCount = items.length;
236
+ const startIndex = this.inputs.focusManager.activeIndex();
237
+ const step = i => this.inputs.wrap() ? (i + delta + itemCount) % itemCount : i + delta;
238
+ for (let i = step(startIndex); i !== startIndex && i < itemCount && i >= 0; i = step(i)) {
239
+ if (this.inputs.focusManager.isFocusable(items[i])) {
240
+ return items[i];
241
+ }
242
+ }
243
+ return;
244
+ }
245
+ }
246
+
130
247
  class GridData {
131
248
  inputs;
132
249
  cells;
133
- rowCount = computed(() => this.cells().length);
134
250
  maxRowCount = computed(() => Math.max(...this._rowCountByCol().values(), 0));
135
251
  maxColCount = computed(() => Math.max(...this._colCountsByRow().values(), 0));
136
252
  _coordsMap = computed(() => {
@@ -220,6 +336,9 @@ class GridData {
220
336
  this.inputs = inputs;
221
337
  this.cells = this.inputs.cells;
222
338
  }
339
+ hasCell(cell) {
340
+ return this._coordsMap().has(cell);
341
+ }
223
342
  getCell(rowCol) {
224
343
  return this._cellMap().get(`${rowCol.row}:${rowCol.col}`);
225
344
  }
@@ -280,7 +399,7 @@ class GridFocus {
280
399
  constructor(inputs) {
281
400
  this.inputs = inputs;
282
401
  }
283
- getCellTabindex(cell) {
402
+ getCellTabIndex(cell) {
284
403
  if (this.gridDisabled()) {
285
404
  return -1;
286
405
  }
@@ -290,7 +409,7 @@ class GridFocus {
290
409
  return this.activeCell() === cell ? 0 : -1;
291
410
  }
292
411
  isFocusable(cell) {
293
- return !cell.disabled() || !this.inputs.skipDisabled();
412
+ return this.inputs.grid.hasCell(cell) && (!cell.disabled() || this.inputs.softDisabled());
294
413
  }
295
414
  focusCell(cell) {
296
415
  if (this.gridDisabled()) {
@@ -349,37 +468,38 @@ class GridNavigation {
349
468
  gotoCoords(coords) {
350
469
  return this.inputs.gridFocus.focusCoordinates(coords);
351
470
  }
352
- peek(direction, fromCoords, wrap) {
471
+ peek(direction, fromCoords, wrap, allowDisabled) {
353
472
  wrap = wrap ?? (direction.row !== undefined ? this.inputs.rowWrap() : this.inputs.colWrap());
354
- return this._peekDirectional(direction, fromCoords, wrap);
473
+ return this._peekDirectional(direction, fromCoords, wrap, allowDisabled);
355
474
  }
356
475
  advance(direction) {
357
476
  const nextCoords = this.peek(direction, this.inputs.gridFocus.activeCoords());
358
477
  return !!nextCoords && this.gotoCoords(nextCoords);
359
478
  }
360
- peekFirst(row) {
479
+ peekFirst(row, allowDisabled) {
361
480
  const fromCoords = {
362
481
  row: row ?? 0,
363
482
  col: -1
364
483
  };
365
- return row === undefined ? this._peekDirectional(direction.Right, fromCoords, 'continuous') : this._peekDirectional(direction.Right, fromCoords, 'nowrap');
484
+ return row === undefined ? this._peekDirectional(direction.Right, fromCoords, 'continuous', allowDisabled) : this._peekDirectional(direction.Right, fromCoords, 'nowrap', allowDisabled);
366
485
  }
367
486
  first(row) {
368
487
  const nextCoords = this.peekFirst(row);
369
488
  return !!nextCoords && this.gotoCoords(nextCoords);
370
489
  }
371
- peekLast(row) {
490
+ peekLast(row, allowDisabled) {
372
491
  const fromCoords = {
373
492
  row: row ?? this.inputs.grid.maxRowCount() - 1,
374
493
  col: this.inputs.grid.maxColCount()
375
494
  };
376
- return row === undefined ? this._peekDirectional(direction.Left, fromCoords, 'continuous') : this._peekDirectional(direction.Left, fromCoords, 'nowrap');
495
+ return row === undefined ? this._peekDirectional(direction.Left, fromCoords, 'continuous', allowDisabled) : this._peekDirectional(direction.Left, fromCoords, 'nowrap', allowDisabled);
377
496
  }
378
497
  last(row) {
379
498
  const nextCoords = this.peekLast(row);
380
499
  return !!nextCoords && this.gotoCoords(nextCoords);
381
500
  }
382
- _peekDirectional(delta, fromCoords, wrap) {
501
+ _peekDirectional(delta, fromCoords, wrap, allowDisabled = false) {
502
+ if (this.inputs.gridFocus.gridDisabled()) return undefined;
383
503
  const fromCell = this.inputs.grid.getCell(fromCoords);
384
504
  const maxRowCount = this.inputs.grid.maxRowCount();
385
505
  const maxColCount = this.inputs.grid.maxColCount();
@@ -390,11 +510,13 @@ class GridNavigation {
390
510
  };
391
511
  for (let step = 0; step < this._maxSteps(); step++) {
392
512
  const isWrapping = nextCoords.col + colDelta < 0 || nextCoords.col + colDelta >= maxColCount || nextCoords.row + rowDelta < 0 || nextCoords.row + rowDelta >= maxRowCount;
393
- if (wrap === 'nowrap' && isWrapping) return;
513
+ if (wrap === 'nowrap' && isWrapping) return undefined;
394
514
  if (wrap === 'continuous') {
395
515
  const generalDelta = delta.row ?? delta.col;
396
516
  const rowStep = isWrapping ? generalDelta : rowDelta;
397
517
  const colStep = isWrapping ? generalDelta : colDelta;
518
+ const bothWrapping = nextCoords.row + rowStep >= maxRowCount && nextCoords.col + colStep >= maxColCount || nextCoords.row + rowStep < 0 && nextCoords.col + colStep < 0;
519
+ if (bothWrapping) return undefined;
398
520
  nextCoords = {
399
521
  row: (nextCoords.row + rowStep + maxRowCount) % maxRowCount,
400
522
  col: (nextCoords.col + colStep + maxColCount) % maxColCount
@@ -406,11 +528,17 @@ class GridNavigation {
406
528
  col: (nextCoords.col + colDelta + maxColCount) % maxColCount
407
529
  };
408
530
  }
531
+ if (wrap === 'nowrap') {
532
+ nextCoords = {
533
+ row: nextCoords.row + rowDelta,
534
+ col: nextCoords.col + colDelta
535
+ };
536
+ }
409
537
  if (nextCoords.row === fromCoords.row && nextCoords.col === fromCoords.col) {
410
538
  return undefined;
411
539
  }
412
540
  const nextCell = this.inputs.grid.getCell(nextCoords);
413
- if (nextCell !== undefined && nextCell !== fromCell && this.inputs.gridFocus.isFocusable(nextCell)) {
541
+ if (nextCell !== undefined && nextCell !== fromCell && (allowDisabled || this.inputs.gridFocus.isFocusable(nextCell))) {
414
542
  return nextCoords;
415
543
  }
416
544
  }
@@ -420,45 +548,45 @@ class GridNavigation {
420
548
 
421
549
  class GridSelection {
422
550
  inputs;
551
+ _undoList = signal([]);
423
552
  constructor(inputs) {
424
553
  this.inputs = inputs;
425
554
  }
426
- select(fromCoords, toCoords) {
427
- for (const cell of this._validCells(fromCoords, toCoords ?? fromCoords)) {
428
- cell.selected.set(true);
555
+ undo() {
556
+ for (const [cell, oldState] of this._undoList()) {
557
+ cell.selected.set(oldState);
429
558
  }
559
+ this._undoList.set([]);
560
+ }
561
+ select(fromCoords, toCoords) {
562
+ this._updateState(fromCoords, toCoords ?? fromCoords, () => true);
430
563
  }
431
564
  deselect(fromCoords, toCoords) {
432
- for (const cell of this._validCells(fromCoords, toCoords ?? fromCoords)) {
433
- cell.selected.set(false);
434
- }
565
+ this._updateState(fromCoords, toCoords ?? fromCoords, () => false);
435
566
  }
436
567
  toggle(fromCoords, toCoords) {
437
- for (const cell of this._validCells(fromCoords, toCoords ?? fromCoords)) {
438
- cell.selected.update(state => !state);
439
- }
568
+ this._updateState(fromCoords, toCoords ?? fromCoords, oldState => !oldState);
440
569
  }
441
570
  selectAll() {
442
- for (const cell of this._validCells({
571
+ this._updateState({
443
572
  row: 0,
444
573
  col: 0
445
574
  }, {
446
575
  row: this.inputs.grid.maxRowCount(),
447
576
  col: this.inputs.grid.maxColCount()
448
- })) {
449
- cell.selected.set(true);
450
- }
577
+ }, () => true);
451
578
  }
452
579
  deselectAll() {
453
- for (const cell of this._validCells({
580
+ this._updateState({
454
581
  row: 0,
455
582
  col: 0
456
583
  }, {
457
584
  row: this.inputs.grid.maxRowCount(),
458
585
  col: this.inputs.grid.maxColCount()
459
- })) {
460
- cell.selected.set(false);
461
- }
586
+ }, () => false);
587
+ }
588
+ isSelectable(cell) {
589
+ return cell.selectable() && !cell.disabled();
462
590
  }
463
591
  *_validCells(fromCoords, toCoords) {
464
592
  const startRow = Math.min(fromCoords.row, toCoords.row);
@@ -473,14 +601,22 @@ class GridSelection {
473
601
  col
474
602
  });
475
603
  if (cell === undefined) continue;
476
- if (!cell.selectable()) continue;
477
- if (cell.disabled()) continue;
604
+ if (!this.isSelectable(cell)) continue;
478
605
  if (visited.has(cell)) continue;
479
606
  visited.add(cell);
480
607
  yield cell;
481
608
  }
482
609
  }
483
610
  }
611
+ _updateState(fromCoords, toCoords, stateFn) {
612
+ const undoList = [];
613
+ for (const cell of this._validCells(fromCoords, toCoords)) {
614
+ const oldState = cell.selected();
615
+ undoList.push([cell, oldState]);
616
+ cell.selected.set(stateFn(oldState));
617
+ }
618
+ this._undoList.set(undoList);
619
+ }
484
620
  }
485
621
 
486
622
  class Grid {
@@ -490,9 +626,12 @@ class Grid {
490
626
  navigationBehavior;
491
627
  selectionBehavior;
492
628
  selectionAnchor = linkedSignal(() => this.focusBehavior.activeCoords());
493
- gridTabIndex = computed(() => this.focusBehavior.gridTabIndex());
494
- gridDisabled = computed(() => this.focusBehavior.gridDisabled());
495
- activeDescendant = computed(() => this.focusBehavior.activeDescendant());
629
+ selectionAnchorCell = computed(() => this.data.getCell(this.selectionAnchor()));
630
+ selectionStabled = signal(true);
631
+ allSelected = computed(() => this.data.cells().flat().filter(c => this.selectionBehavior.isSelectable(c)).every(c => c.selected()));
632
+ gridTabIndex = () => this.focusBehavior.gridTabIndex();
633
+ gridDisabled = () => this.focusBehavior.gridDisabled();
634
+ activeDescendant = () => this.focusBehavior.activeDescendant();
496
635
  constructor(inputs) {
497
636
  this.inputs = inputs;
498
637
  this.data = new GridData(inputs);
@@ -520,51 +659,33 @@ class Grid {
520
659
  return index !== undefined ? index + 1 : undefined;
521
660
  }
522
661
  cellTabIndex(cell) {
523
- return this.focusBehavior.getCellTabindex(cell);
524
- }
525
- up() {
526
- return this.navigationBehavior.advance(direction.Up);
527
- }
528
- rangeSelectUp() {
529
- const coords = this.navigationBehavior.peek(direction.Up, this.selectionAnchor());
530
- if (coords === undefined) return;
531
- this._rangeSelectCoords(coords);
532
- }
533
- down() {
534
- return this.navigationBehavior.advance(direction.Down);
662
+ return this.focusBehavior.getCellTabIndex(cell);
535
663
  }
536
- rangeSelectDown() {
537
- const coords = this.navigationBehavior.peek(direction.Down, this.selectionAnchor());
538
- if (coords === undefined) return;
539
- this._rangeSelectCoords(coords);
664
+ up(opts = {}) {
665
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peek(direction.Up, this.selectionAnchor(), 'nowrap', true)) : this.navigationBehavior.advance(direction.Up), opts);
540
666
  }
541
- left() {
542
- return this.navigationBehavior.advance(direction.Left);
667
+ down(opts = {}) {
668
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peek(direction.Down, this.selectionAnchor(), 'nowrap', true)) : this.navigationBehavior.advance(direction.Down), opts);
543
669
  }
544
- rangeSelectLeft() {
545
- const coords = this.navigationBehavior.peek(direction.Left, this.selectionAnchor());
546
- if (coords === undefined) return;
547
- this._rangeSelectCoords(coords);
670
+ left(opts = {}) {
671
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peek(direction.Left, this.selectionAnchor(), 'nowrap', true)) : this.navigationBehavior.advance(direction.Left), opts);
548
672
  }
549
- right() {
550
- return this.navigationBehavior.advance(direction.Right);
673
+ right(opts = {}) {
674
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peek(direction.Right, this.selectionAnchor(), 'nowrap', true)) : this.navigationBehavior.advance(direction.Right), opts);
551
675
  }
552
- rangeSelectRight() {
553
- const coords = this.navigationBehavior.peek(direction.Right, this.selectionAnchor());
554
- if (coords === undefined) return;
555
- this._rangeSelectCoords(coords);
676
+ first(opts = {}) {
677
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peekFirst(undefined, true)) : this.navigationBehavior.first(), opts);
556
678
  }
557
- first() {
558
- return this.navigationBehavior.first();
559
- }
560
- firstInRow() {
561
- return this.navigationBehavior.first(this.focusBehavior.activeCoords().row);
679
+ firstInRow(opts = {}) {
680
+ const row = this.focusBehavior.activeCoords().row;
681
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peekFirst(row, true)) : this.navigationBehavior.first(row), opts);
562
682
  }
563
- last() {
564
- return this.navigationBehavior.last();
683
+ last(opts = {}) {
684
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peekLast(undefined, true)) : this.navigationBehavior.last(), opts);
565
685
  }
566
- lastInRow() {
567
- return this.navigationBehavior.last(this.focusBehavior.activeCoords().row);
686
+ lastInRow(opts = {}) {
687
+ const row = this.focusBehavior.activeCoords().row;
688
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peekLast(row, true)) : this.navigationBehavior.last(row), opts);
568
689
  }
569
690
  selectRow() {
570
691
  const row = this.focusBehavior.activeCoords().row;
@@ -588,50 +709,48 @@ class Grid {
588
709
  col
589
710
  });
590
711
  }
591
- selectAll() {
592
- this.selectionBehavior.selectAll();
712
+ select() {
713
+ this.selectionBehavior.select(this.focusBehavior.activeCoords());
593
714
  }
594
- gotoCell(cell) {
595
- return this.navigationBehavior.gotoCell(cell);
596
- }
597
- toggleSelect(cell) {
598
- const coords = this.data.getCoords(cell);
599
- if (coords === undefined) return;
600
- this.selectionBehavior.toggle(coords);
601
- }
602
- rangeSelect(cell) {
603
- const coords = this.data.getCoords(cell);
604
- if (coords === undefined) return;
605
- this._rangeSelectCoords(coords);
606
- }
607
- _rangeSelectCoords(coords) {
608
- const activeCell = this.focusBehavior.activeCell();
609
- const anchorCell = this.data.getCell(coords);
610
- if (activeCell === undefined || anchorCell === undefined) {
715
+ deselect() {
716
+ this.selectionBehavior.deselect(this.focusBehavior.activeCoords());
717
+ }
718
+ toggle() {
719
+ this.selectionBehavior.toggle(this.focusBehavior.activeCoords());
720
+ }
721
+ toggleOne() {
722
+ const selected = !!this.focusBehavior.activeCell()?.selected();
723
+ if (selected) {
724
+ this.deselect();
611
725
  return;
612
726
  }
613
- const allCoords = [...this.data.getAllCoords(activeCell), ...this.data.getAllCoords(anchorCell)];
614
- const allRows = allCoords.map(c => c.row);
615
- const allCols = allCoords.map(c => c.col);
616
- const fromCoords = {
617
- row: Math.min(...allRows),
618
- col: Math.min(...allCols)
619
- };
620
- const toCoords = {
621
- row: Math.max(...allRows),
622
- col: Math.max(...allCols)
623
- };
727
+ this.deselectAll();
728
+ this.select();
729
+ }
730
+ selectAll() {
731
+ this.selectionBehavior.selectAll();
732
+ }
733
+ deselectAll() {
624
734
  this.selectionBehavior.deselectAll();
625
- this.selectionBehavior.select(fromCoords, toCoords);
626
- this.selectionAnchor.set(coords);
735
+ }
736
+ gotoCell(cell, opts = {}) {
737
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.data.getCoords(cell)) : this.navigationBehavior.gotoCell(cell), opts);
738
+ }
739
+ setDefaultState() {
740
+ const focusableSelectedCell = this.data.cells().flat().filter(c => this.focusBehavior.isFocusable(c)).find(c => c.selected());
741
+ if (focusableSelectedCell !== undefined) {
742
+ this.focusBehavior.focusCell(focusableSelectedCell);
743
+ return true;
744
+ }
745
+ const firstFocusableCoords = this.navigationBehavior.peekFirst();
746
+ if (firstFocusableCoords !== undefined) {
747
+ return this.focusBehavior.focusCoordinates(firstFocusableCoords);
748
+ }
749
+ return false;
627
750
  }
628
751
  resetState() {
629
752
  if (this.focusBehavior.stateEmpty()) {
630
- const firstFocusableCoords = this.navigationBehavior.peekFirst();
631
- if (firstFocusableCoords === undefined) {
632
- return false;
633
- }
634
- return this.focusBehavior.focusCoordinates(firstFocusableCoords);
753
+ return this.setDefaultState();
635
754
  }
636
755
  if (this.focusBehavior.stateStale()) {
637
756
  if (this.focusBehavior.focusCell(this.focusBehavior.activeCell())) {
@@ -646,6 +765,67 @@ class Grid {
646
765
  }
647
766
  return false;
648
767
  }
768
+ _updateSelectionAnchor(peekFn) {
769
+ const coords = peekFn();
770
+ const success = coords !== undefined;
771
+ if (!success) return false;
772
+ this.selectionAnchor.set(coords);
773
+ return success;
774
+ }
775
+ _updateRangeSelection() {
776
+ if (!this.selectionStabled()) {
777
+ this.selectionBehavior.undo();
778
+ }
779
+ this.selectionBehavior.select(...this._getSelectionCoords(this.focusBehavior.activeCoords(), this.selectionAnchor()));
780
+ }
781
+ _getSelectionCoords(startCoords, endCoords) {
782
+ const startCell = this.data.getCell(startCoords);
783
+ const endCell = this.data.getCell(endCoords);
784
+ const allCoords = [...this.data.getAllCoords(startCell), ...this.data.getAllCoords(endCell)];
785
+ const allRows = allCoords.map(c => c.row);
786
+ const allCols = allCoords.map(c => c.col);
787
+ const fromCoords = {
788
+ row: Math.min(...allRows),
789
+ col: Math.min(...allCols)
790
+ };
791
+ const toCoords = {
792
+ row: Math.max(...allRows),
793
+ col: Math.max(...allCols)
794
+ };
795
+ return [fromCoords, toCoords];
796
+ }
797
+ _navigateWithSelection(op, opts = {}) {
798
+ const success = op();
799
+ if (!success) return false;
800
+ if (opts.anchor) {
801
+ this._updateRangeSelection();
802
+ this.selectionStabled.set(false);
803
+ return success;
804
+ }
805
+ this.selectionStabled.set(true);
806
+ if (opts.select) {
807
+ this.select();
808
+ return success;
809
+ }
810
+ if (opts.selectOne) {
811
+ this.deselectAll();
812
+ this.select();
813
+ return success;
814
+ }
815
+ if (opts.toggle) {
816
+ this.toggle();
817
+ return success;
818
+ }
819
+ if (opts.toggleOne) {
820
+ const selected = !!this.focusBehavior.activeCell()?.selected();
821
+ this.deselectAll();
822
+ if (!selected) {
823
+ this.select();
824
+ }
825
+ return success;
826
+ }
827
+ return success;
828
+ }
649
829
  }
650
830
 
651
831
  class GridPattern {
@@ -656,53 +836,111 @@ class GridPattern {
656
836
  disabled = computed(() => this.gridBehavior.gridDisabled());
657
837
  activeDescendant = computed(() => this.gridBehavior.activeDescendant());
658
838
  activeCell = computed(() => this.gridBehavior.focusBehavior.activeCell());
659
- pauseNavigation = computed(() => this.gridBehavior.data.cells().flat().reduce((res, c) => res || c.widgetActivated(), false));
839
+ anchorCell = computed(() => this.inputs.enableSelection() && this.inputs.multi() ? this.gridBehavior.selectionAnchorCell() : undefined);
840
+ pauseNavigation = computed(() => this.gridBehavior.data.cells().flat().reduce((res, c) => res || c.isActivated(), false));
660
841
  isFocused = signal(false);
842
+ hasBeenFocused = signal(false);
661
843
  dragging = signal(false);
844
+ prevColKey = computed(() => this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft');
845
+ nextColKey = computed(() => this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight');
662
846
  keydown = computed(() => {
663
847
  const manager = new KeyboardEventManager();
664
848
  if (this.pauseNavigation()) {
665
849
  return manager;
666
850
  }
667
- manager.on('ArrowUp', () => this.gridBehavior.up()).on('ArrowDown', () => this.gridBehavior.down()).on('ArrowLeft', () => this.gridBehavior.left()).on('ArrowRight', () => this.gridBehavior.right()).on('Home', () => this.gridBehavior.firstInRow()).on('End', () => this.gridBehavior.lastInRow()).on([Modifier.Ctrl], 'Home', () => this.gridBehavior.first()).on([Modifier.Ctrl], 'End', () => this.gridBehavior.last());
668
- if (this.inputs.enableSelection()) {
669
- manager.on(Modifier.Shift, 'ArrowUp', () => this.gridBehavior.rangeSelectUp()).on(Modifier.Shift, 'ArrowDown', () => this.gridBehavior.rangeSelectDown()).on(Modifier.Shift, 'ArrowLeft', () => this.gridBehavior.rangeSelectLeft()).on(Modifier.Shift, 'ArrowRight', () => this.gridBehavior.rangeSelectRight()).on([Modifier.Ctrl, Modifier.Meta], 'A', () => this.gridBehavior.selectAll()).on([Modifier.Shift], ' ', () => this.gridBehavior.selectRow()).on([Modifier.Ctrl, Modifier.Meta], ' ', () => this.gridBehavior.selectCol());
851
+ const opts = {
852
+ selectOne: this.inputs.enableSelection() && this.inputs.selectionMode() === 'follow'
853
+ };
854
+ manager.on('ArrowUp', () => this.gridBehavior.up(opts)).on('ArrowDown', () => this.gridBehavior.down(opts)).on(this.prevColKey(), () => this.gridBehavior.left(opts)).on(this.nextColKey(), () => this.gridBehavior.right(opts)).on('Home', () => this.gridBehavior.firstInRow(opts)).on('End', () => this.gridBehavior.lastInRow(opts)).on([Modifier.Ctrl], 'Home', () => this.gridBehavior.first(opts)).on([Modifier.Ctrl], 'End', () => this.gridBehavior.last(opts));
855
+ if (this.inputs.enableSelection() && this.inputs.selectionMode() === 'explicit') {
856
+ manager.on(/Enter| /, () => this.inputs.multi() ? this.gridBehavior.toggle() : this.gridBehavior.toggleOne());
857
+ }
858
+ if (this.inputs.enableSelection() && this.inputs.enableRangeSelection()) {
859
+ manager.on(Modifier.Shift, 'ArrowUp', () => this.gridBehavior.up({
860
+ anchor: true
861
+ })).on(Modifier.Shift, 'ArrowDown', () => this.gridBehavior.down({
862
+ anchor: true
863
+ })).on(Modifier.Shift, this.prevColKey(), () => this.gridBehavior.left({
864
+ anchor: true
865
+ })).on(Modifier.Shift, this.nextColKey(), () => this.gridBehavior.right({
866
+ anchor: true
867
+ })).on(Modifier.Shift, 'Home', () => this.gridBehavior.firstInRow({
868
+ anchor: true
869
+ })).on(Modifier.Shift, 'End', () => this.gridBehavior.lastInRow({
870
+ anchor: true
871
+ })).on([Modifier.Ctrl | Modifier.Shift], 'Home', () => this.gridBehavior.first({
872
+ anchor: true
873
+ })).on([Modifier.Ctrl | Modifier.Shift], 'End', () => this.gridBehavior.last({
874
+ anchor: true
875
+ })).on([Modifier.Ctrl, Modifier.Meta], 'A', () => {
876
+ if (this.gridBehavior.allSelected()) {
877
+ this.gridBehavior.deselectAll();
878
+ } else {
879
+ this.gridBehavior.selectAll();
880
+ }
881
+ }).on([Modifier.Shift], ' ', () => this.gridBehavior.selectRow()).on([Modifier.Ctrl, Modifier.Meta], ' ', () => this.gridBehavior.selectCol());
670
882
  }
671
883
  return manager;
672
884
  });
673
885
  pointerdown = computed(() => {
674
886
  const manager = new PointerEventManager();
675
- manager.on(e => {
676
- const cell = this.inputs.getCell(e.target);
677
- if (!cell) return;
678
- this.gridBehavior.gotoCell(cell);
679
- if (this.inputs.enableSelection()) {
680
- this.dragging.set(true);
681
- }
682
- });
683
- if (this.inputs.enableSelection()) {
684
- manager.on([Modifier.Ctrl, Modifier.Meta], e => {
887
+ if (!this.inputs.enableSelection()) {
888
+ manager.on(e => {
685
889
  const cell = this.inputs.getCell(e.target);
686
- if (!cell) return;
687
- this.gridBehavior.toggleSelect(cell);
688
- }).on(Modifier.Shift, e => {
890
+ if (!cell || !this.gridBehavior.focusBehavior.isFocusable(cell)) return;
891
+ this.gridBehavior.gotoCell(cell);
892
+ });
893
+ }
894
+ if (this.inputs.enableSelection()) {
895
+ manager.on(e => {
689
896
  const cell = this.inputs.getCell(e.target);
690
- if (!cell) return;
691
- this.gridBehavior.rangeSelect(cell);
692
- this.dragging.set(true);
897
+ if (!cell || !this.gridBehavior.focusBehavior.isFocusable(cell)) return;
898
+ this.gridBehavior.gotoCell(cell, {
899
+ selectOne: this.inputs.selectionMode() === 'follow',
900
+ toggleOne: this.inputs.selectionMode() === 'explicit' && !this.inputs.multi(),
901
+ toggle: this.inputs.selectionMode() === 'explicit' && this.inputs.multi()
902
+ });
903
+ if (this.inputs.multi() && this.inputs.enableRangeSelection()) {
904
+ this.dragging.set(true);
905
+ }
693
906
  });
907
+ if (this.inputs.multi()) {
908
+ manager.on([Modifier.Ctrl, Modifier.Meta], e => {
909
+ const cell = this.inputs.getCell(e.target);
910
+ if (!cell || !this.gridBehavior.focusBehavior.isFocusable(cell)) return;
911
+ this.gridBehavior.gotoCell(cell, {
912
+ toggle: true
913
+ });
914
+ if (this.inputs.enableRangeSelection()) {
915
+ this.dragging.set(true);
916
+ }
917
+ });
918
+ if (this.inputs.enableRangeSelection()) {
919
+ manager.on(Modifier.Shift, e => {
920
+ const cell = this.inputs.getCell(e.target);
921
+ if (!cell) return;
922
+ this.gridBehavior.gotoCell(cell, {
923
+ anchor: true
924
+ });
925
+ this.dragging.set(true);
926
+ });
927
+ }
928
+ }
694
929
  }
695
930
  return manager;
696
931
  });
697
932
  pointerup = computed(() => {
698
933
  const manager = new PointerEventManager();
699
- if (this.inputs.enableSelection()) {
700
- manager.on([Modifier.Shift, Modifier.None], () => {
934
+ if (this.inputs.enableSelection() && this.inputs.enableRangeSelection()) {
935
+ manager.on([Modifier.Shift, Modifier.Ctrl, Modifier.Meta, Modifier.None], () => {
701
936
  this.dragging.set(false);
702
937
  });
703
938
  }
704
939
  return manager;
705
940
  });
941
+ _maybeDeletion = signal(false);
942
+ _deletion = signal(false);
943
+ _stateStale = signal(false);
706
944
  constructor(inputs) {
707
945
  this.inputs = inputs;
708
946
  this.gridBehavior = new Grid({
@@ -711,65 +949,100 @@ class GridPattern {
711
949
  });
712
950
  }
713
951
  onKeydown(event) {
714
- if (!this.disabled()) {
715
- this.keydown().handle(event);
716
- }
952
+ if (this.disabled()) return;
953
+ this.activeCell()?.onKeydown(event);
954
+ this.keydown().handle(event);
717
955
  }
718
956
  onPointerdown(event) {
719
- if (!this.disabled()) {
720
- this.pointerdown().handle(event);
721
- }
957
+ if (this.disabled()) return;
958
+ this.pointerdown().handle(event);
722
959
  }
723
960
  onPointermove(event) {
724
- if (this.disabled()) return;
725
- if (!this.inputs.enableSelection()) return;
726
- if (!this.dragging()) return;
961
+ if (this.disabled() || !this.inputs.enableSelection() || !this.inputs.enableRangeSelection() || !this.dragging()) {
962
+ return;
963
+ }
727
964
  const cell = this.inputs.getCell(event.target);
728
- if (!cell) return;
729
- this.gridBehavior.rangeSelect(cell);
965
+ if (cell !== undefined) {
966
+ this.gridBehavior.gotoCell(cell, {
967
+ anchor: true
968
+ });
969
+ }
730
970
  }
731
971
  onPointerup(event) {
732
- if (!this.disabled()) {
733
- this.pointerup().handle(event);
734
- }
972
+ if (this.disabled()) return;
973
+ this.pointerup().handle(event);
735
974
  }
736
975
  onFocusIn(event) {
737
976
  this.isFocused.set(true);
977
+ this.hasBeenFocused.set(true);
978
+ if (this.dragging()) return;
738
979
  const cell = this.inputs.getCell(event.target);
739
- if (!cell) return;
740
- this.gridBehavior.gotoCell(cell);
980
+ if (!cell || !this.gridBehavior.focusBehavior.isFocusable(cell)) return;
981
+ cell.onFocusIn(event);
982
+ if (cell !== this.activeCell()) {
983
+ this.gridBehavior.gotoCell(cell);
984
+ }
741
985
  }
742
- _maybeDeletion = signal(false);
743
986
  onFocusOut(event) {
744
- const parentEl = this.inputs.element();
745
- const targetEl = event.relatedTarget;
746
- if (targetEl === null) {
987
+ const blurTarget = event.target;
988
+ const cell = this.inputs.getCell(blurTarget);
989
+ cell?.onFocusOut(event);
990
+ const focusTarget = event.relatedTarget;
991
+ if (this.inputs.element().contains(focusTarget)) return;
992
+ if (focusTarget === null) {
747
993
  this._maybeDeletion.set(true);
748
994
  }
749
- if (parentEl.contains(targetEl)) return;
750
995
  this.isFocused.set(false);
751
996
  }
752
- _deletion = signal(false);
997
+ setDefaultStateEffect() {
998
+ if (this.hasBeenFocused()) return;
999
+ this.gridBehavior.setDefaultState();
1000
+ }
753
1001
  resetStateEffect() {
754
1002
  const hasReset = this.gridBehavior.resetState();
755
- if (hasReset && this._maybeDeletion()) {
756
- this._deletion.set(true);
1003
+ if (hasReset) {
1004
+ if (this._maybeDeletion()) {
1005
+ this._deletion.set(true);
1006
+ } else {
1007
+ this._stateStale.set(true);
1008
+ }
757
1009
  }
758
- if (this._maybeDeletion()) {
759
- this._maybeDeletion.set(false);
1010
+ this._maybeDeletion.set(false);
1011
+ }
1012
+ resetFocusEffect() {
1013
+ const stateStale = this._stateStale();
1014
+ if (!stateStale) return;
1015
+ const isFocused = untracked(() => this.isFocused());
1016
+ const isRoving = untracked(() => this.inputs.focusMode() === 'roving');
1017
+ const activeCell = untracked(() => this.activeCell());
1018
+ if (isRoving && activeCell !== undefined && isFocused) {
1019
+ if (!activeCell.isFocused()) {
1020
+ activeCell.focus();
1021
+ }
760
1022
  }
1023
+ this._stateStale.set(false);
761
1024
  }
762
- focusEffect() {
763
- const activeCell = this.activeCell();
764
- const hasFocus = this.isFocused();
1025
+ restoreFocusEffect() {
765
1026
  const deletion = this._deletion();
766
- const isRoving = this.inputs.focusMode() === 'roving';
767
- if (activeCell !== undefined && isRoving && (hasFocus || deletion)) {
768
- activeCell.element().focus();
769
- if (deletion) {
770
- this._deletion.set(false);
1027
+ if (!deletion) return;
1028
+ const isRoving = untracked(() => this.inputs.focusMode() === 'roving');
1029
+ const activeCell = untracked(() => this.activeCell());
1030
+ if (isRoving && activeCell !== undefined) {
1031
+ if (!activeCell.isFocused()) {
1032
+ activeCell.focus();
771
1033
  }
772
1034
  }
1035
+ this._deletion.set(false);
1036
+ }
1037
+ focusEffect() {
1038
+ const activeCell = this.activeCell();
1039
+ const gridFocused = untracked(() => this.isFocused());
1040
+ if (activeCell === undefined || !gridFocused) return;
1041
+ const isRoving = untracked(() => this.inputs.focusMode() === 'roving');
1042
+ const cellFocused = untracked(() => activeCell.isFocused());
1043
+ if (isRoving && !cellFocused) {
1044
+ activeCell.focus();
1045
+ }
773
1046
  }
774
1047
  }
775
1048
 
@@ -784,44 +1057,224 @@ class GridRowPattern {
784
1057
 
785
1058
  class GridCellPattern {
786
1059
  inputs;
787
- id;
788
- disabled;
1060
+ id = () => this.inputs.id();
1061
+ element = () => this.inputs.element();
1062
+ isFocused = signal(false);
789
1063
  selected;
790
- selectable;
791
- rowSpan;
792
- colSpan;
1064
+ selectable = () => this.inputs.selectable();
1065
+ disabled = () => this.inputs.disabled();
1066
+ rowSpan = () => this.inputs.rowSpan();
1067
+ colSpan = () => this.inputs.colSpan();
1068
+ active = computed(() => this.inputs.grid().activeCell() === this);
1069
+ anchor = computed(() => this.inputs.grid().anchorCell() === this ? true : undefined);
793
1070
  ariaSelected = computed(() => this.inputs.grid().inputs.enableSelection() && this.selectable() ? this.selected() : undefined);
794
1071
  ariaRowIndex = computed(() => this.inputs.row().rowIndex() ?? this.inputs.rowIndex() ?? this.inputs.grid().gridBehavior.rowIndex(this));
795
1072
  ariaColIndex = computed(() => this.inputs.colIndex() ?? this.inputs.grid().gridBehavior.colIndex(this));
796
- element = computed(() => this.inputs.widget()?.element() ?? this.inputs.element());
797
- active = computed(() => this.inputs.grid().activeCell() === this);
798
1073
  _tabIndex = computed(() => this.inputs.grid().gridBehavior.cellTabIndex(this));
799
- tabIndex = computed(() => this.inputs.widget() !== undefined ? -1 : this._tabIndex());
800
- widgetActivated = computed(() => this.inputs.widget()?.inputs.activate() ?? false);
1074
+ tabIndex = computed(() => {
1075
+ if (this.singleWidgetMode() || this.navigationActivated()) {
1076
+ return -1;
1077
+ }
1078
+ return this._tabIndex();
1079
+ });
1080
+ singleWidgetMode = computed(() => this.inputs.widgets().length === 1);
1081
+ multiWidgetMode = computed(() => this.inputs.widgets().length > 1);
1082
+ navigationDisabled = computed(() => !this.multiWidgetMode() || !this.active() || this.inputs.disabled());
1083
+ focusBehavior;
1084
+ navigationBehavior;
1085
+ activeWidget = linkedSignal(() => this.inputs.widgets().length > 0 ? this.inputs.widgets()[0] : undefined);
1086
+ navigationActivated = signal(false);
1087
+ widgetActivated = computed(() => this.inputs.widgets().some(w => w.isActivated()));
1088
+ isActivated = computed(() => this.navigationActivated() || this.widgetActivated());
1089
+ prevKey = computed(() => {
1090
+ if (this.inputs.orientation() === 'vertical') {
1091
+ return 'ArrowUp';
1092
+ }
1093
+ return this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
1094
+ });
1095
+ nextKey = computed(() => {
1096
+ if (this.inputs.orientation() === 'vertical') {
1097
+ return 'ArrowDown';
1098
+ }
1099
+ return this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
1100
+ });
1101
+ keydown = computed(() => {
1102
+ const manager = new KeyboardEventManager();
1103
+ if (!this.navigationActivated()) {
1104
+ manager.on('Enter', () => this.startNavigation());
1105
+ return manager;
1106
+ }
1107
+ manager.on('Escape', () => this.stopNavigation()).on(this.prevKey(), () => this._advance(() => this.navigationBehavior.prev({
1108
+ focusElement: false
1109
+ }))).on(this.nextKey(), () => this._advance(() => this.navigationBehavior.next({
1110
+ focusElement: false
1111
+ }))).on('Home', () => this._advance(() => this.navigationBehavior.next({
1112
+ focusElement: false
1113
+ }))).on('End', () => this._advance(() => this.navigationBehavior.next({
1114
+ focusElement: false
1115
+ })));
1116
+ return manager;
1117
+ });
801
1118
  constructor(inputs) {
802
1119
  this.inputs = inputs;
803
- this.id = inputs.id;
804
- this.disabled = inputs.disabled;
805
- this.rowSpan = inputs.rowSpan;
806
- this.colSpan = inputs.colSpan;
807
1120
  this.selected = inputs.selected;
808
- this.selectable = inputs.selectable;
1121
+ const listNavigationInputs = {
1122
+ ...inputs,
1123
+ items: inputs.widgets,
1124
+ activeItem: this.activeWidget,
1125
+ disabled: this.navigationDisabled,
1126
+ focusMode: () => 'roving',
1127
+ softDisabled: () => true
1128
+ };
1129
+ this.focusBehavior = new ListFocus(listNavigationInputs);
1130
+ this.navigationBehavior = new ListNavigation({
1131
+ ...listNavigationInputs,
1132
+ focusManager: this.focusBehavior
1133
+ });
1134
+ }
1135
+ onKeydown(event) {
1136
+ if (this.disabled() || this.inputs.widgets().length === 0) return;
1137
+ if (this.singleWidgetMode()) {
1138
+ this.activeWidget().onKeydown(event);
1139
+ return;
1140
+ }
1141
+ if (!this.navigationActivated()) {
1142
+ this.keydown().handle(event);
1143
+ return;
1144
+ }
1145
+ const widgetActivated = this.widgetActivated();
1146
+ this.activeWidget().onKeydown(event);
1147
+ if (!widgetActivated) {
1148
+ this.keydown().handle(event);
1149
+ }
1150
+ }
1151
+ onFocusIn(event) {
1152
+ this.isFocused.set(true);
1153
+ const focusTarget = event.target;
1154
+ const widget = this.inputs.getWidget(focusTarget);
1155
+ if (!widget) return;
1156
+ widget.onFocusIn(event);
1157
+ if (widget !== this.activeWidget()) {
1158
+ this.navigationBehavior.goto(widget, {
1159
+ focusElement: false
1160
+ });
1161
+ }
1162
+ if (this.multiWidgetMode()) {
1163
+ this.navigationActivated.set(true);
1164
+ }
1165
+ }
1166
+ onFocusOut(event) {
1167
+ const blurTarget = event.target;
1168
+ const widget = this.inputs.getWidget(blurTarget);
1169
+ widget?.onFocusOut(event);
1170
+ const focusTarget = event.relatedTarget;
1171
+ if (this.element().contains(focusTarget)) return;
1172
+ this.isFocused.set(false);
1173
+ this.navigationActivated.set(false);
1174
+ }
1175
+ focus() {
1176
+ if (this.singleWidgetMode()) {
1177
+ this.activeWidget()?.focus();
1178
+ } else {
1179
+ this.element().focus();
1180
+ }
809
1181
  }
810
1182
  widgetTabIndex() {
811
- return this._tabIndex();
1183
+ if (this.singleWidgetMode()) {
1184
+ return this._tabIndex();
1185
+ }
1186
+ return this.navigationActivated() ? 0 : -1;
1187
+ }
1188
+ startNavigation() {
1189
+ if (this.navigationActivated()) return;
1190
+ this.navigationActivated.set(true);
1191
+ this.navigationBehavior.first();
1192
+ }
1193
+ stopNavigation() {
1194
+ if (!this.navigationActivated()) return;
1195
+ this.navigationActivated.set(false);
1196
+ this.element().focus();
1197
+ }
1198
+ _advance(op) {
1199
+ const success = op();
1200
+ if (success) {
1201
+ this.activeWidget()?.focus();
1202
+ }
812
1203
  }
813
1204
  }
814
1205
 
815
1206
  class GridCellWidgetPattern {
816
1207
  inputs;
817
- element;
1208
+ id = () => this.inputs.id();
1209
+ element = () => this.inputs.element();
1210
+ widgetHost = computed(() => this.inputs.focusTarget() ?? this.element());
1211
+ index = computed(() => this.inputs.cell().inputs.widgets().indexOf(this));
1212
+ disabled = computed(() => this.inputs.disabled() || this.inputs.cell().disabled());
818
1213
  tabIndex = computed(() => this.inputs.cell().widgetTabIndex());
819
- active = computed(() => this.inputs.cell().active());
1214
+ active = computed(() => this.inputs.cell().activeWidget() === this);
1215
+ isActivated = signal(false);
1216
+ lastActivateEvent = signal(undefined);
1217
+ lastDeactivateEvent = signal(undefined);
1218
+ keydown = computed(() => {
1219
+ const manager = new KeyboardEventManager();
1220
+ if (this.inputs.widgetType() === 'simple') {
1221
+ return manager;
1222
+ }
1223
+ if (this.isActivated()) {
1224
+ manager.on('Escape', e => {
1225
+ this.deactivate(e);
1226
+ this.focus();
1227
+ });
1228
+ if (this.inputs.widgetType() === 'editable') {
1229
+ manager.on('Enter', e => {
1230
+ this.deactivate(e);
1231
+ this.focus();
1232
+ });
1233
+ }
1234
+ return manager;
1235
+ }
1236
+ manager.on('Enter', e => this.activate(e));
1237
+ if (this.inputs.widgetType() === 'editable') {
1238
+ manager.on([Modifier.Shift, Modifier.None], /^[a-zA-Z0-9]$/, e => this.activate(e), {
1239
+ preventDefault: false
1240
+ });
1241
+ }
1242
+ return manager;
1243
+ });
820
1244
  constructor(inputs) {
821
1245
  this.inputs = inputs;
822
- this.element = inputs.element;
1246
+ }
1247
+ onKeydown(event) {
1248
+ if (this.disabled()) return;
1249
+ this.keydown().handle(event);
1250
+ }
1251
+ onFocusIn(event) {
1252
+ if (this.inputs.widgetType() === 'simple') return;
1253
+ const focusTarget = event.target;
1254
+ if (this.widgetHost().contains(focusTarget) && this.widgetHost() !== focusTarget) {
1255
+ this.activate(event);
1256
+ }
1257
+ }
1258
+ onFocusOut(event) {
1259
+ const focusTarget = event.relatedTarget;
1260
+ if (this.widgetHost().contains(focusTarget)) return;
1261
+ this.deactivate(event);
1262
+ }
1263
+ focus() {
1264
+ this.widgetHost().focus();
1265
+ }
1266
+ activate(event) {
1267
+ if (this.isActivated()) return;
1268
+ if (this.inputs.widgetType() === 'simple') return;
1269
+ this.isActivated.set(true);
1270
+ this.lastActivateEvent.set(event);
1271
+ }
1272
+ deactivate(event) {
1273
+ if (!this.isActivated()) return;
1274
+ this.isActivated.set(false);
1275
+ this.lastDeactivateEvent.set(event);
823
1276
  }
824
1277
  }
825
1278
 
826
- export { GridCellPattern, GridCellWidgetPattern, GridPattern, GridRowPattern, KeyboardEventManager, Modifier, PointerEventManager };
1279
+ export { GridCellPattern, GridCellWidgetPattern, GridPattern, GridRowPattern, KeyboardEventManager, ListFocus, ListNavigation, Modifier, PointerEventManager };
827
1280
  //# sourceMappingURL=_widget-chunk.mjs.map