# Red Hat PatternFly 设计系统

## Red Hat PatternFly 设计系统界面预览

搜索服务库，选择单个服务或所有可见服务，查看副本总数，检查选择内容，并确认本地计划。

## Red Hat PatternFly 设计系统设计参考

PatternFly React 6.6.1 和 @patternfly/react-tokens 6.6.1。下方展示了选定的已发布浅色主题令牌解析值。原生组件和 pf-v6-theme-dark 会解析深色样式；示例服务数据和布局则由应用自行设定。

### 语义化界面表面与文本角色

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| --pf-t--global--background--color--primary--default | rgb(255, 255, 255, 0.6000) | 在当前主题中使用相应的语义角色；品牌强调红色并非通用的主要操作颜色。 |
| --pf-t--global--background--color--secondary--default | rgb(199, 199, 199, 0.3000) | 在当前主题中使用相应的语义角色；品牌强调红色并非通用的主要操作颜色。 |
| --pf-t--global--text--color--regular | #151515 | 在当前主题中使用相应的语义角色；品牌强调红色并非通用的主要操作颜色。 |
| --pf-t--global--text--color--subtle | #383838 | 在当前主题中使用相应的语义角色；品牌强调红色并非通用的主要操作颜色。 |
| --pf-t--global--text--color--disabled | #a3a3a3 | 在当前主题中使用相应的语义角色；品牌强调红色并非通用的主要操作颜色。 |
| --pf-t--global--color--brand--default | #004d99 | 在当前主题中使用相应的语义角色；品牌强调红色并非通用的主要操作颜色。 |
| --pf-t--global--color--brand--hover | #003366 | 在当前主题中使用相应的语义角色；品牌强调红色并非通用的主要操作颜色。 |
| --pf-t--global--color--brand--accent--default | #ee0000 | 在当前主题中使用相应的语义角色；品牌强调红色并非通用的主要操作颜色。 |
| --pf-t--global--color--brand--subtle--default | #e0f0ff | 在当前主题中使用相应的语义角色；品牌强调红色并非通用的主要操作颜色。 |
### 状态角色

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| --pf-t--global--color--status--success--default | #204d00 | 将状态意图与文字标签搭配使用。这些浅色主题数值不能替代原生 Label 或 Alert 变体。 |
| --pf-t--global--color--status--warning--default | #73480b | 将状态意图与文字标签搭配使用。这些浅色主题数值不能替代原生 Label 或 Alert 变体。 |
| --pf-t--global--color--status--danger--default | #731f00 | 将状态意图与文字标签搭配使用。这些浅色主题数值不能替代原生 Label 或 Alert 变体。 |
| --pf-t--global--color--status--info--default | #3d2785 | 将状态意图与文字标签搭配使用。这些浅色主题数值不能替代原生 Label 或 Alert 变体。 |
| --pf-t--global--color--status--custom--default | #004d4d | 将状态意图与文字标签搭配使用。这些浅色主题数值不能替代原生 Label 或 Alert 变体。 |
| --pf-t--global--color--status--unread--default | #0066cc | 将状态意图与文字标签搭配使用。这些浅色主题数值不能替代原生 Label 或 Alert 变体。 |
### 正文与标题字体

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| --pf-t--global--font--size--body--default | 0.875rem | 有意识地使用字体排印角色和语义标题级别。视觉字号并不决定文档大纲。 |
| --pf-t--global--font--size--body--lg | 1rem | 有意识地使用字体排印角色和语义标题级别。视觉字号并不决定文档大纲。 |
| --pf-t--global--font--size--heading--h1 | 1.5rem | 有意识地使用字体排印角色和语义标题级别。视觉字号并不决定文档大纲。 |
| --pf-t--global--font--size--heading--h2 | 1.25rem | 有意识地使用字体排印角色和语义标题级别。视觉字号并不决定文档大纲。 |
| --pf-t--global--font--size--heading--h3 | 1.125rem | 有意识地使用字体排印角色和语义标题级别。视觉字号并不决定文档大纲。 |
| --pf-t--global--font--size--heading--h4 | 1rem | 有意识地使用字体排印角色和语义标题级别。视觉字号并不决定文档大纲。 |
| --pf-t--global--font--size--heading--h5 | 1rem | 有意识地使用字体排印角色和语义标题级别。视觉字号并不决定文档大纲。 |
| --pf-t--global--font--size--heading--h6 | 1rem | 有意识地使用字体排印角色和语义标题级别。视觉字号并不决定文档大纲。 |
### 间距比例

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| --pf-t--global--spacer--xs | 0.25rem | 使用指定的间距比例设置分组和控件之间的间距；rem 值会随用户根字号变化。 |
| --pf-t--global--spacer--sm | 0.5rem | 使用指定的间距比例设置分组和控件之间的间距；rem 值会随用户根字号变化。 |
| --pf-t--global--spacer--md | 1rem | 使用指定的间距比例设置分组和控件之间的间距；rem 值会随用户根字号变化。 |
| --pf-t--global--spacer--lg | 1.5rem | 使用指定的间距比例设置分组和控件之间的间距；rem 值会随用户根字号变化。 |
| --pf-t--global--spacer--xl | 2rem | 使用指定的间距比例设置分组和控件之间的间距；rem 值会随用户根字号变化。 |
| --pf-t--global--spacer--2xl | 3rem | 使用指定的间距比例设置分组和控件之间的间距；rem 值会随用户根字号变化。 |
| --pf-t--global--spacer--3xl | 4rem | 使用指定的间距比例设置分组和控件之间的间距；rem 值会随用户根字号变化。 |
| --pf-t--global--spacer--4xl | 5rem | 使用指定的间距比例设置分组和控件之间的间距；rem 值会随用户根字号变化。 |
### 形状与焦点

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| --pf-t--global--border--radius--tiny | 4px | 根据具体组件选择合适的形状，不要将胶囊形圆角应用到所有界面表面。 |
| --pf-t--global--border--radius--small | 6px | 根据具体组件选择合适的形状，不要将胶囊形圆角应用到所有界面表面。 |
| --pf-t--global--border--radius--medium | 16px | 根据具体组件选择合适的形状，不要将胶囊形圆角应用到所有界面表面。 |
| --pf-t--global--border--radius--large | 24px | 根据具体组件选择合适的形状，不要将胶囊形圆角应用到所有界面表面。 |
| --pf-t--global--border--radius--pill | 999px | 根据具体组件选择合适的形状，不要将胶囊形圆角应用到所有界面表面。 |
| --pf-t--global--border--radius--control--form-element | 6px | 根据具体组件选择合适的形状，不要将胶囊形圆角应用到所有界面表面。 |
| --pf-t--global--border--radius--action--default | 999px | 根据具体组件选择合适的形状，不要将胶囊形圆角应用到所有界面表面。 |
## Red Hat PatternFly 设计系统完整指南

