Next.jsでCtrl(Command)キと数字キークリックでページ遷移する処理

Next.jsでCtrl(Command)キと数字キークリックでページ遷移する処理

2024/05/12

2024/05/12

Next.jsでCtrlキー(or Commandキー)を押下している時だけ数字を表示し、数字キーをクリックすると、ページ遷移する処理を実装したいときのサンプルソースです。

以下画像のように、サイドバーにCtrlキー(or Commandキー)を押下時のみ数字が表示され、数字を押下するとその画面に遷移する処理です。


オブジェクト配列定義

オブジェクト配列を用意します。
keyはCtrl(Command)キーと一緒に押したいキー、hrefは遷移先のパスを定義しています。
以下はdata/sidebar.tsxというファイルを用意する想定です。

export const SIDEBAR_OPTIONS = [
    {
        key: '1',
        href: '/house',
        label: '住居',
        icon: <FaHome />,
    },
    {
        key: '2',
        href: '/shopping',
        label: '買物',
        icon: <FaCartShopping />,
    },
    {
        key: '3',
        href: '/cooking',
        label: '料理',
        icon: <GiCookingPot />,
    },
    {
        key: '4',
        href: '/childcare',
        label: '育児',
        icon: <MdChildCare />,
    },
    {
        key: '5',
        href: '/money',
        label: 'お金',
        icon: <FaMoneyCheckAlt />,
    },
    {
        key: '6',
        href: '/gift',
        label: 'ギフト',
        icon: <FaGift />,
    },
];


数字キー押下時の共通処理

キー押下時に画面遷移する、共通処理を作成します。
以下はhooks/use-keydown.tsxというファイルを作成する想定です。
event.metaKeyでMacのCommandキーを指定しています。
event.preventDefault();部分でブラウザが持っているショートカットキーのイベントをキャンセルしています。

import { useEffect } from 'react';
import { SIDEBAR_OPTIONS } from '@/data/sidebar';

const useKeydown = () => {
    useEffect(() => {
        /**
         * キーボードのキーが押されたときの処理
         * @param event
         */
        const handleKeyDown = (event: KeyboardEvent) => {
            // CtrlキーまたはCmdキーが押されているとき
            if (event.ctrlKey || event.metaKey) {
                // 押されたキーの番号に対応するページを取得
                const page = SIDEBAR_OPTIONS.find((option) => option.key === event.key)?.href;
                if (page) {
                    // デフォルトのイベントをキャンセル
                    event.preventDefault();

                    // ページ遷移
                    const pageLink: HTMLElement | null = document.querySelector(
                        `a[href="${page}"]`
                    );
                    if (pageLink) {
                        pageLink.click();
                    }
                }
            }
        };

        // イベントリスナーを追加
        window.addEventListener('keydown', handleKeyDown);

        return () => {
            window.removeEventListener('keydown', handleKeyDown);
        };
    }, []);
};

export default useKeydown;


Ctrlキー(Commandキー)押下時の処理

キーが押下されているときだけ数字が表示される実装をします。
components/sidebar.tsxというファイルを作成する想定です。
useKeydown();が前述の手順で作成した共通処理を使用している部分です。

'use client';

import Link from 'next/link';
import { useEffect, useState } from 'react';

import { SIDEBAR_OPTIONS } from '@/data/sidebar';
import useKeydown from '@/hooks/use-keydown';
import { useSidebar } from '@/hooks/use-sidebar';

const Sidebar = () => {
    const { sidebarOption } = useSidebar();

    // キーボードのキーが押されたときの共通処理
    useKeydown();

    // CtrlキーまたはCmdキーが押されているかどうかの状態
    const [isCtrlOrCmdPressed, setIsCtrlOrCmdPressed] = useState(false);

    useEffect(() => {
        /**
         * キーボードのキーが押されたときの処理
         * @param event
         */
        const handleKeyDown = (event: KeyboardEvent) => {
            if (event.ctrlKey || event.metaKey) {
                setIsCtrlOrCmdPressed(true);
            }
        };

        /**
         * キーボードのキーが離されたときの処理
         * @param event
         */
        const handleKeyUp = (event: KeyboardEvent) => {
            if (!event.ctrlKey || !event.metaKey) {
                setIsCtrlOrCmdPressed(false);
            }
        };

        // イベントリスナーを追加
        window.addEventListener('keydown', handleKeyDown);
        window.addEventListener('keyup', handleKeyUp);

        return () => {
            window.removeEventListener('keydown', handleKeyDown);
            window.removeEventListener('keyup', handleKeyUp);
        };
    }, []);

    return (
        <aside className="relative hidden w-48 bg-sidebar py-4 sm:block">
            <h3 className="mb-5 px-4 py-1">
                <Link href="/">LifeHub Links</Link>
            </h3>
            {SIDEBAR_OPTIONS.map((option) => (
                <Link
                    key={option.href}
                    href={option.href}
                    className={`block w-full px-4 py-2 duration-200 hover:bg-gray-600 ${
                        sidebarOption === option ? 'bg-gray-600' : ''
                    }`}
                >
                    <div className="flex justify-between">
                        <div className="flex items-center gap-1">
                            {option.icon}
                            <p>{option.label}</p>
                        </div>
                        {isCtrlOrCmdPressed && <div>{option.key}</div>}
                    </div>
                </Link>
            ))}
            <small className="absolute bottom-2 left-4">© tukky 2024</small>
        </aside>
    );
};

export default Sidebar;


参考資料

以下の自作サイトで実装しています。
LifeHubLlinks