展示类

折叠

行为组件,是一个用于展开或折叠内容的容器。

使用场景

  • Collapsible 是一个行为组件,默认开启动画效果。它被用于 Semi 的各种组件中,如:Navigation, Collapse, Tree, TreeSelect,以及 Typography 中。
  • 当上述组件不能满足需求或者需要自定义一些折叠行为时,可以使用 Collapsible 来包裹需要展开或者折叠的内容。

代码演示

如何引入

import { Collapsible } from '@fibrous/semi-ui-solid';

基本用法

通过 isOpen 来控制内容的展开或者折叠。

代码
import { createSignal } from 'solid-js';
import { Button, Collapsible } from '@fibrous/semi-ui-solid';

export default function CollapsibleDemo() {
    const [isOpen, setOpen] = createSignal(false);
    const toggle = () => {
        setOpen(!isOpen());
    };
    return (
        <div>
            <Button onClick={toggle} aria-expanded={isOpen()} aria-controls="collapsible-basic">
                Toggle
            </Button>
            <Collapsible isOpen={isOpen()} id="collapsible-basic">
                <ul>
                    <li>
                        <p>Semi Design 以内容优先进行设计。</p>
                    </li>
                    <li>
                        <p>更容易地自定义主题。</p>
                    </li>
                    <li>
                        <p>适用国际化场景。</p>
                    </li>
                    <li>
                        <p>效率场景加入人性化关怀。</p>
                    </li>
                </ul>
            </Collapsible>
        </div>
    );
}

自定义动画时间

通过 duration 设置动画展开或者折叠的时间,也可以通过 motion 来关闭动画。

设置动画时间:

代码
import { createSignal } from 'solid-js';
import { Button, Collapsible, InputNumber } from '@fibrous/semi-ui-solid';

export default function CollapsibleDemo() {
    const [isOpen, setOpen] = createSignal(false);
    const [duration, setDuration] = createSignal(250);
    const toggle = () => {
        setOpen(!isOpen());
    };
    return (
        <div>
            <span id="collapsible-duration-label">设置动画时间:</span>
            <InputNumber aria-labelledby="collapsible-duration-label" min={0} defaultValue={250} style={{ width: '120px' }} onChange={(val) => setDuration(Number(val))} step={10} />
            <br />
            <Button onClick={toggle} aria-expanded={isOpen()} aria-controls="collapsible-duration">
                Toggle
            </Button>
            <Collapsible isOpen={isOpen()} duration={duration()} id="collapsible-duration">
                <ul>
                    <li>
                        <p>Semi Design 以内容优先进行设计。</p>
                    </li>
                    <li>
                        <p>更容易地自定义主题。</p>
                    </li>
                    <li>
                        <p>适用国际化场景。</p>
                    </li>
                    <li>
                        <p>效率场景加入人性化关怀。</p>
                    </li>
                </ul>
            </Collapsible>
        </div>
    );
}

嵌套使用


代码
import { createSignal } from 'solid-js';
import { Button, Collapsible } from '@fibrous/semi-ui-solid';

export default function CollapsibleDemo() {
    const [isOpen, setOpen] = createSignal(false);
    const [isChildOpen, setChildOpen] = createSignal(false);

    return (
        <div>
            <Button onClick={() => setOpen(!isOpen())} aria-expanded={isOpen()} aria-controls="collapsible-nested">
                Toggle
            </Button>
            <br />
            <Collapsible isOpen={isOpen()} id="collapsible-nested">
                <div>
                    <span>Semi Design的设计原则包括:</span>
                    <Button onClick={() => setChildOpen(!isChildOpen())} aria-expanded={isChildOpen()} aria-controls="collapsible-nested-list">
                        Toggle List
                    </Button>
                </div>
                <Collapsible isOpen={isChildOpen()} id="collapsible-nested-list">
                    <ul>
                        <li>
                            <p>Semi Design 以内容优先进行设计。</p>
                        </li>
                        <li>
                            <p>更容易地自定义主题。</p>
                        </li>
                        <li>
                            <p>适用国际化场景。</p>
                        </li>
                        <li>
                            <p>效率场景加入人性化关怀。</p>
                        </li>
                    </ul>
                </Collapsible>
            </Collapsible>
        </div>
    );
}

自定义折叠高度

可以使用 collapseHeight 自定义收起的高度

  • Semi Design 以内容优先进行设计。

  • 更容易地自定义主题。

  • 适用国际化场景。

  • 效率场景加入人性化关怀。

代码
import { createSignal, type JSX, Show } from 'solid-js';
import { Button, Collapsible } from '@fibrous/semi-ui-solid';

