Initial TrueGrowth source import
This commit is contained in:
453
specs/002-unified-toolbar/quickstart.md
Normal file
453
specs/002-unified-toolbar/quickstart.md
Normal file
@@ -0,0 +1,453 @@
|
||||
# Quickstart: 统一左侧工具栏容器
|
||||
|
||||
**Feature**: 001-unified-toolbar | **Date**: 2025-12-01
|
||||
|
||||
## 开发前准备
|
||||
|
||||
### 1. 环境要求
|
||||
|
||||
```bash
|
||||
# Node.js 18+ (项目要求)
|
||||
node --version # 应该显示 v18.x 或更高
|
||||
|
||||
# 检查依赖
|
||||
npm install
|
||||
|
||||
# 验证TypeScript编译
|
||||
nx typecheck drawnix
|
||||
```
|
||||
|
||||
### 2. 理解现有架构
|
||||
|
||||
**阅读以下文件** (约15分钟):
|
||||
|
||||
```bash
|
||||
# 现有工具栏实现
|
||||
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 isMobile` mixin应用不同样式
|
||||
- 所有工具栏使用 `Island` 组件包裹
|
||||
- 工具栏组件通过 `DrawnixContext` 访问 board 和 appState
|
||||
|
||||
---
|
||||
|
||||
## 开发流程
|
||||
|
||||
### Phase 1: 创建UnifiedToolbar组件骨架 (30分钟)
|
||||
|
||||
#### 1.1 创建组件文件
|
||||
|
||||
```bash
|
||||
# 在正确的目录创建文件
|
||||
touch packages/drawnix/src/components/toolbar/unified-toolbar.tsx
|
||||
```
|
||||
|
||||
**unified-toolbar.tsx** 初始结构:
|
||||
|
||||
```typescript
|
||||
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` 添加:
|
||||
|
||||
```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` 为例:
|
||||
|
||||
```typescript
|
||||
// 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.tsx`
|
||||
- `zoom-toolbar.tsx`
|
||||
- `theme-toolbar.tsx`
|
||||
|
||||
#### 2.2 更新样式禁用独立定位
|
||||
|
||||
```scss
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
// 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`:
|
||||
|
||||
```typescript
|
||||
// 计算阈值
|
||||
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
|
||||
|
||||
```typescript
|
||||
return (
|
||||
<div ref={containerRef} className={...}>
|
||||
<AppToolbar embedded iconMode={isIconMode} />
|
||||
<CreationToolbar embedded iconMode={isIconMode} />
|
||||
<ZoomToolbar embedded iconMode={isIconMode} />
|
||||
<ThemeToolbar embedded iconMode={isIconMode} />
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
#### 4.3 添加图标模式样式
|
||||
|
||||
```scss
|
||||
.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 手动测试清单
|
||||
|
||||
```bash
|
||||
# 启动开发服务器
|
||||
npm start
|
||||
```
|
||||
|
||||
**桌面端测试** (打开 http://localhost:7200):
|
||||
|
||||
- [ ] 工具栏是否在左侧垂直排列?
|
||||
- [ ] 四个分区之间是否有1px分割线?
|
||||
- [ ] 缩小浏览器高度,工具栏是否切换到图标模式?
|
||||
- [ ] 恢复高度,是否恢复正常模式?
|
||||
- [ ] 所有工具按钮是否仍然可点击?
|
||||
- [ ] 撤销/重做快捷键(Cmd+Z/Cmd+Shift+Z)是否有效?
|
||||
|
||||
**移动端测试** (Chrome DevTools切换到移动设备):
|
||||
|
||||
- [ ] 工具栏是否保持原有位置(应用工具在底部,创作工具在顶部)?
|
||||
- [ ] 布局是否与改动前完全一致?
|
||||
|
||||
#### 5.2 编写组件测试
|
||||
|
||||
```typescript
|
||||
// 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: 添加响应式测试
|
||||
});
|
||||
```
|
||||
|
||||
运行测试:
|
||||
|
||||
```bash
|
||||
nx test drawnix
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见问题排查
|
||||
|
||||
### 问题1: 工具栏不显示
|
||||
|
||||
**检查**:
|
||||
- [ ] 是否在桌面端?(移动端不显示UnifiedToolbar)
|
||||
- [ ] CSS文件是否正确导入?
|
||||
- [ ] 是否存在样式冲突?
|
||||
|
||||
**Debug**:
|
||||
```javascript
|
||||
// 在drawnix.tsx添加日志
|
||||
console.log('isMobile:', appState.isMobile);
|
||||
```
|
||||
|
||||
### 问题2: 工具栏位置不对
|
||||
|
||||
**检查**:
|
||||
- [ ] `.unified-toolbar` 是否有 `position: absolute`?
|
||||
- [ ] `left: 36px; top: 36px;` 是否正确应用?
|
||||
- [ ] 是否有其他样式覆盖了定位?
|
||||
|
||||
### 问题3: 图标模式不工作
|
||||
|
||||
**检查**:
|
||||
- [ ] ResizeObserver是否成功创建?
|
||||
- [ ] isIconMode状态是否正确更新?
|
||||
- [ ] iconMode prop是否传递给子组件?
|
||||
|
||||
**Debug**:
|
||||
```javascript
|
||||
// 在unified-toolbar.tsx添加日志
|
||||
console.log('Container height:', height, 'Icon mode:', isIconMode);
|
||||
```
|
||||
|
||||
### 问题4: 移动端布局被破坏
|
||||
|
||||
**检查**:
|
||||
- [ ] 条件渲染逻辑是否正确?(`!appState.isMobile`)
|
||||
- [ ] 移动端样式是否仍然应用?(`@include isMobile`)
|
||||
- [ ] 是否意外修改了移动端特有的样式?
|
||||
|
||||
---
|
||||
|
||||
## 性能优化检查
|
||||
|
||||
运行完成后验证:
|
||||
|
||||
```bash
|
||||
# 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]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 下一步
|
||||
|
||||
实现完成后:
|
||||
|
||||
1. 运行完整测试套件: `nx test drawnix && nx e2e web`
|
||||
2. 创建Pull Request
|
||||
3. 请求代码审查
|
||||
4. 部署到预发布环境验证
|
||||
|
||||
---
|
||||
|
||||
## 参考文档
|
||||
|
||||
- [宪章](/.specify/memory/constitution.md) - 编码标准和约束
|
||||
- [CLAUDE.md](/CLAUDE.md) - 项目架构概览
|
||||
- [技术规范](./spec.md) - 功能需求
|
||||
- [研究文档](./research.md) - 技术决策
|
||||
- [数据模型](./data-model.md) - 类型定义
|
||||
|
||||
预计总开发时间: **3-4小时** (包括测试)
|
||||
Reference in New Issue
Block a user