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

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 +14 -14
  2. package/_adev_assets/aria-combobox.json +10 -10
  3. package/_adev_assets/aria-grid.json +81 -12
  4. package/_adev_assets/aria-listbox.json +3 -3
  5. package/_adev_assets/aria-menu.json +174 -74
  6. package/_adev_assets/aria-tabs.json +22 -22
  7. package/_adev_assets/aria-toolbar.json +99 -120
  8. package/_adev_assets/aria-tree.json +20 -16
  9. package/fesm2022/_widget-chunk.mjs +266 -144
  10. package/fesm2022/_widget-chunk.mjs.map +1 -1
  11. package/fesm2022/accordion.mjs +12 -13
  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 +9 -7
  16. package/fesm2022/combobox.mjs.map +1 -1
  17. package/fesm2022/grid.mjs +61 -12
  18. package/fesm2022/grid.mjs.map +1 -1
  19. package/fesm2022/listbox.mjs +14 -15
  20. package/fesm2022/listbox.mjs.map +1 -1
  21. package/fesm2022/menu.mjs +117 -61
  22. package/fesm2022/menu.mjs.map +1 -1
  23. package/fesm2022/private.mjs +390 -399
  24. package/fesm2022/private.mjs.map +1 -1
  25. package/fesm2022/tabs.mjs +16 -17
  26. package/fesm2022/tabs.mjs.map +1 -1
  27. package/fesm2022/toolbar.mjs +79 -44
  28. package/fesm2022/toolbar.mjs.map +1 -1
  29. package/fesm2022/tree.mjs +22 -19
  30. package/fesm2022/tree.mjs.map +1 -1
  31. package/package.json +2 -10
  32. package/types/_grid-chunk.d.ts +115 -53
  33. package/types/accordion.d.ts +4 -4
  34. package/types/combobox.d.ts +2 -2
  35. package/types/grid.d.ts +12 -3
  36. package/types/listbox.d.ts +3 -4
  37. package/types/menu.d.ts +33 -21
  38. package/types/private.d.ts +263 -341
  39. package/types/tabs.d.ts +4 -4
  40. package/types/toolbar.d.ts +29 -26
  41. package/types/tree.d.ts +5 -6
  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
