export function useShortcuts()

in frontend/libs/canvasSpreadsheet/src/lib/hooks/useShortcuts.ts [35:264]


export function useShortcuts(gridApi: RefObject<GridApi>) {
  const { gridCallbacks, selectionEdges, selectedTable } =
    useContext(GridStateContext);
  const {
    arrowNavigation,
    extendSelection,
    scrollPage,
    moveSelectionToEdge,
    tabNavigation,
  } = useNavigation();
  const { openAIPrompt } = useAIPrompt(gridApi.current);
  const { copy, paste } = useCopyPaste();
  const { selectRow, selectColumn, stopMoveTable, completeMoveTable } =
    useSelection();
  const { moveSelectionNextAvailable } = useSelectionMoveNextAvailable();
  const { extendSelectionNextAvailable } = useExtendSelectionNextAvailable();

  const handleSwapFields = useCallback(
    (e: KeyboardEvent, direction: HorizontalDirection) => {
      if (!gridApi?.current || !gridCallbacks || !isCanvasEvent(e)) return;

      swapFields(gridApi.current, gridCallbacks, direction);
    },
    [gridApi, gridCallbacks]
  );

  const handleCopy = useCallback(
    (e: KeyboardEvent) => {
      if (!gridApi?.current || !gridCallbacks || !isCanvasEvent(e)) return;

      copy(gridApi.current);
    },
    [copy, gridApi, gridCallbacks]
  );

  const handlePaste = useCallback(
    (e: KeyboardEvent) => {
      if (!gridApi?.current || !gridCallbacks || !isCanvasEvent(e)) return;

      paste(gridApi.current, gridCallbacks);
    },
    [gridApi, gridCallbacks, paste]
  );

  const handleSelectTable = useCallback(
    (e: KeyboardEvent) => {
      if (!gridApi?.current || !isCanvasEvent(e)) return;

      e.preventDefault();

      gridApi.current.event.emit({
        type: GridEvent.selectAll,
        selectFromCurrentCell: true,
      });
    },
    [gridApi]
  );

  const handleOpenNote = useCallback(() => {
    if (!gridApi?.current || !selectionEdges) return;

    const { startCol, startRow } = selectionEdges;

    gridApi.current.event.emit({
      type: GridEvent.openNote,
      col: startCol,
      row: startRow,
    });
  }, [gridApi, selectionEdges]);

  const handleDelete = useCallback(
    (e: KeyboardEvent) => {
      if (!isCanvasEvent(e)) return;

      gridCallbacks.onDelete?.();
    },
    [gridCallbacks]
  );

  const handleSingleKeyShortcuts = useCallback(
    (e: KeyboardEvent) => {
      const isValidSingleShortcut = singleKeyShortcuts.includes(
        e.key as KeyboardCode
      );

      if (isValidSingleShortcut && !isCanvasEvent(e)) {
        return;
      }

      switch (e.key) {
        case KeyboardCode.Space:
          if (!isCellEditorOpen() && isCanvasEvent(e)) {
            e.preventDefault();
            e.stopPropagation();
            openAIPrompt();

            return;
          }
          break;
        case KeyboardCode.Home:
          moveSelectionToEdge('left');
          break;
        case KeyboardCode.End:
          moveSelectionToEdge('right');
          break;
        case KeyboardCode.Enter:
          if (selectedTable) {
            completeMoveTable();
          } else {
            arrowNavigation(KeyboardCode.ArrowDown);
          }
          break;
        case KeyboardCode.Escape:
          if (selectedTable) {
            stopMoveTable();
          }
          break;
        case KeyboardCode.Tab:
          e.preventDefault();
          e.stopPropagation();
          tabNavigation();
          break;
        case KeyboardCode.PageUp:
          scrollPage('up');
          break;
        case KeyboardCode.PageDown:
          scrollPage('down');
          break;
        case KeyboardCode.ArrowDown:
        case KeyboardCode.ArrowUp:
        case KeyboardCode.ArrowLeft:
        case KeyboardCode.ArrowRight:
          arrowNavigation(e.key as KeyboardCode);
          break;
      }
    },
    [
      moveSelectionToEdge,
      selectedTable,
      tabNavigation,
      scrollPage,
      arrowNavigation,
      openAIPrompt,
      completeMoveTable,
      stopMoveTable,
    ]
  );

  const shortcutGlobalHandlersMap: Partial<ShortcutHandlersMap> = useMemo(
    () => ({
      [Shortcut.SwapFieldsRight]: (e) => handleSwapFields(e, 'right'),
      [Shortcut.SwapFieldsLeft]: (e) => handleSwapFields(e, 'left'),
      [Shortcut.Copy]: (e) => handleCopy(e),
      [Shortcut.Paste]: (e) => handlePaste(e),
      [Shortcut.AddNote]: () => handleOpenNote(),
      [Shortcut.Delete]: (e) => handleDelete(e),
      [Shortcut.Backspace]: (e) => handleDelete(e),
      [Shortcut.MoveToSheetStart]: () => moveSelectionToEdge('up'),
      [Shortcut.MoveToSheetEnd]: () => moveSelectionToEdge('down'),
      [Shortcut.SelectAll]: (e) => handleSelectTable(e),
      [Shortcut.SelectRow]: () => selectRow(),
      [Shortcut.SelectColumn]: () => selectColumn(),
      [Shortcut.ExtendRangeSelectionUp]: () =>
        extendSelectionNextAvailable('up'),
      [Shortcut.ExtendRangeSelectionDown]: () =>
        extendSelectionNextAvailable('down'),
      [Shortcut.ExtendRangeSelectionLeft]: () =>
        extendSelectionNextAvailable('left'),
      [Shortcut.ExtendRangeSelectionRight]: () =>
        extendSelectionNextAvailable('right'),
      [Shortcut.RangeSelectionUp]: () => extendSelection('up'),
      [Shortcut.RangeSelectionDown]: () => extendSelection('down'),
      [Shortcut.RangeSelectionLeft]: () => extendSelection('left'),
      [Shortcut.RangeSelectionRight]: () => extendSelection('right'),
      [Shortcut.MoveSelectionNextAvailableUp]: (e) => {
        e.preventDefault();
        moveSelectionNextAvailable('up');
      },
      [Shortcut.MoveSelectionNextAvailableDown]: (e) => {
        e.preventDefault();
        moveSelectionNextAvailable('down');
      },
      [Shortcut.MoveSelectionNextAvailableLeft]: (e) => {
        e.preventDefault();
        moveSelectionNextAvailable('left');
      },
      [Shortcut.MoveSelectionNextAvailableRight]: (e) => {
        e.preventDefault();
        moveSelectionNextAvailable('right');
      },
    }),
    [
      extendSelection,
      extendSelectionNextAvailable,
      handleCopy,
      handleDelete,
      handleOpenNote,
      handlePaste,
      handleSelectTable,
      handleSwapFields,
      moveSelectionNextAvailable,
      moveSelectionToEdge,
      selectColumn,
      selectRow,
    ]
  );

  const onKeyDown = useCallback(
    (event: KeyboardEvent) => {
      for (const shortcutKey in shortcutGlobalHandlersMap) {
        if (shortcutApi.is(shortcutKey as Shortcut, event)) {
          shortcutGlobalHandlersMap[shortcutKey as Shortcut]?.(event);

          return;
        }
      }

      handleSingleKeyShortcuts(event);
    },
    [handleSingleKeyShortcuts, shortcutGlobalHandlersMap]
  );

  useEffect(() => {
    document.addEventListener('keydown', onKeyDown);

    return () => {
      document.removeEventListener('keydown', onKeyDown);
    };
  }, [onKeyDown]);
}