• #vue
  • #refactoring
  • #component
  • #implementation-plan
開発アクティブ

CompanyHeaderコンポーネント共通化計画

進捗状況(2026-01-05確認): 未着手

  • CompanyHeader.vue コンポーネント未作成
  • proportional-animation.vue で直接テンプレート展開のまま
  • ExportContainer.vue で直接テンプレート展開のまま

背景・課題

proportional-animation-qqq.vueでは、企業情報(企業名・セクター・単位ラベル)を2箇所で表示している:

  1. メイン表示 (.company-header): 通常のブラウザ表示用
  2. エクスポートコンテナ (.export-header): 動画ダウンロード用(1920x1080)

現状、同じ情報を2箇所で管理しているため、片方を変更するともう片方の変更を忘れやすい。

解決策

企業ヘッダー部分を共通コンポーネント CompanyHeader.vue として切り出す。

新規作成ファイル

apps/web/app/components/financial-quiz/CompanyHeader.vue

<template>
  <div class="company-header" :class="sizeClass">
    <h2 :id="company.ticker">
      {{ company.name }}
    </h2>
    <div class="sector-row">
      <p v-if="company.sector" class="sector-label">
        {{ company.sector }}
        <span v-if="company.industry"> / {{ company.industry }}</span>
      </p>
      <span class="unit-label">(単位: ${{ unitSuffix }})</span>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'

interface Company {
  ticker: string
  name: string
  sector?: string
  industry?: string
}

interface Props {
  company: Company
  unitSuffix: string  // 'B' or 'M'
  size?: 'normal' | 'large'  // large = エクスポート用
}

const props = withDefaults(defineProps<Props>(), {
  size: 'normal'
})

const sizeClass = computed(() => `size-${props.size}`)
</script>

<style scoped>
.company-header {
  text-align: center;
}

/* 通常サイズ(メイン表示用) */
.company-header.size-normal h2 {
  font-size: 1.3rem;
  margin: 0;
  color: #333;
}

.company-header.size-normal .sector-row {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 1rem;
  margin-top: 0.25rem;
}

.company-header.size-normal .sector-label {
  font-size: 0.8rem;
  color: #888;
  margin: 0;
}

.company-header.size-normal .unit-label {
  font-size: 0.75rem;
  color: #666;
  font-weight: normal;
}

/* 大サイズ(エクスポート用) */
.company-header.size-large {
  margin-bottom: 16px;
}

.company-header.size-large h2 {
  font-size: 36px;
  font-weight: 700;
  margin: 0;
  color: #1a1a1a;
}

.company-header.size-large .sector-row {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 1rem;
  margin-top: 8px;
}

.company-header.size-large .sector-label {
  font-size: 18px;
  color: #666;
  margin: 0;
}

.company-header.size-large .unit-label {
  font-size: 16px;
  color: #666;
  font-weight: normal;
}
</style>

修正ファイル

apps/web/app/pages/financial-quiz/proportional-animation-qqq.vue

変更前(メイン表示)

<!-- 企業情報 -->
<div class="company-header">
  <h2 :id="selectedCompany.ticker">
    {{ selectedCompany.name }}
  </h2>
  <div class="sector-row">
    <p v-if="selectedCompany.sector" class="sector-label">
      {{ selectedCompany.sector }}
      <span v-if="selectedCompany.industry"> / {{ selectedCompany.industry }}</span>
    </p>
    <span class="unit-label">(単位: ${{ unitSuffix }})</span>
  </div>
</div>

変更後(メイン表示)

<!-- 企業情報 -->
<CompanyHeader
  :company="selectedCompany"
  :unit-suffix="unitSuffix"
  size="normal"
/>

変更前(エクスポートコンテナ)

<!-- ヘッダー: 企業情報 -->
<div class="export-header">
  <h2>{{ selectedCompany.name }}</h2>
  <div class="sector-row">
    <p v-if="selectedCompany.sector" class="export-sector">
      {{ selectedCompany.sector }}
      <span v-if="selectedCompany.industry"> / {{ selectedCompany.industry }}</span>
    </p>
    <span class="unit-label">(単位: ${{ unitSuffix }})</span>
  </div>
</div>

変更後(エクスポートコンテナ)

<!-- ヘッダー: 企業情報 -->
<CompanyHeader
  :company="selectedCompany"
  :unit-suffix="unitSuffix"
  size="large"
/>

CSS削除対象

以下のCSSは CompanyHeader.vue に移動するため、ページから削除:

/* 削除: 企業ヘッダー関連 */
.company-header { ... }
.company-header h2 { ... }
.sector-row { ... }
.sector-label { ... }
.unit-label { ... }

/* 削除: エクスポートヘッダー関連 */
.export-header { ... }
.export-header h2 { ... }
.export-sector { ... }
.export-header .sector-row { ... }
.export-header .unit-label { ... }

実装手順

  1. CompanyHeader.vue を新規作成
  2. proportional-animation-qqq.vue のメイン表示部分を <CompanyHeader> に置換
  3. エクスポートコンテナ部分を <CompanyHeader size="large"> に置換
  4. 不要になったCSSを削除
  5. ブラウザで表示確認
  6. 動画エクスポートで表示確認

メリット

  • 変更が1箇所で済む(DRY原則)
  • 今後の表示項目追加も1箇所で対応可能
  • テストしやすい
  • 他のページでも再利用可能

注意点

  • unitSuffix は親コンポーネントで計算して渡す(現状通り)
  • size propで表示サイズを切り替え
  • h2id 属性はアンカーリンク用に保持