Button Group
@cloudflare/kumo
import { Button, ButtonGroup, DropdownMenu } from "@cloudflare/kumo";
import { CaretDownIcon } from "@phosphor-icons/react";

/**
 * Split button: a primary action joined with a dropdown trigger for related
 * secondary actions. The caret button uses `shape="square"` and an
 * `aria-label`.
 */
export function ButtonGroupSplitDemo() {
  return (
    <ButtonGroup aria-label="Deploy">
      <Button variant="primary">Deploy</Button>
      <DropdownMenu>
        <DropdownMenu.Trigger
          render={
            <Button
              variant="primary"
              shape="square"
              aria-label="More deploy options"
            >
              <CaretDownIcon />
            </Button>
          }
        />
        <DropdownMenu.Content>
          <DropdownMenu.Item>Deploy to staging</DropdownMenu.Item>
          <DropdownMenu.Item>Deploy and tail logs</DropdownMenu.Item>
          <DropdownMenu.Item>Schedule deploy…</DropdownMenu.Item>
        </DropdownMenu.Content>
      </DropdownMenu>
    </ButtonGroup>
  );
}

适用场景

ButtonGroup 将一小组紧密关联的按钮合并为一个控件——最常见的形式是拆分按钮:一个主操作按钮旁边配一个用于相关次要操作的下拉触发器。

打算把多个相互独立的按钮或输入框组合在一起?请改用 Toolbar。工具栏(例如格式栏或页面级的一组操作)需要可漫游焦点的键盘语义,而 ButtonGroup 有意不提供这一语义。

安装

批量导入

import { ButtonGroup } from "@cloudflare/kumo";

按需导入

import { ButtonGroup } from "@cloudflare/kumo/components/button-group";

用法

ButtonGroup 是一个布局包装器。其子元素各自保留自己的 variantsizeshape——分组只负责抹平内角并重叠边框,使按钮们共享一条接缝。请为分组提供用于描述该操作的 aria-label,并为下拉触发器单独提供 aria-label

import { Button, ButtonGroup, DropdownMenu } from "@cloudflare/kumo";
import { CaretDownIcon } from "@phosphor-icons/react";

export default function Example() {
return (
  <ButtonGroup aria-label="Deploy">
    <Button variant="primary">Deploy</Button>
    <DropdownMenu>
      <DropdownMenu.Trigger
        render={
          <Button
            variant="primary"
            shape="square"
            aria-label="More deploy options"
          >
            <CaretDownIcon />
          </Button>
        }
      />
      <DropdownMenu.Content>
        <DropdownMenu.Item>Deploy to staging</DropdownMenu.Item>
      </DropdownMenu.Content>
    </DropdownMenu>
  </ButtonGroup>
);
}

示例

拆分按钮

主操作与用于次要操作的下拉触发器组合在一起。

import { Button, ButtonGroup, DropdownMenu } from "@cloudflare/kumo";
import { CaretDownIcon } from "@phosphor-icons/react";

/**
 * Split button: a primary action joined with a dropdown trigger for related
 * secondary actions. The caret button uses `shape="square"` and an
 * `aria-label`.
 */
export function ButtonGroupSplitDemo() {
  return (
    <ButtonGroup aria-label="Deploy">
      <Button variant="primary">Deploy</Button>
      <DropdownMenu>
        <DropdownMenu.Trigger
          render={
            <Button
              variant="primary"
              shape="square"
              aria-label="More deploy options"
            >
              <CaretDownIcon />
            </Button>
          }
        />
        <DropdownMenu.Content>
          <DropdownMenu.Item>Deploy to staging</DropdownMenu.Item>
          <DropdownMenu.Item>Deploy and tail logs</DropdownMenu.Item>
          <DropdownMenu.Item>Schedule deploy…</DropdownMenu.Item>
        </DropdownMenu.Content>
      </DropdownMenu>
    </ButtonGroup>
  );
}

次要

拆分按钮适用于任意按钮变体。

import { Button, ButtonGroup, DropdownMenu } from "@cloudflare/kumo";
import { CaretDownIcon } from "@phosphor-icons/react";

/**
 * Split buttons work with any button variant — here the secondary style for a
 * lower-emphasis action.
 */
export function ButtonGroupSecondaryDemo() {
  return (
    <ButtonGroup aria-label="Save">
      <Button variant="secondary">Save</Button>
      <DropdownMenu>
        <DropdownMenu.Trigger
          render={
            <Button
              variant="secondary"
              shape="square"
              aria-label="More save options"
            >
              <CaretDownIcon />
            </Button>
          }
        />
        <DropdownMenu.Content>
          <DropdownMenu.Item>Save as draft</DropdownMenu.Item>
          <DropdownMenu.Item>Save and publish</DropdownMenu.Item>
          <DropdownMenu.Item>Save a copy…</DropdownMenu.Item>
        </DropdownMenu.Content>
      </DropdownMenu>
    </ButtonGroup>
  );
}

尺寸

在两个按钮上使用一致的 size,以保持拆分按钮对齐。

import { Button, ButtonGroup, DropdownMenu } from "@cloudflare/kumo";
import { CaretDownIcon } from "@phosphor-icons/react";

/**
 * Match the `size` on both buttons to keep the split button aligned.
 */
export function ButtonGroupSizesDemo() {
  const sizes = ["sm", "base", "lg"] as const;
  return (
    <div className="flex flex-wrap items-center gap-4">
      {sizes.map((size) => (
        <ButtonGroup key={size} aria-label="Deploy">
          <Button size={size} variant="primary">
            Deploy
          </Button>
          <DropdownMenu>
            <DropdownMenu.Trigger
              render={
                <Button
                  size={size}
                  variant="primary"
                  shape="square"
                  aria-label="More deploy options"
                >
                  <CaretDownIcon />
                </Button>
              }
            />
            <DropdownMenu.Content>
              <DropdownMenu.Item>Deploy to staging</DropdownMenu.Item>
              <DropdownMenu.Item>Schedule deploy…</DropdownMenu.Item>
            </DropdownMenu.Content>
          </DropdownMenu>
        </ButtonGroup>
      ))}
    </div>
  );
}

API 参考

PropTypeDefaultDescription
classNamestring-Additional CSS classes merged via `cn()`. Use kumo semantic tokens only.
idstring--
langstring--
titlestring--
childrenReactNode-The tightly-coupled controls to join. Typically two `Button`s: a primary action and a dropdown trigger (a "split button").