实用指南，介绍如何为真实界面选择、应用和评审此系统。

完整指导、实现起点和源代码链接。

## Red Hat PatternFly 设计系统运营布局

### 带有持久运营上下文的集合

> 服务库让运营人员可以比较服务的运行状况、位置和利用率，同时由独立的范围面板记录所选服务。所选内容并不等同于当前搜索结果。即使某项服务因筛选而隐藏，它仍会保持选中，直到有人主动将其移除。对于影响范围超出当前可见界面的大批量操作，这一区别至关重要。

### 使用原生组件层级

Gallery 负责响应式卡片列布局，Card 用于区分各项服务，Label 用于标识状态，Progress 则通过标题展示示例百分比。此页面不会用随意的彩色矩形模仿基础设施控制台。完整的 React 示例实现了相应的数据模型、控件和审核状态。

## Red Hat PatternFly 设计系统语义颜色与主题角色

### 主要品牌色与红色强调色彼此不同

PatternFly v6 导出了彼此独立的品牌、强调、文本、背景和状态角色。所选的浅色主题快照包含默认蓝色品牌色和默认红色品牌强调色。如果将所有醒目的操作都视为红帽红色，就会混淆这些角色。原生 Button 和 Label 组件会根据各自变体使用适当的系统角色。

### 在文档级别解析深色主题

