traceRovingTabIndex.tsx 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import type {TraceTree} from '../traceModels/traceTree';
  2. import type {TraceTreeNode} from '../traceModels/traceTreeNode';
  3. import {traceReducerExhaustiveActionCheck} from '../traceState';
  4. export interface TraceRovingTabIndexState {
  5. index: number | null;
  6. items: number | null;
  7. node: TraceTreeNode<TraceTree.NodeValue> | null;
  8. }
  9. export type TraceRovingTabIndexAction =
  10. | {
  11. index: number | null;
  12. items: number;
  13. node: TraceTreeNode<TraceTree.NodeValue> | null;
  14. type: 'initialize roving reducer';
  15. }
  16. | {
  17. action_source: 'click' | 'keyboard' | 'load';
  18. index: number;
  19. node: TraceTreeNode<TraceTree.NodeValue>;
  20. type: 'set roving index';
  21. }
  22. | {type: 'clear'}
  23. | {type: 'clear roving index'}
  24. | {items: number; type: 'set roving count'};
  25. export type RovingTabIndexUserActions = 'next' | 'previous' | 'last' | 'first';
  26. export function traceRovingTabIndexReducer(
  27. state: TraceRovingTabIndexState,
  28. action: TraceRovingTabIndexAction
  29. ): TraceRovingTabIndexState {
  30. switch (action.type) {
  31. case 'initialize roving reducer': {
  32. return {index: action.index, items: action.items, node: action.node};
  33. }
  34. case 'set roving count': {
  35. return {...state, items: action.items};
  36. }
  37. case 'set roving index':
  38. return {...state, node: action.node, index: action.index};
  39. case 'clear roving index':
  40. case 'clear':
  41. return {...state, index: null, node: null};
  42. default:
  43. traceReducerExhaustiveActionCheck(action);
  44. return state;
  45. }
  46. }
  47. export function getRovingIndexActionFromDOMEvent(
  48. event: React.KeyboardEvent
  49. ): RovingTabIndexUserActions | null {
  50. // @TODO it would be trivial to extend this and support
  51. // things like j/k vim-like navigation or add modifiers
  52. // so that users could jump to parent or sibling nodes.
  53. // I would need to put some thought into this, but shift+cmd+up
  54. // seems like a good candidate for jumping to parent node and
  55. // shift+cmd+down for jumping to the next sibling node.
  56. switch (event.key) {
  57. case 'ArrowDown':
  58. if (event.shiftKey) {
  59. return 'last';
  60. }
  61. return 'next';
  62. case 'ArrowUp':
  63. if (event.shiftKey) {
  64. return 'first';
  65. }
  66. return 'previous';
  67. case 'Home':
  68. return 'first';
  69. case 'End':
  70. return 'last';
  71. case 'Tab':
  72. if (event.shiftKey) {
  73. return 'previous';
  74. }
  75. return 'next';
  76. default:
  77. return null;
  78. }
  79. }