1 連絡・概要
1.1 課題3
詳細は、前回の講義資料を確認してください。
README.mdの記述がポイントになります。注意事項や説明を十分に確認して作成するようにしてください。- 学年末なので特に提出期限に注意してください。
- 提出後、引き続きアプリのバージョンアップを継続することは問題ありません。
1.1.1 開発例
高専テクノゼミ 実践教育プログラム 2025春 「Web開発」から抜粋。
1.2 高専テクノゼミ
高専テクノゼミ「実践教育プログラム2026春」のエントリが開始されています。
高専生や大学生が長期休暇に取り組む実践型の教育イベントです。今回のプログラムでは、AIを活用したアプリ開発を学びながら学生が各自で設定したテーマに自由に取り組みます。
高専テクノゼミ 「進路選択の部屋 ~高専からの進路 -大学編入と就職活動の対策-」
- 👉 3月6日(金)・東京大学(交通費補助あり)
- 👉 https://portal.techno-semi.com/shinro
2 補足
本日は「課題3」に取り組んでください。以下、オプション的な内容 (課題3に必須の要素ではない) ですが Next.js 15 + Prisma 7 では検証できてきません。多少の読み替えで動作するはずです。すみません🙇
2.1 schema.prisma のシンタックスハイライト
VSCode の拡張機能として Prisma (識別子 prisma.prisma) をインストールすると
schema.prisma がシンタックスハイライトされるようになります。
3 トースト通知機能
react-hot-toastというライブラリを使用して トースト通知 の機能を実装したサンプルを紹介します。
3.1 実装の手順
VSCodeのターミナル (Ctrl+J)
から以下のコマンドを実行してライブラリをインストールします。
npm i react-hot-toast
サンプルコードを以下に示します。
"use client";
import toast, { Toaster } from "react-hot-toast";
import { twMerge } from "tailwind-merge";
const Page: React.FC = () => {
const buttonStyle = twMerge(
"rounded-md px-3 py-1 ",
"font-bold text-white",
"bg-indigo-500 hover:bg-indigo-700"
);
const successNotify = (msg: string) => toast.success(msg);
const errorNotify = (msg: string) => toast.error(msg);
return (
<main>
<div className="mb-2 text-2xl font-bold ">トースト通知</div>
<div className="flex gap-x-3">
<button
className={buttonStyle}
onClick={() => successNotify("成功スタイルのトースト通知")}
>
success
</button>
<button
className={buttonStyle}
onClick={() => errorNotify("エラースタイルのトースト通知")}
>
error
</button>
</div>
<Toaster position="top-center" />
</main>
);
};
export default Page;トーストの「表示位置」や「外観 (アイコンや背景色)」、表示されてから自動消去されるまでの時間 などもカスタマイズ可能です。詳細については公式ドキュメントを参照してください。
4 モーダル表示
react-modalというライブラリを使用して モーダルウィンドウ を実装したサンプルを紹介します。
4.1 実装の手順
VSCodeのターミナルからライブラリをインストールします。react-modal は
TypeScriptの型定義が同梱されていないため、別途 @types
パッケージとして提供されている型定義ファイル
をインストールする必要があります(型定義ファイルは開発時のみ必要なため、devDependencies として
-D オプションを付けてインストールします)。
npm i react-modal
npm i -D @types/react-modal
まずは、モーダルウィンドウの外観を定義するコンポーネントを実装します。
"use client";
import React, { ReactNode } from "react";
import ReactModal from "react-modal";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faXmark } from "@fortawesome/free-solid-svg-icons";
interface Props {
isOpen: boolean;
onClose: () => void;
children: ReactNode;
}
export const Modal: React.FC<Props> = (props) => {
const { isOpen, onClose, children } = props;
return (
<ReactModal
isOpen={isOpen}
onRequestClose={onClose}
contentLabel="Modal"
closeTimeoutMS={300}
ariaHideApp={false}
className="relative z-50 h-screen w-screen bg-black/50"
overlayClassName="fixed inset-0 bg-black_main flex items-center justify-center z-50"
>
<div className="flex size-full flex-col items-center justify-center">
<div className="flex w-full justify-end md:w-[640px]">
<button className="px-3 py-0.5 md:px-1" onClick={onClose}>
<FontAwesomeIcon
className="cursor-pointer text-2xl text-white hover:text-gray-300"
icon={faXmark}
/>
</button>
</div>
<div className="w-full bg-white p-3 md:w-[640px] md:rounded-md">
{children}
</div>
</div>
</ReactModal>
);
};つづいて、上記で定義した Modal コンポーネントを呼び出します。
"use client";
import { useState } from "react";
import { twMerge } from "tailwind-merge";
import { Modal } from "./_components/Modal"; // コンポーネントのインポート
const Page: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<main>
<div className="mb-2 text-2xl font-bold">モーダル</div>
<div className="flex gap-x-3">
<button
className={twMerge(
"rounded-md px-3 py-1",
"font-bold text-white",
"bg-indigo-500 hover:bg-indigo-700"
)}
onClick={() => setIsModalOpen(true)}
>
モーダルウィンドウを開く
</button>
</div>
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
{/* モーダル内に表示するコンテンツを <Modal>...</Modal> の子要素として与える */}
<div>
<div className="text-xl font-bold">徒然草</div>
<div>
<p>
つれづれなるままに、日暮らし硯に向かひて、心にうつりゆくよしなしごとを、
そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。
</p>
</div>
</div>
</Modal>
</main>
);
};
export default Page;(プロンプト例)
TypeScipt 開発をしていると、ライブラリ本体にくわえて、ときどき
npm i @types/xxxxxが要求されますが、この@types/って、どのような位置づけのライブラリなのですか。
5 shadcn/ui
shadcn/uiという UIコンポーネントコレクション を利用するサンプルを示します。以下に示すように「スイッチ」や「カレンダ」「アコーディオン」「ドロップダウンメニュー」など 統一感のある各種UIコンポーネントを提供 してくれます。
利用可能なコンポーネントの一覧はこちらから確認することができます。
5.1 実装の手順
shadcn/ui のセットアップは一般的なライブラリとはやや異なります。まずは、VSCodeのターミナルから、以下のコマンドを実行してください。
npx shadcn@latest init
いくつかの質問が表示されるので、以下のように回答します。
Which style would you like to use? » New York
Which color would you like to use as the base color? » Slate
Would you like to use CSS variables for theming? … yes
(実行例)
Need to install the following packages:
shadcn@2.3.0
Ok to proceed? (y) y
✔ Preflight checks.
✔ Verifying framework. Found Next.js.
✔ Validating Tailwind CSS.
✔ Validating import alias.
√ Which style would you like to use? » New York
√ Which color would you like to use as the base color? » Slate
√ Would you like to use CSS variables for theming? ... yes
✔ Writing components.json.
✔ Checking registry.
✔ Updating tailwind.config.ts
✔ Updating src\app\globals.css
✔ Installing dependencies.
✔ Created 1 file:
- src\lib\utils.ts
Success! Project initialization completed.
You may now add components.
つづいて、必要なUIを選んでインストールします。例えばスイッチUIを追加する場合は、以下のようにコマンドを実行します。
npx shadcn@latest add switch
また、カレンダUIを追加する場合は、以下のようにコマンドを実行します。
npx shadcn@latest add calendar
上記によってインストールされたライブラリは /src/components/ui/
にインストールされます。
実装例を以下に示します。
サンプルコードを以下に示します。
"use client";
import { useState } from "react";
import dayjs from "dayjs";
import { Switch } from "@/components/ui/switch";
import { Calendar } from "@/components/ui/calendar";
const Page: React.FC = () => {
// スイッチ関連
const [isDebugMode, setIsDebugMode] = useState(false);
const handleIsDebugModeChange = (checked: boolean) => {
if (checked) {
console.log("Debug mode is enabled.");
} else {
console.log("Debug mode is disabled.");
}
setIsDebugMode(checked);
};
// カレンダ関連
const dtFmt = "YYYY年MM月DD日が選択されました。";
const [date, setDate] = useState<Date | undefined>();
const [msg, setMsg] = useState<string>("日付を選択してください。");
const handleDateChange = (date: Date | undefined) => {
if (date === undefined) {
setMsg("日付を選択してください。");
return;
}
setMsg(dayjs(date).format(dtFmt));
setDate(date);
};
return (
<main>
<div className="mb-4 text-2xl font-bold">shadcn/ui</div>
<div className="flex flex-col gap-y-4">
<div className="flex items-center gap-x-3">
<Switch
id="debug-mode"
checked={isDebugMode}
onCheckedChange={handleIsDebugModeChange}
/>
<label htmlFor="debug-mode" className="cursor-pointer">
デバッグモード
</label>
</div>
<div className="">
<Calendar
mode="single"
selected={date}
onSelect={handleDateChange}
className="inline-block rounded-md border"
/>
</div>
<div>{msg}</div>
</div>
</main>
);
};
export default Page;6 Lucide React Icon
shadcn/ui をインストールすると、それに関連する関係で lucide-react
という「アイコンセットを提供するライブラリ」もインストールされます。このライブラリはアイコンは、既に紹介済みのFontAwesomeと併用することも可能です。
Lucide React が提供するアイコンの一覧 (2025/01/29時点で1548個) はこちらから検索・確認できます。
以下に、実装例を示します。
"use client";
import { Camera, ThumbsUp, Squirrel, Settings } from "lucide-react";
const Page: React.FC = () => {
return (
<main>
<div className="mb-4 text-2xl font-bold">Lucide React Icon</div>
<div className="flex gap-4">
<div className="flex flex-col items-center">
<div className="rounded-md border-2 p-2">
<Camera className="size-24 text-red-500" />
</div>
<div>Camera</div>
</div>
<div className="flex flex-col items-center">
<div className="rounded-md border-2 p-2">
<ThumbsUp className="size-24 text-red-500" />
</div>
<div>ThumbsUp</div>
</div>
<div className="flex flex-col items-center">
<div className="rounded-md border-2 p-2">
<Squirrel className="size-24 text-red-500" />
</div>
<div>Squirrel</div>
</div>
<div className="flex flex-col items-center">
<div className="rounded-md border-2 p-2">
<Settings className="size-24 text-red-500" />
</div>
<div>Settings</div>
</div>
</div>
</main>
);
};
export default Page;