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 把一组紧密相关的按钮合并为一个控件——最常见的就是 分体式按钮 (split button):一个主操作,旁边是用于相关次要操作的下拉触发器。

要组合多个彼此独立的按钮或输入控件?请改用 Toolbar。工具栏 (例如格式栏或页面级的一组操作) 需要漫游焦点 (roaming focus) 的键盘语义,而 ButtonGroup 是有意不提供这种语义的。

安装

批量导入

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

细粒度导入

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

用法

ButtonGroup 是一个布局包裹层。子元素保留各自的 variant、size 和 shape ——分组只会把内侧圆角拉平并让边框重叠,使按钮共用一条接缝。请给分组一个描述操作的 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 参考

属性类型默认值说明
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").