• #miller-column
  • #vue
  • #interactive
  • #vitest
  • #eurekapu
  • #scoped-css
開発eurekapu-nuxt4メモ

簿記入門「概要とまとめ」インタラクティブセクション移行

旧プロジェクトの「概要とまとめ」ページには、財務3表コンポーネント、取引ボタン、仕訳帳・総勘定元帳がインタラクティブに動く仕組みがあった。これをMiller Columnsの4列目にそのまま載せ替える作業を1日かけて回した。計画をCodexに投げ、コンポーネント6個とテスト396本を通し、Chrome DevToolsで全セクションを目視確認する流れだが、aspect-ratio問題に3回引っかかり、画像マッピングのずれに足を取られた。

計画とCodexレビュー

旧プロジェクトのソースコードを一通り読み、4フェーズの計画を立てた。

  • Phase 1: データ型・ユーティリティ・計算ロジック(summaryTypes.ts, summaryUtils.ts, financialStatements.ts)
  • Phase 2: コンポーネント6個(JournalBook, GeneralLedger, AccountTab, TransactionTable, SummaryBSPLSS, TrialBalance)
  • Phase 3: MillerViewer.vueのslot機構で01-summary.vueにインタラクティブコンテンツを描画
  • Phase 4: 微調整

MillerViewer.vueを読んだとき、L355-366に$slots.defaultがあればTheaterViewerの代わりにカスタムコンテンツを表示するslot機構が既にあることに気づいた。これを活用すれば、4列目のコンテンツ領域にそのまま差し込める。

Codexにレビューを投げたら、致命的指摘が2点返ってきた。

  1. 非インタラクティブセクションのフォールバック: slot内でもTheaterViewerを使うセクション(「簿記の5つのStep」など)があるが、そのフォールバック処理が計画に書かれていない
  2. スライドとボタンの同期: 取引テーブルの行とスライドのインデックスを連動させる仕組みが欠けている

両方修正してから実装に入った。

Phase 1-2: 型・ユーティリティ・コンポーネント

Phase 1では3つの純粋関数ファイルを作成し、29テスト全パス。Phase 2ではコンポーネント6個を一気に作り、financialStatements.tsのテストも追加して59テスト全パス。

テストは計画時点では入れ忘れていたが、ユーザーから「副作用のない関数にしてテストコードを順次追加してください」と指摘が飛んできて、途中から並行してテストを書いた。

Phase 3-4: slot統合と全396テストパス

MillerViewer.vueのslotにpropsを追加し、01-summary.vueで各STEPのインタラクティブコンテンツを描画。非インタラクティブセクションにはTheaterViewerがフォールバック表示される。

全36ファイル、396テストが全てパス。既存テストへの影響はゼロだった。

aspect-ratio問題に3回引っかかった

Chrome DevToolsで「取引内容」セクションを開いたら、画像の高さが0pxに潰れていた。

1回目: MillerViewerのscoped CSSに .miller .theater-stage { aspect-ratio: unset } というルールが存在していて、slot内のTheaterViewerにも効いていた。01-summary.vueの:deepaspect-ratio: 4/3を復元して解決。

2回目: フォールバックセクション(「簿記の5つのStep」など)でも画像が表示されなくなった。01-summary.vueのscoped CSSはslot内のTheaterViewerに届かないことが判明。MillerViewer側に.detail-body-custom :deep(.theater-stage)aspect-ratio: 4/3を復元するルールを追加。

3回目: .detail-body-customdisplay: blockから親のflex layoutに戻した際、再びslot内のTheaterViewerの画像が消えた。scrollStageモードのときだけaspect-ratio: unsetに除外する分岐を入れて、ようやく安定した。

/* slot内ではaspect-ratioを復元、scrollStageは除外 */
.detail-body-custom :deep(.theater-stage) {
  aspect-ratio: 4/3;
}
.detail-body-custom :deep(.theater-stage.scroll-stage) {
  aspect-ratio: unset;
}

scoped CSSの:deepがどこまで届くかを頭で理解していても、slot境界で挙動が変わるポイントで毎回手が止まった。

画像マッピングのずれ

Chrome DevToolsで「各章の論点一覧」セクションを確認していたとき、ユーザーから「手形のところが預金になってるじゃないですか」と指摘が入った。

データ上はchapter04_1.svgを手形セクションに割り当てていたが、SVGファイルの中身を開くと「預金勘定 Chapter 04」と書いてあった。ファイル名のchapter番号と実際の内容が1つずれていた。

ブラウザで各SVGを1枚ずつ開いて、実際の中身を確認していった。

ファイル実際の内容
chapter03現金
chapter04預金(手形ではない)
chapter05手形
chapter06電子記録債権債務

全セクションのマッピングを修正した。ファイル名と中身が一致している保証はどこにもない。今回は目視で発覚したが、DevToolsを開かなければ気づけなかった。

仕訳帳・総勘定元帳の空行とナビゲーションヒント

仕訳帳に3件しかデータがないとき、表がデータ3行だけで終わってしまう。「簿記をやる人はノートをイメージする」という指摘を受けて、minRows: 14で空行(罫線のみ)を追加した。総勘定元帳も同様。合計行は常にテーブル最下部に固定し、データ行→空行→合計行の順で表示。

disabledボタンのナビゲーションヒントも実装した。ボタンが全てdisabledのセクションにランディングしたとき、「どこを押せばいいのか」がわからない問題を解決するため、セクションの完了状態に応じてメッセージを出し分ける。

  • このセクションに押せるボタンがある → ヒント非表示
  • このセクションのボタンは全て完了 → 「次はSTEP Xに進んでください」
  • 前のSTEPが未完了 → 「先にSTEP Xを実行してください」

ヒントの表示位置も、ボタンと帳簿の間に入れると帳簿の位置がずれるため、帳簿の下に移動した。

取引テーブルとスライドのハイライト連動

スライドを切り替えると、対応する取引テーブルの行がハイライトされる仕組みを入れた。TransactionTableにactiveIndex propsを追加し、スライドのインデックスを渡す。アクティブな行には白背景+左側に青い縦線が入り、今どの取引を見ているかが一目でわかる。

振り返り

1日で型定義・ユーティリティ・コンポーネント6個・テスト396本・slot統合・微調整まで走りきった。ただし、時間の大半はaspect-ratio問題と画像マッピングのずれに費やした。コードを書く速度よりも、scoped CSSのslot境界での挙動を理解してDevToolsで検証する工程、そしてSVGファイルを1枚ずつ開いて中身を目で確かめる工程が律速になった。

テストが396本通っていても、画面を開いた瞬間に画像が0pxに潰れている。テストはロジックを守るが、CSSのslot境界やSVGのファイル名と中身のずれまでは拾えない。最後は目で見て確かめるしかなかった。