@@ -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) {
@@ -130,7 +135,6 @@ class PointerEventManager extends EventManager {
130
135
  class GridData {
131
136
  inputs;
132
137
  cells;
133
- rowCount = computed(() => this.cells().length);
134
138
  maxRowCount = computed(() => Math.max(...this._rowCountByCol().values(), 0));
135
139
  maxColCount = computed(() => Math.max(...this._colCountsByRow().values(), 0));
136
140
  _coordsMap = computed(() => {
@@ -280,7 +284,7 @@ class GridFocus {
280
284
  constructor(inputs) {
281
285
  this.inputs = inputs;
282
286
  }
283
- getCellTabindex(cell) {
287
+ getCellTabIndex(cell) {
284
288
  if (this.gridDisabled()) {
285
289
  return -1;
286
290
  }
@@ -290,7 +294,7 @@ class GridFocus {
290
294
  return this.activeCell() === cell ? 0 : -1;
291
295
  }
292
296
  isFocusable(cell) {
293
- return !cell.disabled() || !this.inputs.skipDisabled();
297
+ return !cell.disabled() || this.inputs.softDisabled();
294
298
  }
295
299
  focusCell(cell) {
296
300
  if (this.gridDisabled()) {
@@ -349,37 +353,38 @@ class GridNavigation {
349
353
  gotoCoords(coords) {
350
354
  return this.inputs.gridFocus.focusCoordinates(coords);
351
355
  }
352
- peek(direction, fromCoords, wrap) {
356
+ peek(direction, fromCoords, wrap, allowDisabled) {
353
357
  wrap = wrap ?? (direction.row !== undefined ? this.inputs.rowWrap() : this.inputs.colWrap());
354
- return this._peekDirectional(direction, fromCoords, wrap);
358
+ return this._peekDirectional(direction, fromCoords, wrap, allowDisabled);
355
359
  }
356
360
  advance(direction) {
357
361
  const nextCoords = this.peek(direction, this.inputs.gridFocus.activeCoords());
358
362
  return !!nextCoords && this.gotoCoords(nextCoords);
359
363
  }
360
- peekFirst(row) {
364
+ peekFirst(row, allowDisabled) {
361
365
  const fromCoords = {
362
366
  row: row ?? 0,
363
367
  col: -1
364
368
  };
365
- return row === undefined ? this._peekDirectional(direction.Right, fromCoords, 'continuous') : this._peekDirectional(direction.Right, fromCoords, 'nowrap');
369
+ return row === undefined ? this._peekDirectional(direction.Right, fromCoords, 'continuous', allowDisabled) : this._peekDirectional(direction.Right, fromCoords, 'nowrap', allowDisabled);
366
370
  }
367
371
  first(row) {
368
372
  const nextCoords = this.peekFirst(row);
369
373
  return !!nextCoords && this.gotoCoords(nextCoords);
370
374
  }
371
- peekLast(row) {
375
+ peekLast(row, allowDisabled) {
372
376
  const fromCoords = {
373
377
  row: row ?? this.inputs.grid.maxRowCount() - 1,
374
378
  col: this.inputs.grid.maxColCount()
375
379
  };
376
- return row === undefined ? this._peekDirectional(direction.Left, fromCoords, 'continuous') : this._peekDirectional(direction.Left, fromCoords, 'nowrap');
380
+ return row === undefined ? this._peekDirectional(direction.Left, fromCoords, 'continuous', allowDisabled) : this._peekDirectional(direction.Left, fromCoords, 'nowrap', allowDisabled);
377
381
  }
378
382
  last(row) {
379
383
  const nextCoords = this.peekLast(row);
380
384
  return !!nextCoords && this.gotoCoords(nextCoords);
381
385
  }
382
- _peekDirectional(delta, fromCoords, wrap) {
386
+ _peekDirectional(delta, fromCoords, wrap, allowDisabled = false) {
387
+ if (this.inputs.gridFocus.gridDisabled()) return undefined;
383
388
  const fromCell = this.inputs.grid.getCell(fromCoords);
384
389
  const maxRowCount = this.inputs.grid.maxRowCount();
385
390
  const maxColCount = this.inputs.grid.maxColCount();
@@ -406,11 +411,17 @@ class GridNavigation {
406
411
  col: (nextCoords.col + colDelta + maxColCount) % maxColCount
407
412
  };
408
413
  }
414
+ if (wrap === 'nowrap') {
415
+ nextCoords = {
416
+ row: nextCoords.row + rowDelta,
417
+ col: nextCoords.col + colDelta
418
+ };
419
+ }
409
420
  if (nextCoords.row === fromCoords.row && nextCoords.col === fromCoords.col) {
410
421
  return undefined;
411
422
  }
412
423
  const nextCell = this.inputs.grid.getCell(nextCoords);
413
- if (nextCell !== undefined && nextCell !== fromCell && this.inputs.gridFocus.isFocusable(nextCell)) {
424
+ if (nextCell !== undefined && nextCell !== fromCell && (allowDisabled || this.inputs.gridFocus.isFocusable(nextCell))) {
414
425
  return nextCoords;
415
426
  }
416
427
  }
@@ -420,45 +431,45 @@ class GridNavigation {
420
431
 
421
432
  class GridSelection {
422
433
  inputs;
434
+ _undoList = signal([]);
423
435
  constructor(inputs) {
424
436
  this.inputs = inputs;
425
437
  }
426
- select(fromCoords, toCoords) {
427
- for (const cell of this._validCells(fromCoords, toCoords ?? fromCoords)) {
428
- cell.selected.set(true);
438
+ undo() {
439
+ for (const [cell, oldState] of this._undoList()) {
440
+ cell.selected.set(oldState);
429
441
  }
442
+ this._undoList.set([]);
443
+ }
444
+ select(fromCoords, toCoords) {
445
+ this._updateState(fromCoords, toCoords ?? fromCoords, () => true);
430
446
  }
431
447
  deselect(fromCoords, toCoords) {
432
- for (const cell of this._validCells(fromCoords, toCoords ?? fromCoords)) {
433
- cell.selected.set(false);
434
- }
448
+ this._updateState(fromCoords, toCoords ?? fromCoords, () => false);
435
449
  }
436
450
  toggle(fromCoords, toCoords) {
437
- for (const cell of this._validCells(fromCoords, toCoords ?? fromCoords)) {
438
- cell.selected.update(state => !state);
439
- }
451
+ this._updateState(fromCoords, toCoords ?? fromCoords, oldState => !oldState);
440
452
  }
441
453
  selectAll() {
442
- for (const cell of this._validCells({
454
+ this._updateState({
443
455
  row: 0,
444
456
  col: 0
445
457
  }, {
446
458
  row: this.inputs.grid.maxRowCount(),
447
459
  col: this.inputs.grid.maxColCount()
448
- })) {
449
- cell.selected.set(true);
450
- }
460
+ }, () => true);
451
461
  }
452
462
  deselectAll() {
453
- for (const cell of this._validCells({
463
+ this._updateState({
454
464
  row: 0,
455
465
  col: 0
456
466
  }, {
457
467
  row: this.inputs.grid.maxRowCount(),
458
468
  col: this.inputs.grid.maxColCount()
459
- })) {
460
- cell.selected.set(false);
461
- }
469
+ }, () => false);
470
+ }
471
+ isSelectable(cell) {
472
+ return cell.selectable() && !cell.disabled();
462
473
  }
463
474
  *_validCells(fromCoords, toCoords) {
464
475
  const startRow = Math.min(fromCoords.row, toCoords.row);
@@ -473,14 +484,22 @@ class GridSelection {
473
484
  col
474
485
  });
475
486
  if (cell === undefined) continue;
476
- if (!cell.selectable()) continue;
477
- if (cell.disabled()) continue;
487
+ if (!this.isSelectable(cell)) continue;
478
488
  if (visited.has(cell)) continue;
479
489
  visited.add(cell);
480
490
  yield cell;
481
491
  }
482
492
  }
483
493
  }
494
+ _updateState(fromCoords, toCoords, stateFn) {
495
+ const undoList = [];
496
+ for (const cell of this._validCells(fromCoords, toCoords)) {
497
+ const oldState = cell.selected();
498
+ undoList.push([cell, oldState]);
499
+ cell.selected.set(stateFn(oldState));
500
+ }
501
+ this._undoList.set(undoList);
502
+ }
484
503
  }
485
504
 
486
505
  class Grid {
@@ -490,9 +509,12 @@ class Grid {
490
509
  navigationBehavior;
491
510
  selectionBehavior;
492
511
  selectionAnchor = linkedSignal(() => this.focusBehavior.activeCoords());
493
- gridTabIndex = computed(() => this.focusBehavior.gridTabIndex());
494
- gridDisabled = computed(() => this.focusBehavior.gridDisabled());
495
- activeDescendant = computed(() => this.focusBehavior.activeDescendant());
512
+ selectionAnchorCell = computed(() => this.data.getCell(this.selectionAnchor()));
513
+ selectionStabled = signal(true);
514
+ allSelected = computed(() => this.data.cells().flat().filter(c => this.selectionBehavior.isSelectable(c)).every(c => c.selected()));
515
+ gridTabIndex = () => this.focusBehavior.gridTabIndex();
516
+ gridDisabled = () => this.focusBehavior.gridDisabled();
517
+ activeDescendant = () => this.focusBehavior.activeDescendant();
496
518
  constructor(inputs) {
497
519
  this.inputs = inputs;
498
520
  this.data = new GridData(inputs);
@@ -520,51 +542,33 @@ class Grid {
520
542
  return index !== undefined ? index + 1 : undefined;
521
543
  }
522
544
  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);
545
+ return this.focusBehavior.getCellTabIndex(cell);
535
546
  }
536
- rangeSelectDown() {
537
- const coords = this.navigationBehavior.peek(direction.Down, this.selectionAnchor());
538
- if (coords === undefined) return;
539
- this._rangeSelectCoords(coords);
547
+ up(opts = {}) {
548
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peek(direction.Up, this.selectionAnchor(), 'nowrap', true)) : this.navigationBehavior.advance(direction.Up), opts);
540
549
  }
541
- left() {
542
- return this.navigationBehavior.advance(direction.Left);
550
+ down(opts = {}) {
551
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peek(direction.Down, this.selectionAnchor(), 'nowrap', true)) : this.navigationBehavior.advance(direction.Down), opts);
543
552
  }
544
- rangeSelectLeft() {
545
- const coords = this.navigationBehavior.peek(direction.Left, this.selectionAnchor());
546
- if (coords === undefined) return;
547
- this._rangeSelectCoords(coords);
553
+ left(opts = {}) {
554
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peek(direction.Left, this.selectionAnchor(), 'nowrap', true)) : this.navigationBehavior.advance(direction.Left), opts);
548
555
  }
549
- right() {
550
- return this.navigationBehavior.advance(direction.Right);
556
+ right(opts = {}) {
557
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peek(direction.Right, this.selectionAnchor(), 'nowrap', true)) : this.navigationBehavior.advance(direction.Right), opts);
551
558
  }
552
- rangeSelectRight() {
553
- const coords = this.navigationBehavior.peek(direction.Right, this.selectionAnchor());
554
- if (coords === undefined) return;
555
- this._rangeSelectCoords(coords);
559
+ first(opts = {}) {
560
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peekFirst(undefined, true)) : this.navigationBehavior.first(), opts);
556
561
  }
557
- first() {
558
- return this.navigationBehavior.first();
559
- }
560
- firstInRow() {
561
- return this.navigationBehavior.first(this.focusBehavior.activeCoords().row);
562
+ firstInRow(opts = {}) {
563
+ const row = this.focusBehavior.activeCoords().row;
564
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peekFirst(row, true)) : this.navigationBehavior.first(row), opts);
562
565
  }
563
- last() {
564
- return this.navigationBehavior.last();
566
+ last(opts = {}) {
567
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peekLast(undefined, true)) : this.navigationBehavior.last(), opts);
565
568
  }
566
- lastInRow() {
567
- return this.navigationBehavior.last(this.focusBehavior.activeCoords().row);
569
+ lastInRow(opts = {}) {
570
+ const row = this.focusBehavior.activeCoords().row;
571
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.navigationBehavior.peekLast(row, true)) : this.navigationBehavior.last(row), opts);
568
572
  }
569
573
  selectRow() {
570
574
  const row = this.focusBehavior.activeCoords().row;
@@ -588,50 +592,48 @@ class Grid {
588
592
  col
589
593
  });
590
594
  }
591
- selectAll() {
592
- this.selectionBehavior.selectAll();
595
+ select() {
596
+ this.selectionBehavior.select(this.focusBehavior.activeCoords());
593
597
  }
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) {
598
+ deselect() {
599
+ this.selectionBehavior.deselect(this.focusBehavior.activeCoords());
600
+ }
601
+ toggle() {
602
+ this.selectionBehavior.toggle(this.focusBehavior.activeCoords());
603
+ }
604
+ toggleOne() {
605
+ const selected = !!this.focusBehavior.activeCell()?.selected();
606
+ if (selected) {
607
+ this.deselect();
611
608
  return;
612
609
  }
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
- };
610
+ this.deselectAll();
611
+ this.select();
612
+ }
613
+ selectAll() {
614
+ this.selectionBehavior.selectAll();
615
+ }
616
+ deselectAll() {
624
617
  this.selectionBehavior.deselectAll();
625
- this.selectionBehavior.select(fromCoords, toCoords);
626
- this.selectionAnchor.set(coords);
618
+ }
619
+ gotoCell(cell, opts = {}) {
620
+ return this._navigateWithSelection(() => opts.anchor ? this._updateSelectionAnchor(() => this.data.getCoords(cell)) : this.navigationBehavior.gotoCell(cell), opts);
621
+ }
622
+ setDefaultState() {
623
+ const focusableSelectedCell = this.data.cells().flat().filter(c => this.focusBehavior.isFocusable(c)).find(c => c.selected());
624
+ if (focusableSelectedCell !== undefined) {
625
+ this.focusBehavior.focusCell(focusableSelectedCell);
626
+ return true;
627
+ }
628
+ const firstFocusableCoords = this.navigationBehavior.peekFirst();
629
+ if (firstFocusableCoords !== undefined) {
630
+ return this.focusBehavior.focusCoordinates(firstFocusableCoords);
631
+ }
632
+ return false;
627
633
  }
628
634
  resetState() {
629
635
  if (this.focusBehavior.stateEmpty()) {
630
- const firstFocusableCoords = this.navigationBehavior.peekFirst();
631
- if (firstFocusableCoords === undefined) {
632
- return false;
633
- }
634
- return this.focusBehavior.focusCoordinates(firstFocusableCoords);
636
+ return this.setDefaultState();
635
637
  }
636
638
  if (this.focusBehavior.stateStale()) {
637
639
  if (this.focusBehavior.focusCell(this.focusBehavior.activeCell())) {
@@ -646,6 +648,67 @@ class Grid {
646
648
  }
647
649
  return false;
648
650
  }
651
+ _updateSelectionAnchor(peekFn) {
652
+ const coords = peekFn();
653
+ const success = coords !== undefined;
654
+ if (!success) return false;
655
+ this.selectionAnchor.set(coords);
656
+ return success;
657
+ }
658
+ _updateRangeSelection() {
659
+ if (!this.selectionStabled()) {
660
+ this.selectionBehavior.undo();
661
+ }
662
+ this.selectionBehavior.select(...this._getSelectionCoords(this.focusBehavior.activeCoords(), this.selectionAnchor()));
663
+ }
664
+ _getSelectionCoords(startCoords, endCoords) {
665
+ const startCell = this.data.getCell(startCoords);
666
+ const endCell = this.data.getCell(endCoords);
667
+ const allCoords = [...this.data.getAllCoords(startCell), ...this.data.getAllCoords(endCell)];
668
+ const allRows = allCoords.map(c => c.row);
669
+ const allCols = allCoords.map(c => c.col);
670
+ const fromCoords = {
671
+ row: Math.min(...allRows),
672
+ col: Math.min(...allCols)
673
+ };
674
+ const toCoords = {
675
+ row: Math.max(...allRows),
676
+ col: Math.max(...allCols)
677
+ };
678
+ return [fromCoords, toCoords];
679
+ }
680
+ _navigateWithSelection(op, opts = {}) {
681
+ const success = op();
682
+ if (!success) return false;
683
+ if (opts.anchor) {
684
+ this._updateRangeSelection();
685
+ this.selectionStabled.set(false);
686
+ return success;
687
+ }
688
+ this.selectionStabled.set(true);
689
+ if (opts.select) {
690
+ this.select();
691
+ return success;
692
+ }
693
+ if (opts.selectOne) {
694
+ this.deselectAll();
695
+ this.select();
696
+ return success;
697
+ }
698
+ if (opts.toggle) {
699
+ this.toggle();
700
+ return success;
701
+ }
702
+ if (opts.toggleOne) {
703
+ const selected = !!this.focusBehavior.activeCell()?.selected();
704
+ this.deselectAll();
705
+ if (!selected) {
706
+ this.select();
707
+ }
708
+ return success;
709
+ }
710
+ return success;
711
+ }
649
712
  }
650
713
 
651
714
  class GridPattern {
@@ -656,53 +719,110 @@ class GridPattern {
656
719
  disabled = computed(() => this.gridBehavior.gridDisabled());
657
720
  activeDescendant = computed(() => this.gridBehavior.activeDescendant());
658
721
  activeCell = computed(() => this.gridBehavior.focusBehavior.activeCell());
722
+ anchorCell = computed(() => this.inputs.enableSelection() && this.inputs.multi() ? this.gridBehavior.selectionAnchorCell() : undefined);
659
723
  pauseNavigation = computed(() => this.gridBehavior.data.cells().flat().reduce((res, c) => res || c.widgetActivated(), false));
660
724
  isFocused = signal(false);
725
+ hasBeenFocused = signal(false);
661
726
  dragging = signal(false);
727
+ prevColKey = computed(() => this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft');
728
+ nextColKey = computed(() => this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight');
662
729
  keydown = computed(() => {
663
730
  const manager = new KeyboardEventManager();
664
731
  if (this.pauseNavigation()) {
665
732
  return manager;
666
733
  }
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());
734
+ const opts = {
735
+ selectOne: this.inputs.enableSelection() && this.inputs.selectionMode() === 'follow'
736
+ };
737
+ 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));
738
+ if (this.inputs.enableSelection() && this.inputs.selectionMode() === 'explicit') {
739
+ manager.on('Enter', () => this.inputs.multi() ? this.gridBehavior.toggle() : this.gridBehavior.toggleOne()).on(' ', () => this.inputs.multi() ? this.gridBehavior.toggle() : this.gridBehavior.toggleOne());
740
+ }
741
+ if (this.inputs.enableSelection() && this.inputs.enableRangeSelection()) {
742
+ manager.on(Modifier.Shift, 'ArrowUp', () => this.gridBehavior.up({
743
+ anchor: true
744
+ })).on(Modifier.Shift, 'ArrowDown', () => this.gridBehavior.down({
745
+ anchor: true
746
+ })).on(Modifier.Shift, this.prevColKey(), () => this.gridBehavior.left({
747
+ anchor: true
748
+ })).on(Modifier.Shift, this.nextColKey(), () => this.gridBehavior.right({
749
+ anchor: true
750
+ })).on(Modifier.Shift, 'Home', () => this.gridBehavior.firstInRow({
751
+ anchor: true
752
+ })).on(Modifier.Shift, 'End', () => this.gridBehavior.lastInRow({
753
+ anchor: true
754
+ })).on([Modifier.Ctrl | Modifier.Shift], 'Home', () => this.gridBehavior.first({
755
+ anchor: true
756
+ })).on([Modifier.Ctrl | Modifier.Shift], 'End', () => this.gridBehavior.last({
757
+ anchor: true
758
+ })).on([Modifier.Ctrl, Modifier.Meta], 'A', () => {
759
+ if (this.gridBehavior.allSelected()) {
760
+ this.gridBehavior.deselectAll();
761
+ } else {
762
+ this.gridBehavior.selectAll();
763
+ }
764
+ }).on([Modifier.Shift], ' ', () => this.gridBehavior.selectRow()).on([Modifier.Ctrl, Modifier.Meta], ' ', () => this.gridBehavior.selectCol());
670
765
  }
671
766
  return manager;
672
767
  });
673
768
  pointerdown = computed(() => {
674
769
  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 => {
770
+ if (!this.inputs.enableSelection()) {
771
+ manager.on(e => {
685
772
  const cell = this.inputs.getCell(e.target);
686
- if (!cell) return;
687
- this.gridBehavior.toggleSelect(cell);
688
- }).on(Modifier.Shift, e => {
773
+ if (!cell || !this.gridBehavior.focusBehavior.isFocusable(cell)) return;
774
+ this.gridBehavior.gotoCell(cell);
775
+ });
776
+ }
777
+ if (this.inputs.enableSelection()) {
778
+ manager.on(e => {
689
779
  const cell = this.inputs.getCell(e.target);
690
- if (!cell) return;
691
- this.gridBehavior.rangeSelect(cell);
692
- this.dragging.set(true);
780
+ if (!cell || !this.gridBehavior.focusBehavior.isFocusable(cell)) return;
781
+ this.gridBehavior.gotoCell(cell, {
782
+ selectOne: this.inputs.selectionMode() === 'follow',
783
+ toggleOne: this.inputs.selectionMode() === 'explicit' && !this.inputs.multi(),
784
+ toggle: this.inputs.selectionMode() === 'explicit' && this.inputs.multi()
785
+ });
786
+ if (this.inputs.multi() && this.inputs.enableRangeSelection()) {
787
+ this.dragging.set(true);
788
+ }
693
789
  });
790
+ if (this.inputs.multi()) {
791
+ manager.on([Modifier.Ctrl, Modifier.Meta], e => {
792
+ const cell = this.inputs.getCell(e.target);
793
+ if (!cell || !this.gridBehavior.focusBehavior.isFocusable(cell)) return;
794
+ this.gridBehavior.gotoCell(cell, {
795
+ toggle: true
796
+ });
797
+ if (this.inputs.enableRangeSelection()) {
798
+ this.dragging.set(true);
799
+ }
800
+ });
801
+ if (this.inputs.enableRangeSelection()) {
802
+ manager.on(Modifier.Shift, e => {
803
+ const cell = this.inputs.getCell(e.target);
804
+ if (!cell) return;
805
+ this.gridBehavior.gotoCell(cell, {
806
+ anchor: true
807
+ });
808
+ this.dragging.set(true);
809
+ });
810
+ }
811
+ }
694
812
  }
695
813
  return manager;
696
814
  });
697
815
  pointerup = computed(() => {
698
816
  const manager = new PointerEventManager();
699
- if (this.inputs.enableSelection()) {
700
- manager.on([Modifier.Shift, Modifier.None], () => {
817
+ if (this.inputs.enableSelection() && this.inputs.enableRangeSelection()) {
818
+ manager.on([Modifier.Shift, Modifier.Ctrl, Modifier.Meta, Modifier.None], () => {
701
819
  this.dragging.set(false);
702
820
  });
703
821
  }
704
822
  return manager;
705
823
  });
824
+ _maybeDeletion = signal(false);
825
+ _deletion = signal(false);
706
826
  constructor(inputs) {
707
827
  this.inputs = inputs;
708
828
  this.gridBehavior = new Grid({
@@ -723,23 +843,23 @@ class GridPattern {
723
843
  onPointermove(event) {
724
844
  if (this.disabled()) return;
725
845
  if (!this.inputs.enableSelection()) return;
846
+ if (!this.inputs.enableRangeSelection()) return;
726
847
  if (!this.dragging()) return;
727
848
  const cell = this.inputs.getCell(event.target);
728
849
  if (!cell) return;
729
- this.gridBehavior.rangeSelect(cell);
850
+ this.gridBehavior.gotoCell(cell, {
851
+ anchor: true
852
+ });
730
853
  }
731
854
  onPointerup(event) {
732
855
  if (!this.disabled()) {
733
856
  this.pointerup().handle(event);
734
857
  }
735
858
  }
736
- onFocusIn(event) {
859
+ onFocusIn() {
737
860
  this.isFocused.set(true);
738
- const cell = this.inputs.getCell(event.target);
739
- if (!cell) return;
740
- this.gridBehavior.gotoCell(cell);
861
+ this.hasBeenFocused.set(true);
741
862
  }
742
- _maybeDeletion = signal(false);
743
863
  onFocusOut(event) {
744
864
  const parentEl = this.inputs.element();
745
865
  const targetEl = event.relatedTarget;
@@ -749,15 +869,16 @@ class GridPattern {
749
869
  if (parentEl.contains(targetEl)) return;
750
870
  this.isFocused.set(false);
751
871
  }
752
- _deletion = signal(false);
872
+ setDefaultStateEffect() {
873
+ if (this.hasBeenFocused()) return;
874
+ this.gridBehavior.setDefaultState();
875
+ }
753
876
  resetStateEffect() {
754
877
  const hasReset = this.gridBehavior.resetState();
755
878
  if (hasReset && this._maybeDeletion()) {
756
879
  this._deletion.set(true);
757
880
  }
758
- if (this._maybeDeletion()) {
759
- this._maybeDeletion.set(false);
760
- }
881
+ this._maybeDeletion.set(false);
761
882
  }
762
883
  focusEffect() {
763
884
  const activeCell = this.activeCell();
@@ -795,6 +916,7 @@ class GridCellPattern {
795
916
  ariaColIndex = computed(() => this.inputs.colIndex() ?? this.inputs.grid().gridBehavior.colIndex(this));
796
917
  element = computed(() => this.inputs.widget()?.element() ?? this.inputs.element());
797
918
  active = computed(() => this.inputs.grid().activeCell() === this);
919
+ anchor = computed(() => this.inputs.grid().anchorCell() === this ? true : undefined);
798
920
  _tabIndex = computed(() => this.inputs.grid().gridBehavior.cellTabIndex(this));
799
921
  tabIndex = computed(() => this.inputs.widget() !== undefined ? -1 : this._tabIndex());
800
922
  widgetActivated = computed(() => this.inputs.widget()?.inputs.activate() ?? false);