首页
/ 为 tldraw 画布应用填充 UI 预留区:TopPanel 与 SharePanel 组件插槽定制指南

为 tldraw 画布应用填充 UI 预留区:TopPanel 与 SharePanel 组件插槽定制指南

2026-09-08 14:18:29作者:董灵辛Dennis

默认的 tldraw 界面在顶部预留了两块空的 UI 插槽(slot):屏幕顶部居中的 TopPanel,以及顶部右侧(样式面板上方)的 SharePanel。本指南讲解如何通过 <Tldraw>components 属性注入自定义 React 组件来填充这两个插槽,适用于在编辑器上方放置文档标题、分享按钮、协作成员头像等自定义功能;读完你将掌握插槽的运行机制、完整实现代码与源码级的布局细节。

两个预留插槽:TopPanel 与 SharePanel

本主题的配套示例文档位于 README.md,其核心说明只有一句话:

默认 UI 为你留下了两个空插槽:屏幕顶部居中的 TopPanel,以及顶部右侧、样式面板上方的 SharePanel。通过 components 属性设置其中任意一个,即可在那里渲染你自己的 React 组件。tldraw.com 正是用它们来承载文档标题与分享按钮。

这两处插槽的渲染位置可以从布局源码中得到印证。在 TldrawUi.tsx 中,顶部区域被划分为左、中、右三个区域:

  • tlui-layout__top__left:渲染 MenuPanel(主菜单)与 HelperButtons(辅助按钮);
  • tlui-layout__top__center居中插槽 TopPanel
  • tlui-layout__top__right右侧插槽 SharePanel,其后才是样式面板 StylePanel(在非只读、且断点达到平板尺寸以上时显示)。

也就是说,SharePanel 之所以"位于样式面板上方",是因为它在 DOM 中先于 StylePanel 渲染,且两者共用右侧顶部栏。

默认值需要澄清的一个细节

示例文档说两个插槽默认都是空的,但从源码看两者默认行为略有差异。在 components.tsx 中,组件上下文的默认值如下:

SharePanel: showCollaborationUi ? DefaultSharePanel : null,
CursorChatBubble: showCollaborationUi ? CursorChatBubble : null,
TopPanel: null,

即:

  • TopPanel 默认就是 null,永远是空的,完全留给你填充;
  • SharePanel 默认并不总是空的:当协作 UI 处于开启状态(showCollaborationUi 为真,例如接入了 tldraw sync 等协作环境)时,它渲染的是 DefaultSharePanel,内部再挂载 PeopleMenu(协作成员列表/头像);在没有协作环境时它才是 null

示例文档将其描述为"空插槽",是站在"最终用户需要自行注入组件、注入后即覆盖默认实现"的使用视角而言的。如果你想完全接管右侧区域,把 SharePanel 换成自己的组件即可——它和 TopPanel 一样,都是 components 属性机制下普通的可替换插槽。

用 components 属性填充插槽

第一步:编写两个自定义组件

参考示例代码 ZonesExample.tsx,组件就是普通的 React 组件,没有特殊 props 约束:

// [1] 渲染在顶部居中区域
function CustomTopPanel() {
	return (
		<div
			style={{
				backgroundColor: 'thistle',
				width: '100%',
				textAlign: 'center',
				padding: '2px',
				minWidth: '80px',
			}}
		>
			<p>Top panel</p>
		</div>
	)
}

// [2] 渲染在顶部右侧、样式面板上方
function CustomSharePanel() {
	return (
		<div
			style={{
				backgroundColor: 'thistle',
				width: '100%',
				textAlign: 'center',
				minWidth: '80px',
			}}
		>
			<p>Share panel</p>
		</div>
	)
}

第二步:组装 TLComponents 并传入

TLComponents 是所有可替换组件的联合接口,它同时继承自编辑器的 TLEditorComponents 与 UI 层的 TLUiComponents(见 Tldraw.tsx)。把两个自定义组件挂到对应的插槽键上:

import { TLComponents, Tldraw } from 'tldraw'
import 'tldraw/tldraw.css'

const components: TLComponents = {
	SharePanel: CustomSharePanel,
	TopPanel: CustomTopPanel,
}

export default function ZonesExample() {
	return (
		<div className="tldraw__editor">
			<Tldraw components={components} />
		</div>
	)
}

示例源码的文件末尾还保留了一段注释,明确说明两个插槽的业务用途定位:

[1] 渲染在顶部居中。tldraw.com 用这个插槽显示文档标题。 [2] 渲染在顶部右侧。tldraw.com 用这个插槽放分享按钮和协作者头像。

这正是这两个插槽最典型的落地方案:居中放文档名/文件名,右侧放与分享、协作相关的操作入口。

第三步:注意 components 对象的引用稳定性

Tldraw.tsx 中,components 属性被显式标注了两条约束:

重要:必须用 useMemo 记忆化,或定义在任何 React 组件之外。

