9.7 KiB
9.7 KiB
Quickstart: 统一左侧工具栏容器
Feature: 001-unified-toolbar | Date: 2025-12-01
开发前准备
1. 环境要求
# Node.js 18+ (项目要求)
node --version # 应该显示 v18.x 或更高
# 检查依赖
npm install
# 验证TypeScript编译
nx typecheck drawnix
2. 理解现有架构
阅读以下文件 (约15分钟):
# 现有工具栏实现
packages/drawnix/src/components/toolbar/app-toolbar/app-toolbar.tsx
packages/drawnix/src/components/toolbar/creation-toolbar.tsx
packages/drawnix/src/components/toolbar/zoom-toolbar.tsx
packages/drawnix/src/components/toolbar/theme-toolbar.tsx
# 主应用入口
packages/drawnix/src/drawnix.tsx
# 现有样式
packages/drawnix/src/styles/index.scss # 查找 .app-toolbar, .draw-toolbar等
关键点:
- 四个工具栏当前使用绝对定位(
position: absolute) - 移动端通过
@include isMobilemixin应用不同样式 - 所有工具栏使用
Island组件包裹 - 工具栏组件通过
DrawnixContext访问 board 和 appState
开发流程
Phase 1: 创建UnifiedToolbar组件骨架 (30分钟)
1.1 创建组件文件
# 在正确的目录创建文件
touch packages/drawnix/src/components/toolbar/unified-toolbar.tsx
unified-toolbar.tsx 初始结构:
import React, { useState, useCallback, useEffect, useRef } from 'react';
import classNames from 'classnames';
import { ATTACHED_ELEMENT_CLASS_NAME } from '@plait/core';
import { Island } from '../island';
import { AppToolbar } from './app-toolbar/app-toolbar';
import { CreationToolbar } from './creation-toolbar';
import { ZoomToolbar } from './zoom-toolbar';
import { ThemeToolbar } from './theme-toolbar';
interface UnifiedToolbarProps {
className?: string;
}
export const UnifiedToolbar: React.FC<UnifiedToolbarProps> = ({
className
}) => {
const [isIconMode, setIsIconMode] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
// TODO: 添加ResizeObserver监听高度变化
return (
<div
ref={containerRef}
className={classNames(
'unified-toolbar',
ATTACHED_ELEMENT_CLASS_NAME,
{
'unified-toolbar--icon-only': isIconMode,
},
className
)}
>
{/* TODO: 添加工具栏分区 */}
</div>
);
};
1.2 添加基础样式
在 packages/drawnix/src/styles/index.scss 添加:
.unified-toolbar {
position: absolute;
left: 36px;
top: 36px;
display: flex;
flex-direction: column;
gap: 0; // 分割线通过border实现,不需要gap
// 仅桌面端显示
@include isMobile {
display: none;
}
&__section {
&:not(:first-child) {
border-top: 1px solid var(--color-border);
padding-top: 8px;
}
}
&--icon-only {
// 图标模式修饰符,后续添加子元素样式
}
}
Phase 2: 修改现有工具栏组件 (45分钟)
2.1 添加embedded prop支持
以 AppToolbar 为例:
// app-toolbar.tsx
interface AppToolbarProps {
embedded?: boolean; // [NEW] 是否嵌入统一容器
iconMode?: boolean; // [NEW] 是否图标模式
}
export const AppToolbar: React.FC<AppToolbarProps> = ({
embedded = false,
iconMode = false
}) => {
// ...现有逻辑
return (
<Island
padding={1}
className={classNames('app-toolbar', ATTACHED_ELEMENT_CLASS_NAME, {
'app-toolbar--embedded': embedded,
'app-toolbar--icon-only': iconMode,
})}
>
{/* 现有工具栏内容保持不变 */}
</Island>
);
};
重复以上修改 对所有4个工具栏组件:
creation-toolbar.tsxzoom-toolbar.tsxtheme-toolbar.tsx
2.2 更新样式禁用独立定位
// index.scss
.app-toolbar {
position: absolute;
top: 36px;
left: 36px;
// [NEW] 嵌入模式禁用定位
&--embedded {
position: static;
top: auto;
left: auto;
}
@include isMobile {
// 移动端样式保持不变
}
}
// 对 .draw-toolbar, .zoom-toolbar, .theme-toolbar 重复相同模式
Phase 3: 集成到主应用 (20分钟)
3.1 修改drawnix.tsx
// drawnix.tsx
import { UnifiedToolbar } from './components/toolbar/unified-toolbar';
// 在渲染部分替换工具栏
<Wrapper ...>
<Board ...></Board>
{/* [MODIFIED] 桌面端使用统一工具栏,移动端保持不变 */}
{!appState.isMobile ? (
<UnifiedToolbar />
) : (
<>
<AppToolbar />
<CreationToolbar />
<ZoomToolbar />
<ThemeToolbar />
</>
)}
{/* 其他组件保持不变 */}
<PopupToolbar />
<LinkPopup />
{/* ... */}
</Wrapper>
Phase 4: 实现响应式图标模式 (40分钟)
4.1 添加ResizeObserver
在 unified-toolbar.tsx:
// 计算阈值
const TOOLBAR_MIN_HEIGHT = 460; // 基于四个分区最小高度
useEffect(() => {
const container = containerRef.current;
if (!container) return;
const observer = new ResizeObserver((entries) => {
const height = entries[0].contentRect.height;
setIsIconMode(height < TOOLBAR_MIN_HEIGHT);
});
observer.observe(container);
return () => observer.disconnect();
}, []);
4.2 传递iconMode prop
return (
<div ref={containerRef} className={...}>
<AppToolbar embedded iconMode={isIconMode} />
<CreationToolbar embedded iconMode={isIconMode} />
<ZoomToolbar embedded iconMode={isIconMode} />
<ThemeToolbar embedded iconMode={isIconMode} />
</div>
);
4.3 添加图标模式样式
.tool-button__label {
display: inline-block;
margin-left: 8px;
transition: opacity 0.2s ease-out;
// 图标模式隐藏文本
.app-toolbar--icon-only &,
.draw-toolbar--icon-only &,
.zoom-toolbar--icon-only &,
.theme-toolbar--icon-only & {
display: none;
}
}
Phase 5: 测试 (30分钟)
5.1 手动测试清单
# 启动开发服务器
npm start
桌面端测试 (打开 http://localhost:7200):
- 工具栏是否在左侧垂直排列?
- 四个分区之间是否有1px分割线?
- 缩小浏览器高度,工具栏是否切换到图标模式?
- 恢复高度,是否恢复正常模式?
- 所有工具按钮是否仍然可点击?
- 撤销/重做快捷键(Cmd+Z/Cmd+Shift+Z)是否有效?
移动端测试 (Chrome DevTools切换到移动设备):
- 工具栏是否保持原有位置(应用工具在底部,创作工具在顶部)?
- 布局是否与改动前完全一致?
5.2 编写组件测试
// packages/drawnix/tests/components/toolbar/unified-toolbar.test.tsx
import { render } from '@testing-library/react';
import { UnifiedToolbar } from '../../../src/components/toolbar/unified-toolbar';
describe('UnifiedToolbar', () => {
it('should render all four toolbar sections', () => {
const { container } = render(<UnifiedToolbar />);
expect(container.querySelector('.app-toolbar')).toBeInTheDocument();
expect(container.querySelector('.draw-toolbar')).toBeInTheDocument();
expect(container.querySelector('.zoom-toolbar')).toBeInTheDocument();
expect(container.querySelector('.theme-toolbar')).toBeInTheDocument();
});
it('should have vertical layout', () => {
const { container } = render(<UnifiedToolbar />);
const toolbar = container.querySelector('.unified-toolbar');
expect(toolbar).toHaveStyle({ flexDirection: 'column' });
});
// TODO: 添加响应式测试
});
运行测试:
nx test drawnix
常见问题排查
问题1: 工具栏不显示
检查:
- 是否在桌面端?(移动端不显示UnifiedToolbar)
- CSS文件是否正确导入?
- 是否存在样式冲突?
Debug:
// 在drawnix.tsx添加日志
console.log('isMobile:', appState.isMobile);
问题2: 工具栏位置不对
检查:
.unified-toolbar是否有position: absolute?left: 36px; top: 36px;是否正确应用?- 是否有其他样式覆盖了定位?
问题3: 图标模式不工作
检查:
- ResizeObserver是否成功创建?
- isIconMode状态是否正确更新?
- iconMode prop是否传递给子组件?
Debug:
// 在unified-toolbar.tsx添加日志
console.log('Container height:', height, 'Icon mode:', isIconMode);
问题4: 移动端布局被破坏
检查:
- 条件渲染逻辑是否正确?(
!appState.isMobile) - 移动端样式是否仍然应用?(
@include isMobile) - 是否意外修改了移动端特有的样式?
性能优化检查
运行完成后验证:
# TypeScript类型检查
nx typecheck drawnix
# Lint检查
nx lint drawnix
# 构建检查
nx build drawnix
性能基准 (Chrome DevTools Performance tab):
- 工具栏首次渲染 < 16ms
- 响应式切换延迟 < 100ms
- 无Layout Shift警告
提交前清单
- 所有文件 < 500行
- TypeScript严格模式通过
- ESLint无错误
- 所有测试通过
- 手动测试通过(桌面 + 移动)
- 无console.log残留
- Git commit message符合规范:
feat(toolbar): 实现统一左侧工具栏容器 - 创建UnifiedToolbar组件整合四个工具栏 - 添加1px水平分割线分隔分区 - 实现响应式图标模式 - 保持移动端布局不变 - 所有功能和快捷键正常工作 Closes #[issue-number]
下一步
实现完成后:
- 运行完整测试套件:
nx test drawnix && nx e2e web - 创建Pull Request
- 请求代码审查
- 部署到预发布环境验证
参考文档
预计总开发时间: 3-4小时 (包括测试)