此示例在 document 元素上切换 pf-v6-theme-dark。应用界面表面和文本使用 --pf-t--global 语义变量，因此主题也能正确解析它们的颜色。不要在自定义卡片中固定使用浅色值，再指望 document 主题类来修正颜色。

## Red Hat PatternFly 设计系统字体与服务元数据

### 将视觉尺寸与标题语义分开

Title 的 headingLevel h1 用于确立页面标题，而 size 控制视觉强调程度。在此软件包快照中，Body-default 为 0.875rem。卡片标签、状态和利用率数值都应保持易读，但不必把每个值都设为标题。元数据定义列表会明确列出副本数量和服务 ID。

### 避免使用含义模糊的操作标签

组件和页脚都将示例利用率标记为示例数据。单独的进度条容易被误认为部署任务或加载操作。说明文字会解释百分比的含义，状态 Label 则单独说明容量是“健康”还是“需检查”。

## Red Hat PatternFly 设计系统图库与批量选择

### 使用复选框的三种状态

所有可见服务均已选中时，“选择可见项”控件的值为 true；未选中任何服务时，值为 false；仅选中部分服务时，值为 null。PatternFly Checkbox 支持表示不确定状态的 null 值。应用会根据经过筛选的集合计算此状态，而不是使用可能与各行选择状态不一致的独立布尔值。

1. 选择一个服务，查看部分选中状态。

2. 选择当前所有可见服务。

3. 搜索一个名称，确认隐藏的已选项仍保留在范围内。

4. 检查确切的选择集和副本总数。

5. 确认本地计划并查看结果。

### 以选择集作为唯一数据源

每项服务都有稳定的 ID。单个复选框和“选择可见项”控件都会更新同一组 ID；范围面板则根据这组 ID 推导服务名称和副本总数。搜索只会改变可见内容。这样一来，即使操作人员在选择多项服务后缩小图库的显示范围，审核摘要仍能保持一致。

## Red Hat PatternFly 设计系统间距与响应式卡片

### 使用语义令牌构建页面外壳

示例中的背景、常规文本和次要元数据使用已发布令牌包中的语义 CSS 属性。原生间距比例从 xs 延伸至 4xl，而此应用在集合与范围面板之间使用 24px 的间距。这个明确指定的组合间距值与令牌引用不同。

### 优先调整图库布局，而不是压缩内容

图库项的最小宽度为 230px，并设有间距。在应用的 850px 断点处，范围面板会移至集合下方。在 500px 处，页面内边距调整为 16px，搜索框占满工具栏宽度。这些是本示例的布局决策，并不意味着所有 PatternFly 产品都使用这些断点。

## Red Hat PatternFly 设计系统审核与选择状态

### 选择操作不应直接执行任务

勾选服务只会将其添加到范围中。点击“审核选择”会显示信息提示 Alert 和确认操作。“确认本地计划”会报告所选服务和副本总数，但不会启动维护任务。更改选择时会关闭审核状态，避免意外确认已过时的选择集。

### 将搜索恢复与选择状态分开处理

未匹配到结果时，会显示一条内联信息提示 Alert，其中包含“清除搜索”操作。此操作只会清除查询，不会更改已选服务。因此，工具栏中的选中数量和范围面板仍保持一致。如果搜索结果为空就顺带重置选择，这会构成一种隐蔽的破坏性行为。

## Red Hat PatternFly 设计系统无障碍控件与状态

### 为选择控件添加标签并使用原生控件

每个 Checkbox 都有稳定的 id 和可见的服务标签。SearchInput 具有无障碍名称，Switch 用于标识外观设置；选择集为空时，“审核选择”处于禁用状态。原生 Progress 会传达其数值和标题。页面不依赖卡片中仅悬停时才出现的操作。

### 用文字明确呈现状态

选择数量使用 polite 实时区域；服务状态以文字显示在 Label 中；最终 Alert 会说明发生了什么。不要仅用颜色表示选择范围或健康状态。键盘测试应涵盖部分选择、筛选掉已选服务，以及返回完整集合等情况。

- 已选中的复选框表示纳入选择，而不是成功状态。

- 状态 Label 描述的是服务状态，而非服务是否已选中。

- Progress 表示示例利用率，而非操作完成进度。