原因在于 UI 层的 TldrawUiComponentsProvider 通过 useShallowObjectIdentity 对 overrides 做浅层引用比较(见 components.tsx),每次渲染若传入新对象,就会引发上下文值重建与重渲染。因此,要么把 components 定义在模块顶层(如上例),要么在组件内部用 useMemo 包裹:

const components = useMemo<TLComponents>(
	() => ({ TopPanel: CustomTopPanel, SharePanel: CustomSharePanel }),
	[]
)
return <Tldraw components={components} />

直接禁用插槽与子组件级定制

设为 null 即可彻底隐藏

TLUiComponents 中每个字段的类型都是 ComponentType<...> | null(见 components.tsx)。Tldraw 组件在渲染时会先判断"组件是否存在"再挂载,例如 {TopPanel && <TopPanel />},因此把某个插槽设为 null 即可完全禁用该区域

const components: TLComponents = {
	TopPanel: null, // 即使默认不渲染,显式置空也可行
	SharePanel: null, // 隐藏默认的协作成员面板
}

TldrawBaseProps 的文档注释也印证了这一点:"用 null 覆盖组件即可彻底禁用它们。"

SharePanel 背后还有更细粒度的协作插槽

SharePanel 是一个"容器级"插槽,而容器内部还有可单独替换的子插槽。从 components.tsx 可以看到与协作/分享相关的其余键:

  • PeopleMenuPeopleMenuAvatarPeopleMenuItemPeopleMenuFacePileUserPresenceEditor 等。

默认的 DefaultSharePanel 内部只是读取 PeopleMenu 并把它包在 .tlui-share-zone 容器中(见 DefaultSharePanel.tsx):

export function DefaultSharePanel() {
	const { PeopleMenu } = useTldrawUiComponents()
	if (!PeopleMenu) return null
	return (
		<div className="tlui-share-zone" draggable={false}>
			<PeopleMenu />
		</div>
	)
}

因此有两种定制粒度:

  1. 整体替换 SharePanel:完全不使用默认实现,适合放"分享到社交平台""邀请链接复制"等自定义按钮;
  2. 保留 DefaultSharePanel,只替换其子组件(如自定义 PeopleMenuAvatar 改变在线头像样式),默认容器与面板逻辑仍然生效。

进阶:让内容在顶部区域智能居中

TopPanel 插槽自身并不会做自动居中——它在布局中位于 .tlui-layout__top__center 容器内。如果你的自定义内容希望在左右 UI(如左侧主菜单、右侧样式面板)之间动态居中、且避免遮挡,仓库还提供了现成的辅助容器 CenteredTopPanelContainer(见 CenteredTopPanelContainer.tsx)。

该组件通过 ResizeObserver 监听顶栏左、右区域与自身宽度,动态计算 translate 偏移并限制 max-width,从而实现"在可用宽度内居中、空间不足时左对齐避让"的效果。它暴露的可调参数包括:

参数 默认值 作用
maxWidth 420 顶栏内容允许的最大宽度
ignoreRightWidth 0 计算自宽时忽略的宽度(如按钮溢出部分)
stylePanelWidth 148 判定右侧样式面板宽度的阈值
marginBetweenZones 12 与左右区域之间保持的最小间距
squeezeAmount 52 小屏(断点 ≤ 6)且右侧过宽时额外收缩的像素

示例中直接在 TopPanel 里用 textAlign: 'center' + width: '100%' 即可满足简单场景;而当你的顶部内容是文档标题这类需要"见缝插针"的 UI 时,CenteredTopPanelContainer 是保持体验稳定的推荐包装。

断点与响应式行为提示

TopPanelSharePanel 都位于顶部栏,顶部栏在小屏/竖屏下仍然渲染,但需要注意:

  • 右侧区域中,StylePanel 只有在断点达到 PORTRAIT_BREAKPOINT.TABLET_SM 及以上且非只读时才渲染(见 TldrawUi.tsx)。因此窄屏下你的 SharePanel 内容会有更多可用空间,但布局代码应做好宽度自适应;
  • 顶栏容器带有 data-breakpoint 等响应式数据属性,若需要精确响应断点,可在组件内使用 useBreakpoint,也可以观察 CenteredTopPanelContainer 的写法作为响应式布局参考。

小结

TopPanelSharePanel 是 tldraw 为自定义宿主应用预留的"官方空白区":

  1. TopPanel 位于屏幕顶部居中,默认恒为空,适合放文档标题等居中内容;
  2. SharePanel 位于顶部右侧、样式面板上方,默认仅在协作 UI 下渲染 DefaultSharePanel,适合放分享按钮与协作者头像,也可整体替换或置 null 禁用;
  3. 两者均通过 <Tldraw components={...}> 注入,组件对象需用模块顶层常量或 useMemo 保证引用稳定;
  4. 需要精细居中时可借助 CenteredTopPanelContainer.tsx,需要拆解协作面板时可单独覆盖 PeopleMenu 系列子插槽。

完整的可运行示例见 ZonesExample.tsx,插槽的类型定义见 components.tsx,渲染布局见 TldrawUi.tsx,三处配合阅读即可彻底掌握该机制。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.74 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.63 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
518
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389