Newer
Older
PixelPaintWar / apps / client / src / scenes / game / input / presentation / useImmediatePressHandlers.ts
/**
 * useImmediatePressHandlers
 * タップ即時発火の押下イベントハンドラを生成するフック
 * pointerdown 起点で入力を確定し click の二重発火を抑止する
 */
import { useCallback } from "react";

/** 即時押下ハンドラ生成の入力プロパティ */
export type UseImmediatePressHandlersOptions = {
  stopPropagation?: boolean;
};

const isActivationKey = (key: string): boolean => {
  return key === "Enter" || key === " " || key === "Spacebar";
};

/** pointerdown と click 抑止のハンドラを生成する */
export const useImmediatePressHandlers = <T extends HTMLElement>(
  onPress: () => void,
  options?: UseImmediatePressHandlersOptions,
) => {
  const stopPropagation = options?.stopPropagation ?? true;

  const onPointerDown = useCallback(
    (event: React.PointerEvent<T>) => {
      event.preventDefault();
      if (stopPropagation) {
        event.stopPropagation();
      }
      onPress();
    },
    [onPress, stopPropagation],
  );

  const onClick = useCallback(
    (event: React.MouseEvent<T>) => {
      event.preventDefault();
      if (stopPropagation) {
        event.stopPropagation();
      }
    },
    [stopPropagation],
  );

  const onKeyDown = useCallback(
    (event: React.KeyboardEvent<T>) => {
      if (!isActivationKey(event.key) || event.repeat) {
        return;
      }

      event.preventDefault();
      if (stopPropagation) {
        event.stopPropagation();
      }
      onPress();
    },
    [onPress, stopPropagation],
  );

  const onKeyUp = useCallback(
    (event: React.KeyboardEvent<T>) => {
      if (!isActivationKey(event.key)) {
        return;
      }

      event.preventDefault();
      if (stopPropagation) {
        event.stopPropagation();
      }
    },
    [stopPropagation],
  );

  return {
    onPointerDown,
    onClick,
    onKeyDown,
    onKeyUp,
  };
};