この記事は約 4 分で読めます。
筆者プロフィール: ソフトウェアエンジニア。「知った気にならない。いつまでも学び続ける」を信条に、業務と個人開発の両輪で技術を磨いています。AI 駆動開発で複数の個人開発アプリを構築・運用中。
👉 ポートフォリオ: 筆者ホームページ
長文編集ダイアログで 「もっと広く使いたい」 ユーザの声に応える、useDialogFullscreen カスタムフックを整理します。運用中の SaaS 「たすきば Knowledge Relay」 で 9 箇所に適用している UI パターン (KDD §5.16) です。
サービスの機能紹介・画面イメージ・コンセプトは公式プロダクトページをご覧ください。
👉 たすきば Knowledge Relay — 公式プロダクトページ
背景
業務 SaaS の編集ダイアログ:
- ナレッジの編集 (Markdown content が長い)
- リスク / 課題の編集 (description + resolution_note)
- 振り返り (KPT / Try)
長文を書く場面が多い。通常サイズ (60vw × 70vh) だと:
| 問題 | 内容 |
|---|---|
| 大画面で狭く感じる | スクロール必要 |
| 余白が無駄 | 画面情報を活かせない |
ユーザに「広く使いたい」というニーズがあったため、全画面トグル機能 を導入しました。
1. 仕様
[通常モード]
60vw × 70vh
[全画面モード]
90vw × 90vh
ボタン 1 つで切替可能。
2. useDialogFullscreen フック
// src/lib/hooks/useDialogFullscreen.ts
'use client';
import { useState } from 'react';
export function useDialogFullscreen(initialState = false) {
const [isFullscreen, setIsFullscreen] = useState(initialState);
const toggle = () => setIsFullscreen(prev => !prev);
const setFullscreen = (value: boolean) => setIsFullscreen(value);
const reset = () => setIsFullscreen(false);
return { isFullscreen, toggle, setFullscreen, reset };
}
状態と操作を 1 つにまとめた、シンプルなフック。
3. 使用例
'use client';
import { useDialogFullscreen } from '@/lib/hooks/useDialogFullscreen';
import { Dialog, DialogContent } from '@/components/ui/dialog';
export function KnowledgeEditDialog({ open, onClose, knowledge }) {
const { isFullscreen, toggle, reset } = useDialogFullscreen();
function handleClose() {
reset(); // 閉じるとき、全画面状態をリセット
onClose();
}
return (
<Dialog open={open} onOpenChange={handleClose}>
<DialogContent
className={isFullscreen
? 'w-[90vw] h-[90vh] max-w-none max-h-none'
: 'w-[60vw] h-[70vh] max-w-3xl'
}
>
<div className="flex justify-between">
<h2>ナレッジを編集</h2>
<button onClick={toggle} aria-label="全画面切替">
⛶
</button>
</div>
</DialogContent>
</Dialog>
);
}
4. 閉じる時にリセット
ダイアログを閉じるとき、全画面状態をリセット (reset()) するのがポイント。
これをしないと:
1. ユーザが全画面で編集 → 閉じる
2. 次に同じダイアログを開くと 全画面のまま
3. 「なぜいつも全画面なの?」とユーザが混乱
毎回「通常モード」で始まるようにリセットします。
5. 同一コンポーネント内の複数 dialog
ある画面で、編集ダイアログ + 確認ダイアログ + 共有ダイアログ などが共存する場合。
function KnowledgePage() {
const editDialog = useDialogFullscreen();
const shareDialog = useDialogFullscreen();
const confirmDialog = useDialogFullscreen();
return (
<>
<KnowledgeEditDialog {...editDialog} />
<KnowledgeShareDialog {...shareDialog} />
<KnowledgeConfirmDialog {...confirmDialog} />
</>
);
}
各 dialog ごとに 独立したフックインスタンス。状態が混ざりません。
6. 適用済 dialog (9 箇所)
KDD §5.16 によると、以下 9 箇所で useDialogFullscreen を適用:
1. ナレッジ編集ダイアログ
2. リスク編集ダイアログ
3. 課題編集ダイアログ
4. 振り返り編集ダイアログ
5. メモ編集ダイアログ
6. プロジェクト編集ダイアログ
7. タスク詳細ダイアログ
8. 顧客編集ダイアログ
9. メンバー詳細ダイアログ
「長文を編集する可能性がある」「画面情報が多い」ダイアログすべてに適用。
7. 全画面化しない dialog の判断
すべての dialog に全画面トグルを付けたわけではありません。
| Dialog | 全画面化? |
|---|---|
| 確認ダイアログ (「本当に削除しますか?」) | ❌ 短い内容なので不要 |
| ロール選択ダイアログ | ❌ リスト選択のみ |
| 簡易フィルタダイアログ | ❌ 数項目のみ |
「長文 / 多項目を扱う」場合のみ、全画面化を提供。
8. アクセシビリティ
<button
onClick={toggle}
aria-label="全画面切替"
aria-expanded={isFullscreen}
>
{isFullscreen ? '通常表示に戻す' : '全画面表示'}
</button>
| 配慮 | 内容 |
|---|---|
aria-label |
スクリーンリーダ対応 |
aria-expanded |
状態が変わったことを伝える |
| キーボード操作 | Tab + Enter で切替可能 |
9. なぜ fullscreen API を使わないか
ブラウザの element.requestFullscreen() API もあります。F11 のような真の全画面化が可能。
しかし、
| 問題 | 内容 |
|---|---|
| ブラウザ UI が消える | ヘッダ / アドレスバーも消える |
| ユーザに違和感 | 強すぎる |
| アプリヘッダ / フッタも消える | 不要な情報損失 |
たすきばが目指すのは 「ダイアログを大きくする」だけ。ブラウザ UI は残したい。
そのため、requestFullscreen ではなく、CSS の width / height で実現。
おわりに
| 仕組み | 効果 |
|---|---|
useDialogFullscreen フック |
状態管理を共通化 |
| 60vw × 70vh ↔ 90vw × 90vh | 通常 / 全画面の切替 |
| 閉じるときにリセット | 次回のために初期状態維持 |
| 9 箇所に適用 | 全画面でユーザ体験統一 |
| Visual Regression で検証 | 見え方を構造的に保証 |
個人開発でも、UI の使い勝手を 1 段階上げる小さくて強力なパターン です。
本記事のフックは、運用中の SaaS 「たすきば Knowledge Relay」 で実装しています。
👉 たすきば Knowledge Relay — 公式プロダクトページ