traceRovingTabIndex.tsx 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  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 roving index'}
  23. | {items: number; type: 'set roving count'};
  24. export type RovingTabIndexUserActions = 'next' | 'previous' | 'last' | 'first';
  25. export function traceRovingTabIndexReducer(
  26. state: TraceRovingTabIndexState,
  27. action: TraceRovingTabIndexAction
  28. ): TraceRovingTabIndexState {
  29. switch (action.type) {
  30. case 'initialize roving reducer': {
  31. return {index: action.index, items: action.items, node: action.node};
  32. }
  33. case 'set roving count': {
  34. return {...state, items: action.items};
  35. }
  36. case 'set roving index':
  37. return {...state, node: action.node, index: action.index};
  38. case 'clear roving index':
  39. return {...state, index: null, node: null};
  40. default:
  41. traceReducerExhaustiveActionCheck(action);
  42. return state;
  43. }
  44. }
  45. export function getRovingIndexActionFromDOMEvent(
  46. event: React.KeyboardEvent
  47. ): RovingTabIndexUserActions | null {
  48. // @TODO it would be trivial to extend this and support
  49. // things like j/k vim-like navigation or add modifiers
  50. // so that users could jump to parent or sibling nodes.
  51. // I would need to put some thought into this, but shift+cmd+up
  52. // seems like a good candidate for jumping to parent node and
  53. // shift+cmd+down for jumping to the next sibling node.
  54. switch (event.key) {
  55. case 'ArrowDown':
  56. if (event.shiftKey) {
  57. return 'last';
  58. }
  59. return 'next';
  60. case 'ArrowUp':
  61. if (event.shiftKey) {
  62. return 'first';
  63. }
  64. return 'previous';
  65. case 'Home':
  66. return 'first';
  67. case 'End':
  68. return 'last';
  69. case 'Tab':
  70. if (event.shiftKey) {
  71. return 'previous';
  72. }
  73. return 'next';
  74. default:
  75. return null;
  76. }
  77. }