## Red Hat PatternFly 设计系统 React 集成

### 仅在入口处导入一次重置样式和组件样式

此示例在入口文件中导入 @patternfly/react-core/dist/styles/base.css。原生 React 组件通过软件包提供自身的组件样式。应用的 CSS 作用域限定为 pf-example，并引用当前的 --pf-t--global 命名空间；它不会覆盖组件内部样式。

### 仅通过服务契约添加真正的调度功能

本地计划有意设计为预览。生产环境实现需要处理授权、维护时段、受影响的依赖项、待处理反馈和服务器端验证。在请求期间保持所选 ID 稳定，并根据实际服务响应协调确认结果。原生展示组件不提供操作后端。

## Red Hat PatternFly 设计系统组件示例

### main.jsx — 服务图库与批量审核

完整的原生 Gallery/Card/Checkbox/Progress 组合，包含派生的选择范围和本地确认。

```
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import {
  Alert,
  Button,
  Card,
  CardBody,
  CardTitle,
  Checkbox,
  Gallery,
  GalleryItem,
  Label,
  Progress,
  SearchInput,
  Switch,
  Title,
} from "@patternfly/react-core";
import "@patternfly/react-core/dist/styles/base.css";
import "./patternfly.css";
const services = [
  {
    id: "api",
    name: "Public API",
    region: "eu-west",
    replicas: 6,
    load: 62,
    status: "Healthy",
  },
  {
    id: "jobs",
    name: "Background jobs",
    region: "eu-west",
    replicas: 3,
    load: 81,
    status: "Review capacity",
  },
  {
    id: "search",
    name: "Search index",
    region: "us-east",
    replicas: 4,
    load: 44,
    status: "Healthy",
  },
  {
    id: "events",
    name: "Event stream",
    region: "us-east",
    replicas: 8,
    load: 72,
    status: "Healthy",
  },
];
function App() {
  const [query, setQuery] = useState(""),
    [selected, setSelected] = useState([]),
    [review, setReview] = useState(false),
    [notice, setNotice] = useState(""),
    [dark, setDark] = useState(false);
  const visible = services.filter((x) =>
    x.name.toLowerCase().includes(query.toLowerCase()),
  );
  const picked = services.filter((x) => selected.includes(x.id));
  const allVisible =
    visible.length > 0 && visible.every((x) => selected.includes(x.id));
  const someVisible = visible.some((x) => selected.includes(x.id));
  function choose(id, checked) {
    setSelected((v) =>
      checked ? [...new Set([...v, id])] : v.filter((x) => x !== id),
    );
    setReview(false);
  }
  function selectVisible(checked) {
    setSelected((v) =>
      checked
        ? [...new Set([...v, ...visible.map((x) => x.id)])]
        : v.filter((id) => !visible.some((x) => x.id === id)),
    );
    setReview(false);
  }
  return (
    <main className="pf-example">
      <header>
        <div>
          <span className="overline">PATTERNFLY · SERVICE OPERATIONS</span>
          <Title headingLevel="h1" size="2xl">
            Plan a maintenance review
          </Title>
          <p>
            Select services and inspect the combined scope before creating a
            local plan.
          </p>
        </div>
        <Switch
          id="dark-theme"
          label="Dark theme"
          isChecked={dark}
          onChange={(_, checked) => {
            setDark(checked);
            document.documentElement.classList.toggle(
              "pf-v6-theme-dark",
              checked,
            );
          }}
        />
      </header>
      <div className="service-toolbar">
        <SearchInput
          aria-label="Find a service"
          placeholder="Find a service"
          value={query}
          onChange={(_, value) => setQuery(value)}
          onClear={() => setQuery("")}
        />
        <Checkbox
          id="select-visible"
          label="Select visible"
          isChecked={allVisible ? true : someVisible ? null : false}
          isDisabled={!visible.length}
          onChange={(_, checked) => selectVisible(checked)}
        />
        <span aria-live="polite">{selected.length} selected</span>
        <Button isDisabled={!selected.length} onClick={() => setReview(true)}>
          Review selection
        </Button>
      </div>
      <div className="service-layout">
        <Gallery hasGutter minWidths={{ default: "230px" }}>
          {visible.map((s) => (
            <GalleryItem key={s.id}>
              <Card>
                <CardTitle>
                  <Checkbox
                    id={"select-" + s.id}
                    label={s.name}
                    isChecked={selected.includes(s.id)}
                    onChange={(_, checked) => choose(s.id, checked)}
                  />
                </CardTitle>
                <CardBody>
                  <div className="service-meta">
                    <Label color={s.status === "Healthy" ? "green" : "orange"}>
                      {s.status}
                    </Label>
                    <span>{s.region}</span>
                  </div>
                  <Progress
                    value={s.load}
                    title="Sample utilization"
                    measureLocation="outside"
                  />
                  <dl>
                    <dt>Replicas</dt>
                    <dd>{s.replicas}</dd>
                    <dt>Service ID</dt>
                    <dd>
                      <code>{s.id}</code>
                    </dd>
                  </dl>
                </CardBody>
              </Card>
            </GalleryItem>
          ))}
        </Gallery>
        <aside>
          <Card>
            <CardTitle>Maintenance scope</CardTitle>
            <CardBody>
              <p>
                {picked.length} services ·{" "}
                {picked.reduce((n, s) => n + s.replicas, 0)} replicas
              </p>
              {picked.length ? (
                <ul>
                  {picked.map((s) => (
                    <li key={s.id}>
                      {s.name}
                      <span>{s.region}</span>
                    </li>
                  ))}
                </ul>
              ) : (
                <p className="muted">Select a service to inspect its scope.</p>
              )}
              {review && (
                <div className="review">
                  <Alert
                    isInline
                    variant="info"
                    title="Review the selected services"
                  >
                    This is a fictional plan. No workload will be restarted.
                  </Alert>
                  <Button
                    onClick={() => {
                      setNotice(
                        `Local plan includes ${picked.length} services and ${picked.reduce((n, s) => n + s.replicas, 0)} replicas. No operation was scheduled.`,
                      );
                      setReview(false);
                    }}
                  >
                    Confirm local plan
                  </Button>
                </div>
              )}
            </CardBody>
          </Card>
        </aside>
      </div>
      {!visible.length && (
        <Alert isInline variant="info" title="No services match">
          <Button variant="link" onClick={() => setQuery("")}>
            Clear search
          </Button>
        </Alert>
      )}
      {notice && (
        <Alert isInline variant="success" title="Local plan recorded">
          {notice}
        </Alert>
      )}
      <footer>
        Native PatternFly 6.6.1 · service data and capacity figures are
        fictional, memory-only examples.
      </footer>
    </main>
  );
}
createRoot(document.getElementById("root")).render(<App />);
```