const linkStyle: JSX.CSSProperties = {
    position: 'absolute',
    left: '0',
    right: '0',
    width: '100%',
    padding: '0',
    border: 'none',
    background: 'none',
    'font-size': 'inherit',
    'font-family': 'inherit',
    'line-height': 'inherit',
    'text-align': 'center',
    bottom: '-10px',
    'font-weight': '700',
    cursor: 'pointer',
    color: 'var(--semi-color-link)',
};

export default function CollapsibleDemo() {
    const [isOpen, setOpen] = createSignal(false);
    const toggle = () => {
        setOpen(!isOpen());
    };
    return (
        <>
            <Button onClick={toggle} aria-expanded={isOpen()} aria-controls="collapsible-height">
                Toggle
            </Button>
            <div style={{ position: 'relative' }}>
                <Collapsible
                    isOpen={isOpen()}
                    collapseHeight={60}
                    style={
                        isOpen()
                            ? {}
                            : {
                                  '-webkit-mask-image': 'linear-gradient(to bottom, black 0%, rgba(0, 0, 0, 1) 60%, rgba(0, 0, 0, 0.2) 80%, transparent 100%)',
                              }
                    }
                    id="collapsible-height"
                >
                    <ul>
                        <li>
                            <p>Semi Design 以内容优先进行设计。</p>
                        </li>
                        <li>
                            <p>更容易地自定义主题。</p>
                        </li>
                        <li>
                            <p>适用国际化场景。</p>
                        </li>
                        <li>
                            <p>效率场景加入人性化关怀。</p>
                        </li>
                    </ul>
                </Collapsible>
                <Show when={!isOpen()}>
                    <button type="button" onClick={toggle} aria-expanded={isOpen()} aria-controls="collapsible-height" style={linkStyle}>
                        + Show More
                    </button>
                </Show>
            </div>
        </>
    );
}

API 参考

Props

属性说明类型默认值
children需要展开或收起的内容。JSX.Element-
class外层 wrapper 的类名。string-
collapseHeight收起时的高度,单位为像素。number0
collapseHeightAdaptive内容高度小于 collapseHeight 时,收起高度是否取内容高度。booleanfalse
duration动画执行的时间,单位为毫秒。number250
fade是否开启淡入淡出。booleanfalse
id内层内容元素的 id,可配合触发器的 aria-controls 使用;不读全局默认值。string-
isOpen是否展开内容区域。booleanfalse
keepDOM是否保留隐藏的内容 DOM 树,默认销毁。booleanfalse
lazyRender配合 keepDOM 使用,为 true 时挂载时不渲染内容,首次展开后才渲染。booleanfalse
motion是否开启动画。booleantrue
onMotionEnd展开或收起动画结束时的回调。Type() => void-

class、style、ref 与其他原生属性和事件落在外层 wrapper 上;id 是组件声明的 prop,落在内层内容元素上。使用方的 onTransitionEnd 与组件自身的处理同时触发。

Accessibility

ARIA

  • Collapsible 具有 id props,传入的值会被设置为内层内容元素的 id(不是外层 wrapper),触发按钮用 aria-controls 指向它,并用 aria-expanded 表示当前是否展开,见下方使用示例。
import { createSignal } from 'solid-js';
import { Button, Collapsible } from '@fibrous/semi-ui-solid';

() => {
    const collapseId = 'myCollapsible';
    const [visible, setVisible] = createSignal(false);
    return (
        <>
            <Button onClick={() => setVisible(!visible())} aria-expanded={visible()} aria-controls={collapseId}>
                {visible() ? 'hide' : 'show'}
            </Button>
            <Collapsible isOpen={visible()} id={collapseId}>
                <div>hide content</div>
            </Collapsible>
        </>
    );
};

焦点

  • collapseHeight 为 0 时,收起的内容设为 inert,读屏读不到,Tab 也到不了。
  • collapseHeight 大于 0 时,收起后露出的预览区与被裁掉的部分是同一个元素,不能只隐藏被裁掉的部分,所以整段内容保持可读、可聚焦,读屏用户能读到全文。被裁掉的部分里不要放链接、按钮等可聚焦元素,否则键盘焦点会落在看不见的位置。

FAQ

  • 为什么使用 Collapsible 没有正常展开?
    Collapsible 的展开高度由 CSS 计算,不依赖测量节点高度,父级为 display: none 时切换 isOpen 也能得到正确的展开高度。请先检查 isOpen 是否随状态更新;collapseHeight 大于 0 时,收起状态本来就会露出一部分内容。