Collapsible 折叠区域

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

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

import {
  Button,
  Card,
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
  DisclosureIcon,
  Stack,
  Text,
} from '@hina-ui/react'

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

用法

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

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

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

import { Collapsible, CollapsibleContent, CollapsibleTrigger, Stack, Text } from '@hina-ui/react'

export default function Demo() {
  return (
    <Collapsible className="w-full max-w-md">
      <Stack gap="sm" align="start">
        <CollapsibleTrigger>阅读设置</CollapsibleTrigger>
        <CollapsibleContent>
          <Text tone="muted" size="sm">
            字号、行距、翻页方向与背景色都在这里调整。
          </Text>
        </CollapsibleContent>
      </Stack>
    </Collapsible>
  )
}
tsx

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

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

示例

受控

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

open:false

'use client'

import { useState } from 'react'
import {
  Button,
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
  Inline,
  Stack,
  Text,
} from '@hina-ui/react'

export default function Demo() {
  const [open, setOpen] = useState(false)

  return (
    <Stack className="w-full max-w-md">
      <Inline>
        <Button size="sm" variant="soft" tone="neutral" onClick={() => setOpen(!open)}>
          {open ? '收起' : '展开'}
        </Button>
        <Text tone="muted" size="sm">
          open:{String(open)}
        </Text>
      </Inline>
      <Collapsible open={open} onOpenChange={setOpen}>
        <Stack gap="sm" align="start">
          <CollapsibleTrigger>组件自己的触发器</CollapsibleTrigger>
          <CollapsibleContent>
            <Text tone="muted" size="sm">
              两处触发器操作的是同一个状态。
            </Text>
          </CollapsibleContent>
        </Stack>
      </Collapsible>
    </Stack>
  )
}
tsx

禁用

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

import { Collapsible, CollapsibleContent, CollapsibleTrigger, Stack, Text } from '@hina-ui/react'

export default function Demo() {
  return (
    <Collapsible disabled className="w-full max-w-md">
      <Stack gap="sm" align="start">
        <CollapsibleTrigger>编辑记录(需要登录)</CollapsibleTrigger>
        <CollapsibleContent>
          <Text tone="muted" size="sm">
            这段内容不会被展开。
          </Text>
        </CollapsibleContent>
      </Stack>
    </Collapsible>
  )
}
tsx

更换字形与自带触发器

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

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

import { Plus } from 'lucide-react'
import {
  Button,
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
  DisclosureIcon,
  Stack,
  Text,
} from '@hina-ui/react'

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

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

行为

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

API

Collapsible

属性
类型
默认值
说明
open
boolean
—
是否展开,可受控
defaultOpen
boolean
false
初始是否展开
disabled
boolean
false
是否不可操作
className
string
—
追加至根元素的类名
回调
参数
说明
onOpenChange
open: boolean
开合状态变化

CollapsibleTrigger

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

CollapsibleContent

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