代码参考

- 保存为 src/main.jsx。Checkbox onChange 会接收事件和选中值；集合处于混合选择状态时，isChecked 可以为 null。

### patternfly.css — 响应式操作界面外壳

自定义页面布局使用语义化的 v6 令牌，同时保留原生组件样式。

```
body {
  margin: 0;
}
.pf-example {
  padding: 24px;
  background: var(--pf-t--global--background--color--secondary--default);
  color: var(--pf-t--global--text--color--regular);
  font-family: var(--pf-t--global--font--family--body);
}
.pf-example header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 24px;
  margin-bottom: 24px;
}
.overline {
  font-size: 11px;
  letter-spacing: 0.07em;
  color: var(--pf-t--global--text--color--subtle);
}
.pf-example header h1 {
  margin: 8px 0;
}
.service-toolbar {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}
.service-toolbar > .pf-v6-c-search-input {
  width: 280px;
}
.service-toolbar > span {
  margin-left: auto;
}
.service-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  gap: 24px;
  align-items: start;
}
.service-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 12px;
  margin-bottom: 24px;
}
.service-layout dl {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 24px;
  font-size: 12px;
}
.service-layout dd {
  margin: 0;
}
.service-layout ul {
  padding: 0;
  list-style: none;
  margin: 16px 0;
}
.service-layout li {
  padding: 12px 0;
  border-bottom: 1px solid var(--pf-t--global--border--color--default);
}
.service-layout li span {
  display: block;
  font-size: 12px;
  color: var(--pf-t--global--text--color--subtle);
}
.muted {
  color: var(--pf-t--global--text--color--subtle);
  margin-top: 16px;
}
.review > .pf-v6-c-button {
  margin-top: 16px;
}
.pf-example > .pf-v6-c-alert {
  margin-top: 24px;
}
.pf-example footer {
  font-size: 12px;
  color: var(--pf-t--global--text--color--subtle);
  margin-top: 24px;
}
@media (max-width: 850px) {
  .service-layout {
    grid-template-columns: 1fr;
  }
  .pf-example header {
    align-items: start;
  }
  .service-toolbar > span {
    margin-left: 0;
  }
}
@media (max-width: 500px) {
  .pf-example {
    padding: 16px;
  }
  .pf-example header {
    flex-direction: column;
  }
  .service-toolbar {
    gap: 16px;
  }
  .service-toolbar > .pf-v6-c-search-input {
    width: 100%;
  }
}
```

