- 可观测性
- 云原生
- 运维
【免费下载链接】hyperdx
Resolve production issues, fast. An open source observability platform unifying session replays, logs, metrics, traces and errors powered by ClickHouse and OpenTelemetry.
HyperDX 的 App 页面共用一套「粘性顶栏 + 可滚动内容区」的页面骨架:左导航(AppNav)+ 统一滚动容器之上,是吸顶的PageHeader页头,页头下方才是各自页面的内容区。本文以 agent_docs/page_layout.md 为骨架,结合 PageHeader.tsx、PageLayout.tsx 及 Alerts、Service Map、Kubernetes Dashboard、Sessions、Dashboard 等真实页面源码,系统讲解两个共享布局原语(primitive)的完整 API、三种页面形态的选型规则、常见反模式与迁移步骤。读完本文,你将掌握在 HyperDX 中新增或改造页面时,如何用一致的方式组织标题、面包屑、来源选择器、时间范围与主操作按钮,保证 Search、列表页与工具页(Service Map、Kubernetes、Chart Explorer)的视觉与交互一致。
为什么需要统一的页头体系
HyperDX App 的所有受保护页面都运行在同一套外壳中。查看 packages/app/src/layout.tsx,withAppNav负责把页面包装进HDXSpotlightProvider与PageWrapper:PageWrapper左侧渲染AppNav,右侧是带有APP_CONTENT_SCROLL_CONTAINER_ID的滚动容器(overflowY: 'scroll'),所有页面内容都在这个容器内滚动。
这意味着页面的"吸顶"是相对于这个滚动容器而言的,而非整个浏览器窗口;任何页面若各自用Text size="xl"写一个标题、再手动拼一个Group justify="space-between"工具条,都会在边距、字号、吸顶层级和滚动行为上与相邻页面产生肉眼可见的差异。统一页头(page chrome)的职责由此而来:让标题、控件与间距在 Search、列表页和工具页之间保持一致。
两个核心组件:PageHeader 与 PageLayout
page_layout.md 给出了两个组件的选型结论,仓库中两个组件相邻位于packages/app/src/components/:
| 组件 | 路径 | 使用场景 |
|---|---|---|
PageHeader | packages/app/src/components/PageHeader.tsx | 只需要页头栏(页面已有自己的内容包装器) |
PageLayout | packages/app/src/components/PageLayout.tsx | 需要"页头 + flex 内容列"一个包装器搞定(新页面推荐) |
两者都位于withAppNav(packages/app/src/layout.tsx)之下,后者提供左侧导航与滚动容器。从源码看,PageLayout本质上是PageHeader的薄封装:PageLayoutProps通过Pick<PageHeaderProps, 'title' | 'leading' | 'actions' | 'breadcrumbs' | 'children'>透传页头插槽,未传入自定义header时,内部自动构造一个<PageHeader>(PageLayout.tsx)。因此插槽语义完全继承自PageHeader。
PageHeader API 详解
PageHeader.tsx 的类型定义中,PageHeaderProps提供了两种组合形式,源码注释按优先级明确标注:
- 结构化插槽:
title/leading/actions/breadcrumbs。适用于标题(或面包屑轨迹)+ 水平工具条能清晰拆成"左侧组 + 右侧组"的标准页头; - 自定义
children:用于工具条无法表达为leading + actions的页面(例如 Sessions 全宽混合控件行),仅在结构化插槽会歪曲预期布局时才使用。
基础用法
<PageHeader title="Alerts" /> {/* 粘性栏包含输入控件时:不传 `title`,把面包屑传进页头 */} <PageHeader breadcrumbs={<Breadcrumbs>...</Breadcrumbs>} leading={<SourceSelectControlled ... />} actions={<TimePicker ... />} /> <PageHeader> {/* 自定义标题行,例如 Team 设置里可编辑的团队名 */} </PageHeader>Props 语义对照表
| Prop | 用途 |
|---|---|
title | 纯文本页面标题(<h1>)。仅当粘性栏没有输入控件时使用(列表/设置页)。如果栏上有选择器、搜索、滑块或 Run 按钮,必须省略title,改用breadcrumbs(或仅靠导航 + 文档标题表达位置)。源码中toolbarInner仅在title != null时渲染<h1 className={styles.title}>(PageHeader.tsx) |
breadcrumbs | 粘性页头内部的位置轨迹,当leading/actions存在时渲染在工具条上方。使用 MantineBreadcrumbs(如Dashboards→ 当前页)。不要与title重复同一页面名称。渲染逻辑见hasBreadcrumbs && hasToolbar分支,此时页头应用headerStacked样式(PageHeader.tsx) |
leading | 左侧簇:来源选择器、徽章或其他控件。存在输入控件时,不要与同一页面名的title配对 |
actions | 右侧簇:时间范围、Run/Save、采样、刷新 |
children | 插槽不够用时的完整自定义页头。除非走纯面包屑分支,否则不要与title/leading/actions/breadcrumbs混用 |
growing | 启用"块级内边距只在工具条超过min-height时出现"的行为。Sessions 的多行搜索使用它;纯标题页省略它 |
stickyRow | 指定页头中唯一吸顶的行,其余页头 chrome 随页面滚动消失(用于 Dashboard 这类"面包屑 + 可编辑名 + 查询工具条"的高页头,见下文进阶章节) |
className/data-testid | 页头样式类与 E2E 测试定位锚点 |
样式与行为细节(源码级)
样式定义在 PageHeader.module.scss:
- 吸顶与分隔:
.header设置position: sticky; top: 0,带border-bottom: 1px solid var(--color-border),与 Search 页的px="sm"对齐,水平内边距为var(--mantine-spacing-sm); - 最小高度:单行页头保持
min-height: 60px;堆叠页头(面包屑 + 工具条)通过.headerStacked改为纵向布局(flex-direction: column)、min-height: auto,随内容生长; - 层级:
z-index: 2,源码注释明确说明该应用顶层抽屉渲染在contextZIndex + 10(即 10),页头保持比抽屉低 8 层,保证抽屉遮罩始终覆盖页面 chrome,同时页头仍浮于普通滚动内容之上(PageHeader.module.scss); growing行为:.header.growing:not(.notSticky)增加padding-block: var(--mantine-spacing-xs)。块级内边距在静止时被吸收,只有当行超过页头高度(Sessions 的多行搜索)才显现,避免查询框贴边;而纯标题页保持零块内边距,60px 单行不产生位移(PageHeader.module.scss);- 行内排版:
.start(标题 + leading)flex: 1,.actions固定不收缩且gap: 12px,.title继承字号/字重、white-space: nowrap,避免标题换行破坏单行布局。
PageLayout API 详解
<PageLayout ><PageLayout >// ❌ 在页面主体里临时写标题 <Group justify="space-between"> <Text size="xl">Service Map</Text> <TimePicker ... /> </Group> // ✅ 工具页:面包屑进页头、输入控件在同一个粘性块中(无 `title`) <PageLayout breadcrumbs={<Breadcrumbs>...</Breadcrumbs>} leading={<SourceSelect ... />} actions={<TimePicker ... />} content={<>...</>} /> // ❌ title 与面包屑重复同一页面名 <PageLayout title="Kubernetes Dashboard" breadcrumbs={<Breadcrumbs>… Kubernetes</Breadcrumbs>} /> // ❌ 用 Box 把整个页面(包括标题)包一层重复 padding <Box p="sm"> <Text size="xl">Alerts</Text> ... </Box> // ✅ 页头在 padding 区之外 <PageHeader title="Alerts" /> <Container py="lg">...</Container>几点原理补充:
- 禁止主体内
Text size="xl"标题:这样的标题不在粘性页头内、不参与统一排版,滚动时也不会吸顶,破坏跨页一致性; - 禁止
title与breadcrumbs文案重复:两者表达同一定位信息会产生冗余;若栏上有输入控件,title还会导致<h1>与工具条争夺单行空间; - 禁止重复 padding 包裹:
PageHeader自带--mantine-spacing-sm水平内边距,页面主体再包一层p="sm"会造成双重留白;正确做法是页头在 padding 区之外,内容用Container/padded自行控制。
迁移现有页面:六步走
- 用
PageLayout或PageHeader插槽替换Text size="xl"标题 +Group justify="space-between"; - 把时间选择器与主操作按钮移到
actions; - 把来源选择器和徽章移到
leading; - 如果粘性栏有输入控件,不要设置
title;当路由存在层级时,把breadcrumbs传给PageLayout(面包屑渲染在粘性PageHeader内部,而不是content里); - 在
PageLayout/ 页面根节点保留data-testid供 E2E 测试使用——例如 AlertsPage.tsx 的data-testid="alerts-page"被 tests/e2e/page-objects/AlertsPage.ts 的page.locator('[data-testid="alerts-page"]')引用,Service Map 的data-testid="service-map-page"与 Kubernetes Dashboard 的data-testid="kubernetes-dashboard-page"同理; - 运行受影响的 Playwright 测试(
packages/app/tests/e2e/)。
变更后记得跑 Knip
packages/app的 Knip 入口根是pages/、scripts/和 e2e 测试。新增或移动PageLayout导入后,请从仓库根目录运行yarn knip(若packages/app单独配置了则在其目录下执行yarn knip),确保新增的消费方仍然被正确接线、没有未使用或未声明的导入残留。
小结
- 选型一句话:只差页头用
PageHeader,页头 + 内容一体的新页面用PageLayout; - 输入控件决定 title 取舍:粘性栏有任何输入控件就省略
title,用breadcrumbs(在页头内部)表达位置; - 全局控件在
actions、上下文控件在leading,全高画布加fillViewport; - 复杂形态走自定义槽:Search/Chart Explorer 保留定制工具条;Sessions 用
header承载单行工具条;Dashboard 用stickyRow钉住查询工具条; - 保持可测性与可维护性:
data-testid落在布局根节点,改动后跑 e2e 与yarn knip。
这套体系的源码、样式与全部真实用例都集中在packages/app/src/components/PageHeader.tsx、packages/app/src/components/PageLayout.tsx与各自.module.scss中,新增页面时直接对照上述规则即可与既有页面保持一致。
- 可观测性
- 云原生
- 运维
【免费下载链接】hyperdx
Resolve production issues, fast. An open source observability platform unifying session replays, logs, metrics, traces and errors powered by ClickHouse and OpenTelemetry.
相关推荐
YouTube.js 节点解析实战:深入剖析 PageHeader 页面头部节点类
YouTube.js 节点解析实战:深入剖析 PageHeader 页面头部节点类 PageHeader 是 YouTube.js(InnerTube API
后端AntdUI页头控件:PageHeader的标题显示与面包屑导航
AntdUI页头控件:PageHeader的标题显示与面包屑导航 还在为WinForm应用缺乏现代化界面而烦恼吗?AntdUI的PageHeader控件为你提供
UI组件桌面应用如何快速美化你的Terminal终端:Terminator Themes终极指南
如何快速美化你的Terminal终端:Terminator Themes终极指南 你是否厌倦了单调的黑色终端界面?想让你的编程环境既美观又高效?Terminat
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考