Collapse 折りたたみパネル - Vant 4
📂 紹介
Collapse 折りたたみパネルは美しいアコーディオンのような本です 📖。各ページは豊富なコンテンツを提供し、軽くタップするだけで優雅に展開または折りたたむことができます。それはページ空間の魔法使いのようです ✨。複雑な情報をシンプルなタイトルの背後に巧妙に隠し、ユーザーは必要に応じて探索できます。インターフェイスの美しさと情報の完全な伝達を両立させます。FAQ、製品の詳細、設定オプションなど、折りたたみパネルはコンテンツの表示を整然とし、ユーザーに快適な閲覧体験を提供します。
📦 導入
以下の方法でグローバルにコンポーネントを登録します。詳細な登録方法についてはコンポーネントの登録を参照してください。
import { createApp } from 'vue'; import { Collapse, CollapseItem } from 'vant';
const app = createApp(); app.use(Collapse); app.use(CollapseItem);
## 🎯 コード例
### 基本的な使い方
`v-model` という親切な管理人 👨💼 を通じて、展開するパネルのリストを精確に制御します。`activeNames` 配列はVIPリストのように、どのパネルがその内容を表示する権利があるかを記録します。複数のパネルを同時に表示したいですか?問題ありません!この自由度によって、コンテンツの表示が思いのままになります。まるで交響楽団を指揮するかのように、各楽章を必要に応じて演奏できます。
```html
<van-collapse v-model="activeNames">
<van-collapse-item title="タイトル1" name="1">
コンテンツ1
</van-collapse-item>
<van-collapse-item title="タイトル2" name="2">
コンテンツ2
</van-collapse-item>
<van-collapse-item title="タイトル3" name="3">
コンテンツ3
</van-collapse-item>
</van-collapse>
```js
import { ref } from 'vue';
export default {
setup() {
const activeNames = ref(['1']);
return { activeNames };
},
};アコーディオン
accordion アコーディオンモードを有効にすると、優雅なピアニスト 🎹 のように、同じ時間に一つの美しいメロディに集中します。この時、activeName は文字列形式の独奏者に変身し、ステージには常に一つの主役だけが輝くことを確認します。この専門的な表示方法によって、ユーザーの注意力がより集中し、情報過多の問題を回避し、シンプルでありながら簡単ではない視覚体験を提供します。
<van-collapse v-model="activeName" accordion>
<van-collapse-item title="タイトル1" name="1">
コンテンツ1
</van-collapse-item>
<van-collapse-item title="タイトル2" name="2">
コンテンツ2
</van-collapse-item>
<van-collapse-item title="タイトル3" name="3">
コンテンツ3
</van-collapse-item>
</van-collapse>
```js
import { ref } from 'vue';
export default {
setup() {
const activeName = ref('1');
return { activeName };
},
};無効な状態
特定のパネルに disabled 属性を設定することで、「邪魔しないでください」という標識 🚫 をつけることができます。それらは存在感を維持しながら、誤ってトリガーされることもありません。この思いやりのある保護メカニズムによって、重要なコンテンツの安全性が確保されます。まるで貴重なアート作品に保護シールドを追加するようなものです。
<van-collapse v-model="activeNames">
<van-collapse-item title="タイトル1" name="1">
コンテンツ1
</van-collapse-item>
<van-collapse-item title="タイトル2" name="2" disabled>
コンテンツ2
</van-collapse-item>
<van-collapse-item title="タイトル3" name="3">
コンテンツ3
</van-collapse-item>
</van-collapse>
### カスタムタイトルコンテンツ
`title` スロットというクリエイティブデザイナー 🎨 を通じて、タイトルバーに個性的な魂を注入することができます。単調なテキストに限定されず、アイコン、バッジ、アニメーション効果などを追加して、各パネルのタイトルを独特なアートワークにすることができます。ユーザーの目を引き、より豊富な情報を伝えます。
```html
<van-collapse v-model="activeNames">
<van-collapse-item name="1">
<template #title>
<div class="custom-title">
<span class="title-text">カスタムタイトル</span>
<van-tag type="primary">NEW</van-tag>
</div>
</template>
コンテンツ
</van-collapse-item>
</van-collapse>
```js
import { ref } from 'vue';
export default {
setup() {
const activeNames = ref(['1']);
return { activeNames };
},
};すべて展開とすべて切り替え
Collapse インスタンスの toggleAll という全能の指揮者 🎭 を通じて、すべてのパネルの集団パフォーマンスを一撃で実現できます。すべてのコンテンツを一見したいですか?それともシンプルな状態に戻したいですか?指揮棒を軽く振るだけで、すべてのパネルが整然とした協調性を見せながら、あなたの命令に応じます。まるで訓練された合唱団のように、完璧な調和を示します。
<van-collapse v-model="activeNames" ref="collapse">
<van-collapse-item title="タイトル1" name="1">
コンテンツ1
</van-collapse-item>
<van-collapse-item title="タイトル2" name="2">
コンテンツ2
</van-collapse-item>
<van-collapse-item title="タイトル3" name="3">
コンテンツ3
</van-collapse-item>
</van-collapse>
<div class="demo-collapse-button">
<van-button type="primary" @click="openAll">すべて展開</van-button>
<van-button @click="toggleAll">すべて切り替え</van-button>
</div>
```js
import { ref } from 'vue';
export default {
setup() {
const activeNames = ref(['1']);
const collapse = ref(null);
const openAll = () => {
collapse.value.toggleAll(true);
};
const toggleAll = () => {
collapse.value.toggleAll();
};
return { activeNames, openAll, toggleAll, collapse };
},
};Tips: アコーディオンモードでは toggleAll メソッドを使用できません。
API
Collapse Props
| パラメータ | 説明 | タイプ | デフォルト値 |
|---|---|---|---|
| v-model | 現在展開中のパネルの名前 | アコーディオンモード:number | string非アコーディオンモード:(number | string)[] | - |
| accordion | アコーディオンモードを有効にするかどうか | boolean | false |
| border | 外枠を表示するかどうか | boolean | true |
Collapse Events
| イベント名 | 説明 | コールバックパラメータ |
|---|---|---|
| change | パネルを切り替えたときに発生 | activeNames: v-model にバインドされた値と同じ型 |
CollapseItem Props
| パラメータ | 説明 | タイプ | デフォルト値 |
|---|---|---|---|
| name | 一意の識別子、デフォルトはインデックス値 | number | string | index |
| icon | タイトルバー左側のアイコン名または画像リンク、Icon コンポーネントの name 属性 と同等 | string | - |
| size | タイトルバーのサイズ、large を選択可能 | string | - | | title | タイトルバー左側のコンテンツ | number | string | - | | value | タイトルバー右側のコンテンツ | number | string | - | | label | タイトルバーの説明情報 | number | string | - | | border | 内枠を表示するかどうか | boolean | true | | is-link | タイトルバー右側に矢印を表示し、クリックフィードバックを有効にするかどうか | boolean | true | | disabled | パネルを無効にするかどうか | boolean | false | | readonly | 読み取り専用モードかどうか、読み取り専用モードではパネルを操作できません | boolean | false | | lazy-render | 初めて展開する際にのみパネルのコンテンツをレンダリングするかどうか | boolean | true | | title-class | 左側のタイトルの追加クラス名 | string | - | | value-class | 右側のコンテンツの追加クラス名 | string | - | | label-class | 説明情報の追加クラス名 | string | - |
Collapse メソッド
ref を通じて CollapseItem インスタンスを取得し、インスタンスメソッドを呼び出すことができます。詳細についてはコンポーネントインスタンスメソッドを参照してください。
| メソッド名 | 説明 | パラメータ | 戻り値 |
|---|---|---|---|
| toggleAll | すべてのパネルの展開状態を切り替え、true を渡すとすべて展開、false を渡すとすべて折りたたみ、パラメータなしではすべて切り替え | options?: boolean | object | - |
toggleAll メソッドの例
import { ref } from 'vue';
import type { CollapseInstance } from 'vant';
const collapseRef = ref<CollapseInstance>();
// すべて切り替え
collapseRef.value?.toggleAll();
// すべて展開
collapseRef.value?.toggleAll(true);
// すべて折りたたみ
collapseRef.value?.toggleAll(false);
// すべて切り替え、無効なパネルをスキップ
collapseRef.value?.toggleAll({
skipDisabled: true,
});
// すべて展開、無効なパネルをスキップ
collapseRef.value?.toggleAll({
expanded: true,
skipDisabled: true,
});CollapseItem メソッド
ref を通じて CollapseItem インスタンスを取得し、インスタンスメソッドを呼び出すことができます。詳細についてはコンポーネントインスタンスメソッドを参照してください。
| メソッド名 | 説明 | パラメータ | 戻り値 |
|---|---|---|---|
| toggle | パネルの展開状態を切り替え、true を渡すと展開、false を渡すと折りたたみ、パラメータなしでは切り替え | expand?: boolean | - |
型定義
コンポーネントは以下の型定義をエクスポートしています:
import type {
CollapseProps,
CollapseItemProps,
CollapseItemInstance,
CollapseToggleAllOptions,
} from 'vant';CollapseItemInstance はコンポーネントインスタンスの型で、次のように使用します:
import { ref } from 'vue';
import type { CollapseItemInstance } from 'vant';
const collapseItemRef = ref<CollapseItemInstance>();
collapseItemRef.value?.toggle();CollapseItem Slots
| 名前 | 説明 |
|---|---|
| default | パネルのコンテンツ |
| title | カスタムタイトルバー左側のコンテンツ |
| value | カスタムタイトルバー右側のコンテンツ |
| label | カスタムタイトルバーの説明情報 |
| icon | カスタムタイトルバー左側のアイコン |
| right-icon | カスタムタイトルバー右側のアイコン |
テーマカスタマイズ
スタイル変数
コンポーネントは以下の CSS 変数を提供しており、カスタムスタイルに使用できます。使用方法については ConfigProvider コンポーネント を参照してください。
| 名前 | デフォルト値 | 説明 |
|---|---|---|
| --van-collapse-item-duration | var(--van-duration-base) | - |
| --van-collapse-item-content-padding | var(--van-padding-sm) var(--van-padding-md) | - |
| --van-collapse-item-content-font-size | var(--van-font-size-md) | - |
| --van-collapse-item-content-line-height | 1.5 | - |
| --van-collapse-item-content-text-color | var(--van-text-color-2) | - |
| --van-collapse-item-content-background | var(--van-background-2) | - |
| --van-collapse-item-title-disabled-color | var(--van-text-color-3) | - |
ベストプラクティス
コンテンツ組織戦略
折りたたみパネルのコンテンツ構造を合理的に組織しましょう 📋:
<!-- ✅ 推奨:論理的に明確なコンテンツグループ -->
<van-collapse v-model="activeNames">
<van-collapse-item title="基本情報" name="basic">
<div class="info-section">
<p>名前:張三</p>
<p>年齢:25歳</p>
<p>職業:フロントエンドエンジニア</p>
</div>
</van-collapse-item>
<van-collapse-item title="連絡先" name="contact">
<div class="contact-section">
<p>電話:138-0000-0000</p>
<p>メール:zhangsan@example.com</p>
<p>住所:北京市朝陽区</p>
</div>
</van-collapse-item>
<van-collapse-item title="職歴" name="experience">
<div class="experience-section">
<!-- 詳細な職歴コンテンツ -->
</div>
</van-collapse-item>
</van-collapse>
<!-- ❌ 避ける:コンテンツが散らかったり集中しすぎたりする -->
<van-collapse v-model="activeNames">
<van-collapse-item title="すべての情報" name="all">
<!-- すべてのコンテンツを一つのパネルに詰め込む -->
</van-collapse-item>
</van-collapse>タイトル設計原則
<!-- ✅ 推奨:簡潔明確なタイトル -->
<van-collapse-item title="アカウント設定" name="account">
<template #title>
<div class="custom-title">
<van-icon name="setting-o" />
<span>アカウント設定</span>
<van-tag type="primary" size="mini">重要</van-tag>
</div>
</template>
<!-- コンテンツ -->
</van-collapse-item>
<!-- ❌ 避ける:タイトルが長すぎたり情報が明確でなかったりする -->
<van-collapse-item
title="これは非常に長いタイトルで、多くの不必要な説明情報が含まれています、ユーザーエクスペリエンスに影響します"
name="bad"
>
<!-- コンテンツ -->
</van-collapse-item>レスポンシブデザイン
<template>
<van-collapse
v-model="activeNames"
:accordion="isMobile"
:class="{ 'mobile-collapse': isMobile }"
>
<van-collapse-item
v-for="item in collapseItems"
:key="item.name"
:title="item.title"
:name="item.name"
>
<div :class="['content-wrapper', { 'mobile-content': isMobile }]">
{{ item.content }}
</div>
</van-collapse-item>
</van-collapse>
</template>
<script>
import { ref, computed, onMounted, onUnmounted } from 'vue';
export default {
setup() {
const screenWidth = ref(window.innerWidth);
const activeNames = ref(['1']);
const isMobile = computed(() => screenWidth.value < 768);
const updateScreenWidth = () => {
screenWidth.value = window.innerWidth;
};
onMounted(() => {
window.addEventListener('resize', updateScreenWidth);
});
onUnmounted(() => {
window.removeEventListener('resize', updateScreenWidth);
});
return { activeNames, isMobile };
}
};
</script>
<style>
.mobile-collapse .van-collapse-item__title {
font-size: 14px;
padding: 12px 16px;
}
.mobile-content {
padding: 12px 16px;
font-size: 14px;
line-height: 1.6;
}
</style>パフォーマンス最適化のヒント
コンテンツの遅延読み込み
lazy-render 属性を利用してパフォーマンスを最適化しましょう 🚀:
<!-- ✅ 推奨:複雑なコンテンツには遅延読み込みを使用 -->
<van-collapse v-model="activeNames">
<van-collapse-item
title="チャートデータ"
name="charts"
:lazy-render="true"
>
<div v-if="activeNames.includes('charts')">
<!-- 展開時のみ複雑なチャートコンポーネントをレンダリング -->
<heavy-chart-component />
</div>
</van-collapse-item>
</van-collapse>
<!-- 動的にコンテンツを読み込む -->
<van-collapse-item
title="ユーザーリスト"
name="users"
:lazy-render="true"
@click="loadUserData"
>
<div v-if="userDataLoaded">
<user-list :data="userData" />
</div>
<van-loading v-else>読み込み中...</van-loading>
</van-collapse-item>頻繁な再レンダリングを避ける
// ✅ 推奨:計算プロパティを使用してデータ処理を最適化
const processedItems = computed(() => {
return rawItems.value.map(item => ({
...item,
formattedDate: formatDate(item.date),
isHighlighted: item.priority === 'high'
}));
});
// ❌ 避ける:テンプレート内で複雑な計算を行う
// <van-collapse-item :title="formatComplexTitle(item)">v-show と v-if を合理的に使用
<!-- 頻繁に切り替えるコンテンツには v-show を使用 -->
<van-collapse-item title="素早く切り替えるコンテンツ" name="quick">
<div v-show="showQuickContent">
<!-- 頻繁に表示/非表示にするコンテンツ -->
</div>
</van-collapse-item>
<!-- 条件付きレンダリングには v-if を使用 -->
<van-collapse-item title="条件コンテンツ" name="conditional">
<div v-if="shouldRenderContent">
<!-- 条件によってレンダリングするかどうかを決めるコンテンツ -->
</div>
</van-collapse-item>デザインアドバイス
視覚階層
- タイトル階層:重要性を区別するために異なるフォントサイズと色を使用 📊
- コンテンツ間隔:一貫したパディングとマージンを維持 📏
- アイコンの使用:認識性を高めるためにアイコンを合理的に使用 🎯
- 状態フィードバック:明確な展開/折りたたみ状態の指示を提供 💫
インタラクション体験
- アニメーション効果:スムーズな展開/折りたたみアニメーションを使用 🎬
- タッチフレンドリー:モバイルデバイスで十分なタップ領域を確保 📱
- キーボードナビゲーション:アクセシビリティを向上させるためにキーボード操作をサポート ⌨️
- ローディング状態:非同期コンテンツにはローディングインジケータを提供 ⏳
よくある質問と解決策
Q: 折りたたみパネルのネストを実装するには?
<van-collapse v-model="parentActive">
<van-collapse-item title="親パネル" name="parent">
<van-collapse v-model="childActive" class="nested-collapse">
<van-collapse-item title="子パネル 1" name="child1">
<p>子コンテンツ 1</p>
</van-collapse-item>
<van-collapse-item title="子パネル 2" name="child2">
<p>子コンテンツ 2</p>
</van-collapse-item>
</van-collapse>
</van-collapse-item>
</van-collapse>
<style>
.nested-collapse {
margin: 12px 0;
border: 1px solid #ebedf0;
border-radius: 6px;
}
.nested-collapse .van-collapse-item__title {
background-color: #f7f8fa;
font-size: 14px;
}
</style>Q: 折りたたみパネルの検索フィルタを実装するには?
<template>
<div>
<van-search
v-model="searchKeyword"
placeholder="コンテンツを検索"
@input="handleSearch"
/>
<van-collapse v-model="activeNames">
<van-collapse-item
v-for="item in filteredItems"
:key="item.name"
:title="highlightTitle(item.title)"
:name="item.name"
>
<div v-html="highlightContent(item.content)"></div>
</van-collapse-item>
</van-collapse>
<van-empty
v-if="filteredItems.length === 0 && searchKeyword"
description="関連コンテンツが見つかりません"
/>
</div>
</template>
<script>
const searchKeyword = ref('');
const allItems = ref([
{ name: '1', title: 'ユーザー管理', content: 'システムユーザー情報を管理' },
{ name: '2', title: '権限設定', content: 'ユーザー権限と役割を設定' },
{ name: '3', title: 'データ統計', content: 'システムデータレポートを表示' }
]);
const filteredItems = computed(() => {
if (!searchKeyword.value) return allItems.value;
return allItems.value.filter(item =>
item.title.includes(searchKeyword.value) ||
item.content.includes(searchKeyword.value)
);
});
const highlightTitle = (title) => {
if (!searchKeyword.value) return title;
return title.replace(
new RegExp(searchKeyword.value, 'gi'),
`<mark>$&</mark>`
);
};
const highlightContent = (content) => {
if (!searchKeyword.value) return content;
return content.replace(
new RegExp(searchKeyword.value, 'gi'),
`<mark>$&</mark>`
);
};
</script>Q: 折りたたみパネルのドラッグソートを実装するには?
<template>
<van-collapse v-model="activeNames">
<draggable
v-model="sortableItems"
@end="handleDragEnd"
item-key="name"
>
<template #item="{ element }">
<van-collapse-item
:title="element.title"
:name="element.name"
class="draggable-item"
>
<template #title>
<div class="drag-title">
<van-icon name="bars" class="drag-handle" />
<span>{{ element.title }}</span>
</div>
</template>
{{ element.content }}
</van-collapse-item>
</template>
</draggable>
</van-collapse>
</template>
<script>
import draggable from 'vuedraggable';
const sortableItems = ref([
{ name: '1', title: 'プロジェクト 1', content: 'コンテンツ 1' },
{ name: '2', title: 'プロジェクト 2', content: 'コンテンツ 2' },
{ name: '3', title: 'プロジェクト 3', content: 'コンテンツ 3' }
]);
const handleDragEnd = (event) => {
console.log('ドラッグ完了', event);
// 新しい並び順をバックエンドに保存
saveSortOrder(sortableItems.value);
};
</script>
<style>
.drag-title {
display: flex;
align-items: center;
gap: 8px;
}
.drag-handle {
cursor: move;
color: #969799;
}
.draggable-item {
transition: all 0.3s ease;
}
.draggable-item:hover {
background-color: #f7f8fa;
}
</style>高度な使い方の例
FAQ 質問応答システム
<template>
<div class="faq-container">
<van-search
v-model="searchQuery"
placeholder="よくある質問を検索"
class="faq-search"
/>
<div v-for="category in faqCategories" :key="category.id" class="faq-category">
<h3 class="category-title">{{ category.name }}</h3>
<van-collapse v-model="activeQuestions[category.id]">
<van-collapse-item
v-for="faq in filteredFAQs(category.questions)"
:key="faq.id"
:name="faq.id"
class="faq-item"
>
<template #title>
<div class="faq-title">
<van-icon name="help-o" />
<span>{{ faq.question }}</span>
<van-tag v-if="faq.isHot" type="danger" size="mini">人気</van-tag>
</div>
</template>
<div class="faq-answer">
<div v-html="faq.answer"></div>
<div class="faq-actions">
<van-button
size="mini"
type="primary"
plain
@click="markHelpful(faq.id)"
>
役に立つ ({{ faq.helpfulCount }})
</van-button>
<van-button
size="mini"
plain
@click="contactSupport"
>
サポートに問い合わせる
</van-button>
</div>
</div>
</van-collapse-item>
</van-collapse>
</div>
</div>
</template>製品仕様表示
<template>
<div class="product-specs">
<van-collapse v-model="activeSpecs" accordion>
<van-collapse-item
v-for="spec in productSpecs"
:key="spec.category"
:name="spec.category"
class="spec-item"
>
<template #title>
<div class="spec-title">
<van-icon :name="spec.icon" />
<span>{{ spec.name }}</span>
<van-badge
v-if="spec.highlight"
content="新"
class="spec-badge"
/>
</div>
</template>
<div class="spec-content">
<div
v-for="detail in spec.details"
:key="detail.key"
class="spec-detail"
>
<span class="detail-label">{{ detail.label }}:</span>
<span class="detail-value">{{ detail.value }}</span>
</div>
<div v-if="spec.comparison" class="spec-comparison">
<h4>同種製品との比較</h4>
<van-grid :column-num="3" :border="false">
<van-grid-item
v-for="item in spec.comparison"
:key="item.name"
:text="item.name"
:badge="item.advantage ? '利点' : ''"
/>
</van-grid>
</div>
</div>
</van-collapse-item>
</van-collapse>
</div>
</template>関連コンポーネント
- Cell セル - 折りたたみパネルと組み合わせて使用できます
- List リスト - リストデータを表示
- Tabs タブ - 別のコンテンツ組織方法
- Popup ポップアップ - ポップアップ形式のコンテンツ表示
- ActionSheet アクションシート - 下部アクション選択