CFWS v2のセル単位ハイライト + 全navigate型への横展開
CFWS(キャッシュフロー精算表)の「税引前当期純利益」をクリックすると、年次推移表のFY202603の該当セルにジャンプする。FY202603の税引前セルがオレンジ枠で囲まれ、scrollIntoView で画面中央に滑り込む。同じ仕組みを年次推移表・財務シート・投資シート・株式発行シートの全navigate型に展開した。途中で「建物と備品が別々にハイライトされない」問題を踏み、複合キーの優先順位を逆転させて解消した。CSS specificityの罠も2回踏み、最後に simplify をかけて共通composablesに整理した。
モデルケース: 「税引前当期純利益」のセル単位ハイライト
最初のモデルケースとして、CFWSの「税引前当期純利益」リンクから年次推移表へ飛ぶ動線を組んだ。
CfItemAction 型に highlightYearIndex を追加し、CFWSの年度クリックと連動できるようにした。
// app/types/cashflow-statement.ts
export interface CfItemAction {
navigate: { type: NavigateType; key?: string }
highlightAccountKey?: string
highlightYearIndex?: number // 追加
}
V2AccountingSheet側で highlightYearIndex を受け取り、対応セルにクラスと ref を付与する。
<td
:ref="(el) => setCellRef(el, year, '税引前当期純利益')"
:class="{ 'cell-highlight': isHighlighted(year, '税引前当期純利益') }"
>
{{ fmt(income) }}
</td>
ブラウザで叩くと、FY202603の▲2,113,770セルがオレンジ枠で囲まれ、scrollIntoView({ block: 'center' }) で画面中央に揃った。テストも17件追加して79/79 pass。
試行錯誤1: 建物と備品が同じセルにハイライトする
ここでユーザーから指摘が入った。「建物の減価償却費(150万円)と備品の減価償却費(12万円)を別々にクリックしても、合計行の162万円にハイライトが飛んでしまう」。
スクリーンショットを2枚並べて見ると、確かに両方とも「合計」セルにオレンジ枠がついていた。
原因1: 単独キーが先に拾われる
cfItemActions は '減価償却費' という単独キーで登録されていた。建物クリックでも備品クリックでも、このキーが最初にヒットしてしまい、複合キー('資産名:列名' 形式)にたどり着かない。
複合キーを後から追加して cfItemActions['減価償却費:建物'] を登録しても、CfWorksheetTable.handleCfItemClick 側の参照順が「単独キー → 複合キー」のままだったため、依然として単独キーが先に拾われていた。順序を逆転させて解決。
// Before: 単独キーが先
const action = cfItemActions[cfItemName] ?? cfItemActions[`${cfItemName}:${bsAccount}`]
// After: 複合キーが先
const action = cfItemActions[`${cfItemName}:${bsAccount}`] ?? cfItemActions[cfItemName]
原因2: cfSourceTableから親科目を拾えていない
buildV2CfItemActions の中で、cfSourceTable の row だけを見て複合キーを生成していた。ところが row には 減価償却累計額 しか入っておらず、建物 備品 のような親科目(tangible 系)が抽出できない。tangibleAccounts を別途引き、親科目名で複合キーを組み立てる方式に変更した。
const tangibleAccounts = bsAccounts.filter(a => a.kind === 'tangible')
for (const asset of tangibleAccounts) {
cfItemActions[`減価償却費:${asset.name}`] = {
navigate: { type: 'investment', key: asset.name },
highlightAccountKey: `${asset.name}:減価償却`, // 行+列の複合キー
highlightYearIndex: i,
}
}
V2InvestmentSheet側では、'資産名:列名' 形式の highlightKey をパースして「行のセル単位」でハイライト判定する。
<td :class="{
'cell-highlight': year.index === highlightYearIndex
&& `${assetName}:${columnName}` === highlightAccountKey
}">
ハードリロード後にURLが ?ihl=建物:減価償却&ihy=1 に変わり、建物行の▲1,500,000セルだけがオレンジ枠に。備品クリックも▲120,000のセル単独でハイライトされた。合計行(▲1,620,000)には何もつかない。テストは86/86 pass。
CSS specificityの罠(2回踏んだ)
CFWSとは別の話だが、同日に2回踏んだので記録する。
1回目: 文字色が緑にならない
CF計算書の年次推移ビューで、navigate可能な項目(他シート参照リンク)の文字色が緑にならない。マウスオーバーした瞬間だけ緑に変わる。
DevToolsで確認すると、.cf-table td { color: #000000 } が .navigable { color: #008000 } を上書きしていた。
| セレクタ | specificity | 値 |
|---|---|---|
.cf-table td | (0,1,1) | color: #000000 |
.navigable | (0,1,0) | color: #008000 |
specificityが (0,1,1) > (0,1,0) なので黒が勝つ。:hover 擬似クラスがつくと specificity が上がるので、マウスオーバー時だけ緑に変わるという奇妙な挙動になっていた。
セレクタを .cf-table td.navigable (0,1,2) に上げて修正した。
2回目: 数値が右寄せにならない
同じ犯人で、.cf-table td { text-align: left } が .num { text-align: right } を上書き。.cf-table td.num に書き直して解決。
CSSの specificity は頭で覚えていても、実装中は油断する。.table td のような共通スタイルを書いた瞬間に、後付けの修飾クラスが効かなくなる。
全navigate型への横展開
モデルケースが固まったので、finance investment equity accounting の全navigate typeに同期 ref を [...slug].vue で実装した。
- finance: 借入シートは1行集約のため、
highlightYearIndexだけ追加して列ハイライトと年度連動 - equity: 専用 ref を作らず
store.setYearIndexを直接呼んで簡略化 - investment / accounting: モデルケースと同じ複合キー方式
借入金返済をクリックすれば finance/FY202703 にジャンプし、株式発行をクリックすれば equity の対応年度セルにジャンプする。
simplifyリファクタ
横展開後、/simplify で共通化した。3つのレビューエージェントを並列で走らせ、重複度の高い問題から潰す。
- 共通フォーマッタ(金額・パーセント表示)を抽出
- 複合キーの encode/decode を
composables/useCellHighlightに集約 - composables(
useCellScroll,useYearSync)を新設し、5つのシート(V2AccountingSheet / V2InvestmentSheet / V2FinanceSheet / V2EquitySheet / V2CfStatementSheet)を共通化 buildV2CfItemActionsの signature を変更し、tangibleAccountsを1回だけ計算
テスト 86 件 pass を維持。コミット 2294e3d に15ファイル(+622 / -275行)を記録した。
ミラーカラムレイアウトへの統合
夕方になって追加要望が出た。CFWSの年度切替が「FY202603 / FY202703 / FY202803 / FY202903 / FY203003」のボタン形式になっていた。これをミラーカラムレイアウトの第3カラム(年度選択列)に統合し、矢印キーで年度を移動できるようにしてほしい、とのこと。
globalIndex を更新して worksheet 専用の年度サブアイテムを追加し、worksheetYearIndex で同期させた。NavEntry 型にも worksheetYearIndex を追加して currentNavIndex / goTo を更新。V2WorksheetSheet.vue から年度ボタンを削除して完了。
これで矢印キーだけで年度を切り替えられるようになり、CFWSの見た目もすっきりした。
scheduled_tasks.lock のコミット事故
途中で .claude/scheduled_tasks.lock をコミットに含めかけた。ScheduleWakeup で作られた古いロックファイルで、Claude Code 内部のもの。削除して .gitignore に追加した。
学びメモ
- 複合キーで参照する設計に切り替えるときは、呼び出し側の参照順も同時に逆転させる。片方だけ直しても、結局単独キーが先に拾われて意味がない
- CSS specificityは頭で計算する前にDevToolsの「Computed」タブを開く方が早い。
:hoverで値が変わる現象が出たら specificity 戦争のサイン - 同じ作業を5シートに展開するときは、最初の1つを終わらせてから簡素化するのではなく、横展開した直後に simplify をかけると composables の境界が見つけやすい
- HMRが効かない場面(ローン編集UIなど state を持つコンポーネント周辺)では、ハードリロードを早めに試す。「修正した気になっていた」事故が減る