rovingTabIndex.tsx 1.8 KB

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