fix(popup-toolbar): 修复多选元素时填充色图标显示异常

- 修复内联 style undefined 覆盖 CSS 类导致杂色图标不显示的问题
- 修复 BackgroundColorIcon 作为函数传递导致 React 警告
- 填充色杂色样式改用环形渐变,与线条杂色表示一致
- 添加相关编码规则到文档

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
liquidandClaude committed 2026-01-23 00:13:35 +08:00
1 parent 5049f6eb10
commit bbfeaaac91
5 files changed
+93 -24

No files matched your search

+2
View File
@@ -174,6 +174,8 @@ import { quickInsert } from '../../../services/canvas-operations';
2. Hover 延迟操作需要正确的计时器清理
3. 第三方窗口需用 `createPortal` 保持 React 事件流
4. 图标组件使用 `React.FC`,支持 `size` 属性
5. 传递组件作为 prop 时必须实例化:`icon={<Icon />}` 而非 `icon={Icon}`
6. 内联 style 的 `undefined` 值会覆盖 CSS 类,需要 CSS 类生效时传 `style={undefined}`
### 缓存规则
+58
View File
@@ -526,6 +526,64 @@ const openDialog = (dialogType: DialogType) => {
- 函数式更新 `setState(prev => ...)` 保证 `prev` 始终是最新状态
- 这个问题在多个弹窗/抽屉同时打开时特别容易出现
#### 传递 React 组件作为 prop 时必须实例化
**场景**: 将 React 组件作为 `icon` 或其他 prop 传递给子组件时
❌ **错误示例**:
```typescript
// 错误:传递组件函数本身,而不是 JSX 实例
import { BackgroundColorIcon } from './icons';
const icon = !hexColor ? BackgroundColorIcon : undefined;
// 子组件中渲染时:
// {icon} → React 警告 "Functions are not valid as a React child"
```
✅ **正确示例**:
```typescript
// 正确:传递 JSX 实例
import { BackgroundColorIcon } from './icons';
const icon = !hexColor ? <BackgroundColorIcon /> : undefined;
// 子组件中渲染时:
// {icon} → 正常渲染
```
**原因**: React 组件本质上是函数,直接将函数作为子元素传递会导致 React 警告。需要调用组件(`<Component />`)生成 JSX 元素后再传递。
#### 内联 style 的 undefined 值会覆盖 CSS 类样式
**场景**: 当需要条件性地应用内联样式,同时使用 CSS 类作为备选样式时
❌ **错误示例**:
```typescript
// 错误:style 对象中的 undefined 值会覆盖 CSS 类的 background
<label
className={classNames('fill-label', { 'color-mixed': fill === undefined })}
style={{
background: fill ? fill : undefined, // undefined 会覆盖 .color-mixed 的 background
}}
/>
```
✅ **正确示例**:
```typescript
// 正确:当需要 CSS 类生效时,不传递 style 对象
<label
className={classNames('fill-label', { 'color-mixed': fill === undefined })}
style={
fill === undefined
? undefined // 不设置 style,让 CSS 类的 background 生效
: { background: fill }
}
/>
```
**原因**: React 的内联 style 优先级高于 CSS 类。即使 `background: undefined`,React 仍会在元素上设置空的 style 属性,这可能干扰 CSS 类的样式应用。当需要 CSS 类完全控制样式时,应该不传递 style 对象(`style={undefined}`)。
#### 使用 ResizeObserver 实现组件级别的响应式布局
**场景**: 当组件位于可调整大小的侧边栏、抽屉或面板中时,使用基于视口宽度的媒体查询 (`@media`) 无法准确反映组件的实际可用空间。
@@ -92,7 +92,7 @@ export const PopupFillButton: React.FC<PopupFillButtonProps> = ({
}, [currentColor]);
const icon =
!hexColor || isFullyTransparent(opacity) ? BackgroundColorIcon : undefined;
!hexColor || isFullyTransparent(opacity) ? <BackgroundColorIcon /> : undefined;
// 处理纯色变更
const handleSolidChange = useCallback(
@@ -19,15 +19,19 @@
border: 1px solid #c0c0c0;
}
&.color-mixed {
background: conic-gradient(
from 0deg,
#f87171 0deg 60deg,
#fbbf24 60deg 120deg,
#34d399 120deg 180deg,
#60a5fa 180deg 240deg,
#a78bfa 240deg 300deg,
#f472b6 300deg 360deg
);
// 使用环形渐变效果,与线条杂色表示方式一致
border: var(--border-radius-sm) solid transparent;
background:
linear-gradient(var(--color-surface, #fff), var(--color-surface, #fff)) padding-box,
conic-gradient(
from 0deg,
#f87171 0deg 60deg,
#fbbf24 60deg 120deg,
#34d399 120deg 180deg,
#60a5fa 180deg 240deg,
#a78bfa 240deg 300deg,
#f472b6 300deg 360deg
) border-box;
}
}
.stroke-label {
@@ -501,20 +501,25 @@ export const PopupToolbar = () => {
'color-gradient': state.fill && isFillConfig(state.fill) && state.fill.type === 'gradient',
'color-image': state.fill && isFillConfig(state.fill) && state.fill.type === 'image',
})}
style={{
// 统一使用 background 属性,避免 backgroundColor 和 background 冲突
background: state.fill && isSolidFill(state.fill)
? state.fill
: state.fill && isFillConfig(state.fill)
? state.fill.type === 'gradient' && state.fill.gradient
? gradientToCSS(state.fill.gradient)
: state.fill.type === 'solid' && state.fill.solid
? state.fill.solid.color
: state.fill.type === 'image' && state.fill.image?.imageUrl
? `url(${state.fill.image.imageUrl}) center/cover no-repeat`
: undefined
: undefined,
}}
style={
// 当 fill 为 undefined 时(杂色状态),不设置内联 background,让 CSS 类生效
state.fill === undefined
? undefined
: {
// 统一使用 background 属性,避免 backgroundColor 和 background 冲突
background: isSolidFill(state.fill)
? state.fill
: isFillConfig(state.fill)
? state.fill.type === 'gradient' && state.fill.gradient
? gradientToCSS(state.fill.gradient)
: state.fill.type === 'solid' && state.fill.solid
? state.fill.solid.color
: state.fill.type === 'image' && state.fill.image?.imageUrl
? `url(${state.fill.image.imageUrl}) center/cover no-repeat`
: undefined
: undefined,
}
}
></label>
</PopupFillButton>
)}