Collapsible 折叠区域

由一个触发器控制展开与收起的区域。

转学第一天,我在天台遇见了那个抱着旧相机的少女。她说这台相机拍得到明天。

<script setup lang="ts">
  import {
    Button,
    Card,
    Collapsible,
    CollapsibleContent,
    CollapsibleTrigger,
    DisclosureIcon,
    Stack,
    Text,
  } from '@hina-ui/vue'
</script>

<template>
  <Card class="w-full max-w-md">
    <Collapsible>
      <Stack gap="sm" align="start">
        <Text tone="muted" size="sm">
          转学第一天,我在天台遇见了那个抱着旧相机的少女。她说这台相机拍得到明天。
        </Text>
        <CollapsibleTrigger as-child>
          <Button variant="link" size="sm">
            <template #trailing>
              <DisclosureIcon />
            </template>
            展开全部简介
          </Button>
        </CollapsibleTrigger>
        <CollapsibleContent>
          <Text tone="muted" size="sm">
            那之后的每一天,我们都在放学后爬上那道生锈的铁梯。她按下快门,我负责记录时间。直到某个傍晚,取景框里出现了不该出现的东西。
          </Text>
        </CollapsibleContent>
      </Stack>
    </Collapsible>
  </Card>
</template>
vue

用法

import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@hina-ui/vue'
ts

组件由三部分构成:Collapsible 持有开合状态,CollapsibleTrigger 是切换开合的控件,CollapsibleContent 是被折叠的内容。

触发器本身即为完整的控件,并自带随开合旋转的指示物。只需写入文字,无需组合按钮,也无需处理过渡。

<script setup lang="ts">
  import { Collapsible, CollapsibleContent, CollapsibleTrigger, Stack, Text } from '@hina-ui/vue'
</script>

<template>
  <Collapsible class="w-full max-w-md">
    <Stack gap="sm" align="start">
      <CollapsibleTrigger>阅读设置</CollapsibleTrigger>
      <CollapsibleContent>
        <Text tone="muted" size="sm">字号、行距、翻页方向与背景色都在这里调整。</Text>
      </CollapsibleContent>
    </Stack>
  </Collapsible>
</template>
vue

触发器的外观固定为一种克制的样式,因为它只需表达展开与收起两种状态。正文中的「展开全部」这类需要文字链外观的场合,通过 as-child 更换整个触发器,详见下文。

仅有一处内容需要折叠时使用该组件。同一组内多段内容互斥展开时,应使用 Accordion。

示例

受控

v-model:open 将开合状态交由外部持有,页面上的其他控件也可操作同一区域。无需外部控制时,用 default-open 指定初始状态即可。

open:false

<script setup lang="ts">
  import { ref } from 'vue'
  import {
    Button,
    Collapsible,
    CollapsibleContent,
    CollapsibleTrigger,
    Inline,
    Stack,
    Text,
  } from '@hina-ui/vue'

  const open = ref(false)
</script>

<template>
  <Stack class="w-full max-w-md">
    <Inline>
      <Button size="sm" variant="soft" tone="neutral" @click="open = !open">
        {{ open ? '收起' : '展开' }}
      </Button>
      <Text tone="muted" size="sm">open:{{ open }}</Text>
    </Inline>
    <Collapsible v-model:open="open">
      <Stack gap="sm" align="start">
        <CollapsibleTrigger>组件自己的触发器</CollapsibleTrigger>
        <CollapsibleContent>
          <Text tone="muted" size="sm">两处触发器操作的是同一个状态。</Text>
        </CollapsibleContent>
      </Stack>
    </Collapsible>
  </Stack>
</template>
vue

禁用

设置 disabled 后触发器不再响应点击,内容保持当前状态。

<script setup lang="ts">
  import { Collapsible, CollapsibleContent, CollapsibleTrigger, Stack, Text } from '@hina-ui/vue'
</script>

<template>
  <Collapsible disabled class="w-full max-w-md">
    <Stack gap="sm" align="start">
      <CollapsibleTrigger>编辑记录(需要登录)</CollapsibleTrigger>
      <CollapsibleContent>
        <Text tone="muted" size="sm">这段内容不会被展开。</Text>
      </CollapsibleContent>
    </Stack>
  </Collapsible>
</template>
vue

更换字形与自带触发器

icon 插槽仅替换指示物的字形,旋转仍由组件负责;将 icon 设为 false 则不显示指示物。

as-child 将行为借给唯一的子元素,触发器的外观与指示物随之交由调用方决定。文字链、描边、整宽带图标位等外观变化均通过该方式实现;如需箭头,自行放置一个 DisclosureIcon 即可,它仍能获取状态。本页顶部的示例采用的正是 variant="link" 的文字链外观。

<script setup lang="ts">
  import { Plus } from '@lucide/vue'
  import {
    Button,
    Collapsible,
    CollapsibleContent,
    CollapsibleTrigger,
    DisclosureIcon,
    Stack,
    Text,
  } from '@hina-ui/vue'
</script>

<template>
  <Stack class="w-full max-w-md">
    <Collapsible>
      <Stack gap="sm" align="start">
        <CollapsibleTrigger>
          更换字形
          <template #icon>
            <Plus />
          </template>
        </CollapsibleTrigger>
        <CollapsibleContent>
          <Text tone="muted" size="sm">加号旋转四分之一圈后成为叉号,旋转仍由组件负责。</Text>
        </CollapsibleContent>
      </Stack>
    </Collapsible>
    <Collapsible>
      <Stack gap="sm" align="start">
        <CollapsibleTrigger as-child>
          <Button variant="outline" tone="neutral" block class="justify-between">
            自行提供整个触发器
            <template #trailing>
              <DisclosureIcon />
            </template>
          </Button>
        </CollapsibleTrigger>
        <CollapsibleContent>
          <Text tone="muted" size="sm">
            此时外观与指示物均由调用方决定,放置一个 DisclosureIcon 即可。
          </Text>
        </CollapsibleContent>
      </Stack>
    </Collapsible>
  </Stack>
</template>
vue

组件不转发 variant 等外观属性:展开开关只需表达展开与收起,若引入按钮的全部外观维度,该 API 将逐渐演变为第二个 Button。需要更换外观时,应当整体替换触发器。

行为

  • 展开与收起时内容区的高度随之变化,两个方向使用同一组过渡参数。
  • 收起时内容从无障碍树中移除,键盘焦点不会进入其中。

API

Collapsible

属性
类型
默认值
说明
open
boolean
—
是否展开,支持 v-model:open
defaultOpen
boolean
false
初始是否展开
disabled
boolean
false
是否不可操作
class
string
—
追加至根元素的类名
事件
参数
说明
update:open
open: boolean
开合状态变化

CollapsibleTrigger

属性
类型
默认值
说明
icon
boolean
true
是否显示展开指示物
asChild
boolean
false
不渲染自带的按钮,把行为借给唯一子元素
class
string
—
追加至触发器的类名
插槽
说明
default
触发器的文字
icon
替换指示物的字形

CollapsibleContent

属性
类型
默认值
说明
class
string
—
追加至内容区的类名