rovingTabIndex.tsx 1.9 KB

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