代码参考

- 保存为 src/patternfly.css。应用断点是本示例明确指定的一部分。

## Red Hat PatternFly 设计系统 Agent 设置

### 实现起点

原生 React 18.3.1 / PatternFly 6.6.1 组合。Vite 需要受支持的 Node.js 版本。实际服务数据和操作 API 仍由应用负责。

```
npm create vite@latest patternfly-services -- --template react
cd patternfly-services
npm install
npm install react@18.3.1 react-dom@18.3.1 @patternfly/react-core@6.6.1
# Replace src/main.jsx and add src/patternfly.css using the complete examples.
npm run dev
```

### 适用于你项目的提示词

```
使用 PatternFly 6.6.1 原生 Gallery、Card、Checkbox、Progress、Label 和 Alert，构建运维集合。使用提供的语义令牌和完整示例。将筛选后的可见项与已选 ID 分开管理，计算批量选择的部分选中状态，并要求审核确切的受影响对象集合。添加真实的 API 后，应如实反馈待处理、失败或成功状态。确保移动端布局能够重排，并支持键盘操作。
```

## Red Hat PatternFly 设计系统的来源与范围

这是由 OpenDesign 编写的应用指南，附带虚构的维护计划示例，使用原生 PatternFly React 6.6.1 运行。利用率、区域和副本数量均为示例数据。确认操作只会更新本地状态；不会重启服务或安排计划。

- 使用 PatternFly 开发 原生包与样式表集成。

- 已发布的令牌模块 带版本的令牌名称及解析后的浅色主题值。

- 批量选择模式 选择范围与集合行为。

- PatternFly React 仓库 原生组件实现与许可证。

## Red Hat PatternFly 设计系统相关资源

### REI Cedar

构建一个具体的 Cedar 履约方式选择器，采用原生 Vue 磁贴、真实令牌值、完整的键盘导航和响应式产品布局。

### Sainsbury's 设计系统

将 Sainsbury’s 的公开设计指南应用于完整的配送时段选择流程：使用真实的配色和尺寸参考、无障碍单选项、动态更新的摘要，以及如实说明的本地操作反馈。

### Salesforce Lightning

一份 Lightning 蓝图指南，适用于密集的记录处理场景：真实的间距、字体与语义色，一个可编辑的案例工作区、字段验证，以及能反映本地变更的活动历史记录。


- [实现参考](https://www.patternfly.org/patterns/bulk-selection/)
- [来源](https://cdn.jsdelivr.net/npm/@patternfly/react-tokens@6.6.1/dist/esm/)
- [代码参考](https://www.patternfly.org/components/checkbox/)
- [代码参考](https://www.patternfly.org/foundations-and-styles/design-tokens/develop/)
- [使用 PatternFly 开发 原生包与样式表集成。](https://www.patternfly.org/get-started/develop/)
- [已发布的令牌模块 带版本的令牌名称及解析后的浅色主题值。](https://cdn.jsdelivr.net/npm/@patternfly/react-tokens@6.6.1/dist/esm/)
- [批量选择模式 选择范围与集合行为。](https://www.patternfly.org/patterns/bulk-selection/)
- [PatternFly React 仓库 原生组件实现与许可证。](https://github.com/patternfly/patternfly-react)
