mirror of
https://github.com/hanzoai/icons.git
synced 2026-09-24 16:59:52 +00:00
♻️ refactor: Refactor static syle
This commit is contained in:
@@ -0,0 +1,959 @@
|
||||
# createStaticStyles 迁移指南
|
||||
|
||||
## 📖 概述
|
||||
|
||||
`createStaticStyles` 是 `antd-style` 提供的静态样式创建函数,相比 `createStyles`(hook 方案)具有零运行时开销的优势。样式在模块加载时计算一次,而不是每次组件渲染时计算。
|
||||
|
||||
## 🎯 适用场景
|
||||
|
||||
### ✅ 可以优化的场景
|
||||
|
||||
1. **纯静态样式**:不依赖运行时动态值
|
||||
2. **使用标准 token**:所有 token 都在 `cssVar.json` 中有对应项
|
||||
3. **简单的条件逻辑**:可以通过静态样式拆分处理
|
||||
|
||||
### ❌ 无法优化的场景
|
||||
|
||||
1. **JS 计算函数**:`readableColor()`, `chroma()`, `mix()`, `calc()` 中使用 token 数值
|
||||
2. **复杂的动态 props**:需要运行时计算的复杂逻辑
|
||||
3. **动态 prefixCls**:需要运行时传入的类名前缀(但可以硬编码为 `'ant'`)
|
||||
|
||||
## 🔄 基本转换步骤
|
||||
|
||||
### 1. 样式文件转换
|
||||
|
||||
**之前(createStyles):**
|
||||
|
||||
```typescript
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, token }) => {
|
||||
return {
|
||||
root: css`
|
||||
color: ${token.colorText};
|
||||
font-size: ${token.fontSize}px;
|
||||
`,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
**之后(createStaticStyles):**
|
||||
|
||||
```typescript
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
export const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
return {
|
||||
root: css`
|
||||
color: ${cssVar.colorText};
|
||||
font-size: ${cssVar.fontSize};
|
||||
`,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
### 2. 组件文件转换
|
||||
|
||||
**之前:**
|
||||
|
||||
```typescript
|
||||
import { useStyles } from './style';
|
||||
|
||||
const Component = () => {
|
||||
const { styles, cx } = useStyles();
|
||||
return <div className={cx(styles.root, className)} />;
|
||||
};
|
||||
```
|
||||
|
||||
**之后:**
|
||||
|
||||
```typescript
|
||||
import { cx } from 'antd-style';
|
||||
import { styles } from './style';
|
||||
|
||||
const Component = () => {
|
||||
return <div className={cx(styles.root, className)} />;
|
||||
};
|
||||
```
|
||||
|
||||
## 🛠️ 常见场景处理
|
||||
|
||||
### 场景 1: Token 转换
|
||||
|
||||
**规则:**
|
||||
|
||||
- `token.xxx` → `cssVar.xxx`
|
||||
- 注意:`cssVar.fontSize` 已经包含 `px` 单位,不需要再加 `px`
|
||||
|
||||
**示例:**
|
||||
|
||||
```typescript
|
||||
// ❌ 错误
|
||||
font-size: ${cssVar.fontSize}px; // cssVar.fontSize 已经是 "14px"
|
||||
|
||||
// ✅ 正确
|
||||
font-size: ${cssVar.fontSize}; // 直接使用
|
||||
```
|
||||
|
||||
**特殊情况 - calc ():**
|
||||
|
||||
```typescript
|
||||
// ❌ 错误
|
||||
calc(${token.fontSize}px * 2.5)
|
||||
|
||||
// ✅ 正确
|
||||
calc(${cssVar.fontSize} * 2.5) // cssVar.fontSize 已经包含单位
|
||||
```
|
||||
|
||||
### 场景 2: 动态 Props → CSS 变量
|
||||
|
||||
**适用:** 数值、字符串类型的 props
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. 在样式文件中使用 CSS 变量(带默认值)
|
||||
2. 在组件中通过 `style` prop 设置 CSS 变量
|
||||
|
||||
**示例:**
|
||||
|
||||
**样式文件:**
|
||||
|
||||
```typescript
|
||||
export const styles = createStaticStyles(({ css }) => {
|
||||
return {
|
||||
root: css`
|
||||
width: var(--component-size, 24px);
|
||||
height: var(--component-size, 24px);
|
||||
`,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
**组件文件:**
|
||||
|
||||
```typescript
|
||||
import { useMemo } from 'react';
|
||||
|
||||
const Component = ({ size = 24, style, ...rest }) => {
|
||||
const cssVariables = useMemo<Record<string, string>>(
|
||||
() => ({
|
||||
'--component-size': `${size}px`,
|
||||
}),
|
||||
[size],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.root}
|
||||
style={{
|
||||
...cssVariables,
|
||||
...style,
|
||||
}}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**已优化示例:**
|
||||
|
||||
- `Video`: `maxHeight`, `maxWidth`, `minHeight`, `minWidth`
|
||||
- `ScrollShadow`: `size`
|
||||
- `MaskShadow`: `size`
|
||||
- `ColorSwatches`: `size`
|
||||
- `Grid`: `rows`, `maxItemWidth`, `gap`
|
||||
- `Layout`: `headerHeight`
|
||||
- `Footer`: `contentMaxWidth`
|
||||
|
||||
### 场景 3: 布尔值 Props → 静态样式拆分
|
||||
|
||||
**适用:** 简单的布尔值 props(2-3 个)
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. 创建所有可能的组合样式
|
||||
2. 运行时使用 `cx` 组合
|
||||
|
||||
**示例:**
|
||||
|
||||
**样式文件:**
|
||||
|
||||
```typescript
|
||||
export const styles = createStaticStyles(({ css }) => {
|
||||
return {
|
||||
root: css`
|
||||
/* base styles */
|
||||
`,
|
||||
root_closable_true: css`
|
||||
/* closable styles */
|
||||
`,
|
||||
root_closable_false: css`
|
||||
/* no closable styles */
|
||||
`,
|
||||
root_hasTitle_true: css`
|
||||
/* has title styles */
|
||||
`,
|
||||
root_hasTitle_false: css`
|
||||
/* no title styles */
|
||||
`,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
**组件文件:**
|
||||
|
||||
```typescript
|
||||
const Component = ({ closable, hasTitle }) => {
|
||||
const className = cx(
|
||||
styles.root,
|
||||
styles[`root_closable_${!!closable}`],
|
||||
styles[`root_hasTitle_${!!hasTitle}`],
|
||||
);
|
||||
return <div className={className} />;
|
||||
};
|
||||
```
|
||||
|
||||
**已优化示例:**
|
||||
|
||||
- `Alert`: `closable`, `hasTitle`, `showIcon` → 8 个组合(2×2×2)
|
||||
- `Image`: `alwaysShowActions` → 2 个样式
|
||||
- `StoryBook`: `noPadding` → 2 个样式
|
||||
|
||||
### 场景 4: isDarkMode → 静态样式拆分
|
||||
|
||||
**适用:** 依赖 `isDarkMode` 的条件样式
|
||||
|
||||
**有两种处理方式:**
|
||||
|
||||
#### 方式 A: 直接条件选择(简单场景)
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. 创建 `Dark` 和 `Light` 两个静态样式
|
||||
2. 运行时根据 `theme.isDarkMode` 选择
|
||||
|
||||
**示例:**
|
||||
|
||||
**样式文件:**
|
||||
|
||||
```typescript
|
||||
export const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
return {
|
||||
rootDark: css`
|
||||
background: ${cssVar.colorFillTertiary};
|
||||
color: ${cssVar.colorTextLightSolid};
|
||||
`,
|
||||
rootLight: css`
|
||||
background: ${cssVar.colorFillQuaternary};
|
||||
color: ${cssVar.colorText};
|
||||
`,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
**组件文件:**
|
||||
|
||||
```typescript
|
||||
import { useThemeMode } from 'antd-style';
|
||||
|
||||
const Component = () => {
|
||||
const { isDarkMode } = useThemeMode();
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
isDarkMode ? styles.rootDark : styles.rootLight
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
#### 方式 B: 使用 cva 将 isDarkMode 作为 variant(推荐,适用于复杂场景)
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. 创建 `Dark` 和 `Light` 两个静态样式
|
||||
2. 在 `cva` 中将 `isDarkMode` 作为 variant prop
|
||||
3. 运行时直接传入 `isDarkMode` 值
|
||||
|
||||
**示例:**
|
||||
|
||||
**样式文件:**
|
||||
|
||||
```typescript
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { cva } from 'class-variance-authority';
|
||||
|
||||
export const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
return {
|
||||
filledDark: css`
|
||||
background: ${cssVar.colorFillTertiary};
|
||||
color: ${cssVar.colorTextLightSolid};
|
||||
`,
|
||||
filledLight: css`
|
||||
background: ${cssVar.colorFillQuaternary};
|
||||
color: ${cssVar.colorText};
|
||||
`,
|
||||
outlined: css`
|
||||
border: 1px solid ${cssVar.colorBorder};
|
||||
`,
|
||||
root: css`
|
||||
/* base styles */
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
export const variants = cva(styles.root, {
|
||||
defaultVariants: {
|
||||
isDarkMode: false,
|
||||
variant: 'filled',
|
||||
},
|
||||
variants: {
|
||||
isDarkMode: {
|
||||
false: null,
|
||||
true: null, // isDarkMode 本身不添加样式,通过 compoundVariants 组合
|
||||
},
|
||||
variant: {
|
||||
filled: null, // variant 本身不添加样式,通过 compoundVariants 组合
|
||||
outlined: styles.outlined,
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
class: styles.filledDark,
|
||||
isDarkMode: true,
|
||||
variant: 'filled',
|
||||
},
|
||||
{
|
||||
class: styles.filledLight,
|
||||
isDarkMode: false,
|
||||
variant: 'filled',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
**组件文件:**
|
||||
|
||||
```typescript
|
||||
import { useThemeMode } from 'antd-style';
|
||||
import { variants } from './style';
|
||||
|
||||
const Component = ({ variant = 'filled' }) => {
|
||||
const { isDarkMode } = useThemeMode();
|
||||
return (
|
||||
<div
|
||||
className={variants({ isDarkMode, variant })}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**优势:**
|
||||
|
||||
- ✅ 不需要 `useMemo` 动态创建 variants
|
||||
- ✅ 更符合 `cva` 的设计理念
|
||||
- ✅ 代码更简洁,性能更好
|
||||
- ✅ 类型安全,IDE 自动补全
|
||||
|
||||
**已优化示例:**
|
||||
|
||||
- `TypewriterEffect`: `textDark` / `textLight`(方式 A)
|
||||
- `Collapse`: `filledDark` / `filledLight`(可优化为方式 B)
|
||||
- `Hotkey`: `inverseThemeDark` / `inverseThemeLight`(可优化为方式 B)
|
||||
- `GuideCard`: `filledDark` / `filledLight`(可优化为方式 B)
|
||||
- `GradientButton`: `buttonDark` / `buttonLight`(方式 A)
|
||||
|
||||
### 场景 5: responsive → 静态 responsive
|
||||
|
||||
**适用:** 使用响应式断点
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. 导入静态 `responsive` from `antd-style`
|
||||
2. 使用 `responsive.sm` 替代 `responsive.mobile`
|
||||
3. 从 `createStyles` 参数中移除 `responsive`
|
||||
|
||||
**示例:**
|
||||
|
||||
**之前:**
|
||||
|
||||
```typescript
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, responsive }) => ({
|
||||
root: css`
|
||||
${responsive.mobile} {
|
||||
padding: 12px;
|
||||
}
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**之后:**
|
||||
|
||||
```typescript
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { responsive } from 'antd-style';
|
||||
|
||||
export const styles = createStaticStyles(({ css }) => ({
|
||||
root: css`
|
||||
${responsive.sm} {
|
||||
padding: 12px;
|
||||
}
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**注意:**
|
||||
|
||||
- `responsive.mobile` → `responsive.sm`
|
||||
- 静态 `responsive` 提供:`xs`, `sm`, `md`, `lg`, `xl`, `xxl`
|
||||
|
||||
**已优化示例:**
|
||||
|
||||
- `Header`: `responsive.mobile` → `responsive.sm`
|
||||
- `FormModal`: `responsive.mobile` → `responsive.sm`
|
||||
- `Hero`: `responsive.mobile` → `responsive.sm`
|
||||
|
||||
### 场景 6: stylish → lobeStaticStylish
|
||||
|
||||
**适用:** 使用自定义 `stylish` 工具
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. 导入 `lobeStaticStylish` from `@/styles`
|
||||
2. 替换 `stylish.xxx` → `lobeStaticStylish.xxx`
|
||||
|
||||
**示例:**
|
||||
|
||||
**之前:**
|
||||
|
||||
```typescript
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, stylish }) => ({
|
||||
root: css`
|
||||
${stylish.blur};
|
||||
${stylish.variantFilled};
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**之后:**
|
||||
|
||||
```typescript
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
import { lobeStaticStylish } from '@/styles';
|
||||
|
||||
export const styles = createStaticStyles(({ css }) => ({
|
||||
root: css`
|
||||
${lobeStaticStylish.blur};
|
||||
${lobeStaticStylish.variantFilled};
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**已优化示例:**
|
||||
|
||||
- `Button`: `stylish.blur` → `lobeStaticStylish.blur`
|
||||
- `Hero`: `stylish.gradientAnimation` → `lobeStaticStylish.gradientAnimation`
|
||||
|
||||
### 场景 7: prefixCls → 硬编码
|
||||
|
||||
**适用:** 使用动态 `prefixCls` 参数
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. 在文件顶部硬编码 `const prefixCls = 'ant'`
|
||||
2. 从 `createStyles` 参数中移除 `prefixCls`
|
||||
|
||||
**示例:**
|
||||
|
||||
**之前:**
|
||||
|
||||
```typescript
|
||||
export const useStyles = createStyles(({ css }, prefixCls: string) => ({
|
||||
root: css`
|
||||
.${prefixCls}-button {
|
||||
/* styles */
|
||||
}
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**之后:**
|
||||
|
||||
```typescript
|
||||
const prefixCls = 'ant';
|
||||
|
||||
export const styles = createStaticStyles(({ css }) => ({
|
||||
root: css`
|
||||
.${prefixCls}-button {
|
||||
/* styles */
|
||||
}
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**已优化示例:**
|
||||
|
||||
- `Alert`, `Collapse`, `FormModal`, `Image`, `Burger`, `DraggablePanel`, `DraggableSideNav`, `Toc`, `ColorSwatches`, `EmojiPicker`, `Form`, `awesome/Features`
|
||||
|
||||
### 场景 8: readableColor () → Token 替换
|
||||
|
||||
**适用:** 使用 `readableColor()` 计算对比色
|
||||
|
||||
**规则:**
|
||||
|
||||
- `readableColor(token.colorPrimary)` → `cssVar.colorTextLightSolid`(主色背景用白色文字)
|
||||
- `readableColor(token.colorTextQuaternary)` → `cssVar.colorText`(浅色背景用深色文字)
|
||||
|
||||
**示例:**
|
||||
|
||||
**之前:**
|
||||
|
||||
```typescript
|
||||
import { readableColor } from 'polished';
|
||||
|
||||
export const useStyles = createStyles(({ css, token }) => ({
|
||||
checked: css`
|
||||
background-color: ${token.colorPrimary};
|
||||
color: ${readableColor(token.colorPrimary)};
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**之后:**
|
||||
|
||||
```typescript
|
||||
export const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
checked: css`
|
||||
background-color: ${cssVar.colorPrimary};
|
||||
color: ${cssVar.colorTextLightSolid};
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**已优化示例:**
|
||||
|
||||
- `Checkbox`: `readableColor(token.colorPrimary)` → `cssVar.colorTextLightSolid`
|
||||
|
||||
### 场景 9: rgba () → color-mix ()
|
||||
|
||||
**适用:** 使用 `rgba()` 设置透明度
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. 使用 CSS 原生的 `color-mix()` 函数
|
||||
2. 格式:`color-mix(in srgb, ${cssVar.xxx} alpha%, transparent)`
|
||||
|
||||
**示例:**
|
||||
|
||||
**之前:**
|
||||
|
||||
```typescript
|
||||
import { rgba } from 'polished';
|
||||
|
||||
export const useStyles = createStyles(({ css, token }) => ({
|
||||
root: css`
|
||||
background-color: ${rgba(token.colorBgLayout, 0.4)};
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**之后:**
|
||||
|
||||
```typescript
|
||||
export const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
root: css`
|
||||
background-color: color-mix(in srgb, ${cssVar.colorBgLayout} 40%, transparent);
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**已优化示例:**
|
||||
|
||||
- `Header`: `rgba(cssVar.colorBgLayout, 0.4)` → `color-mix(...)`
|
||||
- `FormModal`: `rgba(cssVar.colorBgContainer, 0)` → `color-mix(...)`
|
||||
|
||||
### 场景 10: keyframes → css
|
||||
|
||||
**适用:** 使用 `keyframes` 创建动画
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. 在 `createStaticStyles` 外部定义 `keyframes`
|
||||
2. 在样式内部使用
|
||||
|
||||
**示例:**
|
||||
|
||||
**之前:**
|
||||
|
||||
```typescript
|
||||
export const useStyles = createStyles(({ css, keyframes }) => {
|
||||
const spin = keyframes`
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
`;
|
||||
return {
|
||||
icon: css`
|
||||
animation: ${spin} 1s linear infinite;
|
||||
`,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
**之后:**
|
||||
|
||||
```typescript
|
||||
import { keyframes } from 'antd-style';
|
||||
|
||||
const spin = keyframes`
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
`;
|
||||
|
||||
export const styles = createStaticStyles(({ css }) => ({
|
||||
icon: css`
|
||||
animation: ${spin} 1s linear infinite;
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**已优化示例:**
|
||||
|
||||
- `Icon`: `keyframes` 动画
|
||||
- `Skeleton`: `keyframes` shimmer 动画
|
||||
|
||||
## ⚠️ 反模式:避免使用 createVariants (isDarkMode)
|
||||
|
||||
**不推荐的做法:**
|
||||
|
||||
```typescript
|
||||
// ❌ 不推荐:在组件中动态创建 variants
|
||||
export const createVariants = (isDarkMode: boolean) =>
|
||||
cva(styles.root, {
|
||||
variants: {
|
||||
variant: {
|
||||
filled: isDarkMode ? styles.filledDark : styles.filledLight,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 组件中
|
||||
const variants = useMemo(() => createVariants(isDarkMode), [isDarkMode]);
|
||||
```
|
||||
|
||||
**推荐的做法:**
|
||||
|
||||
将 `isDarkMode` 作为 `cva` 的 variant prop(见场景 4 方式 B),这样:
|
||||
|
||||
- ✅ 不需要 `useMemo` 动态创建
|
||||
- ✅ 更符合 `cva` 的设计理念
|
||||
- ✅ 代码更简洁,性能更好
|
||||
- ✅ 类型安全,IDE 自动补全
|
||||
|
||||
```typescript
|
||||
// ✅ 推荐:将 isDarkMode 作为 variant prop
|
||||
export const variants = cva(styles.root, {
|
||||
variants: {
|
||||
isDarkMode: {
|
||||
false: null,
|
||||
true: null,
|
||||
},
|
||||
variant: {
|
||||
filled: null,
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
class: styles.filledDark,
|
||||
isDarkMode: true,
|
||||
variant: 'filled',
|
||||
},
|
||||
{
|
||||
class: styles.filledLight,
|
||||
isDarkMode: false,
|
||||
variant: 'filled',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 组件中
|
||||
const { isDarkMode } = useThemeMode();
|
||||
const className = variants({ isDarkMode, variant: 'filled' });
|
||||
```
|
||||
|
||||
## ⚠️ 无法优化的场景
|
||||
|
||||
### 1. JS 计算函数
|
||||
|
||||
**无法优化:**
|
||||
|
||||
- `chroma()` - 颜色计算库
|
||||
- `readableColor()` - 需要运行时计算(但可以用 token 替代)
|
||||
- `mix()` - 颜色混合计算
|
||||
- `calc()` 中使用 token 数值进行复杂计算
|
||||
|
||||
**示例:**
|
||||
|
||||
```typescript
|
||||
// ❌ 无法优化
|
||||
const scale = chroma.bezier([token.colorText, backgroundColor]).scale().colors(6);
|
||||
```
|
||||
|
||||
### 2. 复杂的动态 Props
|
||||
|
||||
**无法优化:**
|
||||
|
||||
- 需要复杂计算的 props
|
||||
- 对象 / 数组类型的 props
|
||||
- 函数类型的 props
|
||||
|
||||
### 3. useTheme Hook
|
||||
|
||||
**无法优化:**
|
||||
|
||||
- 直接使用 `useTheme()` hook 获取运行时值
|
||||
- 例如:`awesome/Giscus/style.ts` 使用 `useTheme()` 获取主题值
|
||||
|
||||
## 📋 迁移检查清单
|
||||
|
||||
### 样式文件检查
|
||||
|
||||
- [ ] `createStyles` → `createStaticStyles`
|
||||
- [ ] `token.xxx` → `cssVar.xxx`
|
||||
- [ ] 移除 `px` 后缀(`cssVar` 已包含单位)
|
||||
- [ ] `responsive.mobile` → `responsive.sm`(如果使用)
|
||||
- [ ] `stylish.xxx` → `lobeStaticStylish.xxx`(如果使用)
|
||||
- [ ] `rgba()` → `color-mix()`(如果使用)
|
||||
- [ ] `readableColor()` → token 替换(如果使用)
|
||||
- [ ] `prefixCls` 参数 → 硬编码 `const prefixCls = 'ant'`(如果使用)
|
||||
- [ ] `isDarkMode` → 静态样式拆分(如果使用)
|
||||
- [ ] 动态 props → CSS 变量(如果使用)
|
||||
|
||||
### 组件文件检查
|
||||
|
||||
- [ ] `useStyles()` → `import { styles } from './style'`
|
||||
- [ ] `import { cx } from 'antd-style'`(如果需要)
|
||||
- [ ] `import { useTheme } from 'antd-style'`(如果需要 `theme.isDarkMode`)
|
||||
- [ ] 动态 props → CSS 变量设置(如果使用)
|
||||
- [ ] `isDarkMode` 条件 → `theme.isDarkMode` 判断(如果使用)
|
||||
|
||||
## 🎯 优化优先级
|
||||
|
||||
### 高优先级(简单优化)
|
||||
|
||||
1. ✅ 纯静态样式(无动态 props)
|
||||
2. ✅ `isDarkMode` 拆分
|
||||
3. ✅ `responsive.mobile` → `responsive.sm`
|
||||
4. ✅ `stylish` → `lobeStaticStylish`
|
||||
5. ✅ `readableColor()` → token 替换
|
||||
|
||||
### 中优先级(需要转换)
|
||||
|
||||
6. ✅ 简单的动态 props → CSS 变量(1-2 个)
|
||||
7. ✅ 布尔值 props → 静态样式拆分(2-3 个)
|
||||
|
||||
### 低优先级(复杂优化)
|
||||
|
||||
8. ⚠️ 多个动态 props → CSS 变量(3+ 个)
|
||||
9. ⚠️ 复杂的条件逻辑拆分
|
||||
|
||||
## 📚 参考示例
|
||||
|
||||
### 完整示例 1: 简单组件
|
||||
|
||||
**样式文件:**
|
||||
|
||||
```typescript
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
export const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
root: css`
|
||||
padding: ${cssVar.padding};
|
||||
color: ${cssVar.colorText};
|
||||
border-radius: ${cssVar.borderRadius};
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**组件文件:**
|
||||
|
||||
```typescript
|
||||
import { cx } from 'antd-style';
|
||||
import { styles } from './style';
|
||||
|
||||
const Component = ({ className }) => {
|
||||
return <div className={cx(styles.root, className)} />;
|
||||
};
|
||||
```
|
||||
|
||||
### 完整示例 2: 带动态 Props
|
||||
|
||||
**样式文件:**
|
||||
|
||||
```typescript
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
export const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
root: css`
|
||||
width: var(--component-size, 24px);
|
||||
height: var(--component-size, 24px);
|
||||
background: ${cssVar.colorBgContainer};
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**组件文件:**
|
||||
|
||||
```typescript
|
||||
import { cx } from 'antd-style';
|
||||
import { useMemo } from 'react';
|
||||
import { styles } from './style';
|
||||
|
||||
const Component = ({ size = 24, className, style, ...rest }) => {
|
||||
const cssVariables = useMemo<Record<string, string>>(
|
||||
() => ({
|
||||
'--component-size': `${size}px`,
|
||||
}),
|
||||
[size],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(styles.root, className)}
|
||||
style={{
|
||||
...cssVariables,
|
||||
...style,
|
||||
}}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 完整示例 3: 带 isDarkMode
|
||||
|
||||
**样式文件:**
|
||||
|
||||
```typescript
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
export const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
rootDark: css`
|
||||
background: ${cssVar.colorFillTertiary};
|
||||
color: ${cssVar.colorTextLightSolid};
|
||||
`,
|
||||
rootLight: css`
|
||||
background: ${cssVar.colorFillQuaternary};
|
||||
color: ${cssVar.colorText};
|
||||
`,
|
||||
}));
|
||||
```
|
||||
|
||||
**组件文件:**
|
||||
|
||||
```typescript
|
||||
import { cx, useTheme } from 'antd-style';
|
||||
import { styles } from './style';
|
||||
|
||||
const Component = ({ className }) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
theme.isDarkMode ? styles.rootDark : styles.rootLight,
|
||||
className
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## 🔍 验证步骤
|
||||
|
||||
1. **类型检查:** `pnpm run type-check`
|
||||
2. **运行时测试:** 确保视觉效果一致
|
||||
3. **性能验证:** 检查样式计算是否在模块加载时完成
|
||||
|
||||
## 📊 优化效果
|
||||
|
||||
- ✅ **零运行时开销**:样式在模块加载时计算一次
|
||||
- ✅ **减少重新渲染**:组件不再依赖样式 hook
|
||||
- ✅ **更好的性能**:减少每次渲染的计算开销
|
||||
- ✅ **代码更简洁**:直接导入样式对象
|
||||
|
||||
## 🔧 场景 11: useTheme () → useThemeMode () /cssVar
|
||||
|
||||
**适用:** 组件中只使用 `theme.isDarkMode` 或其他 token 值
|
||||
|
||||
**规则:**
|
||||
|
||||
- 如果只使用 `theme.isDarkMode`,使用 `const { isDarkMode } = useThemeMode()` 替代
|
||||
- 如果使用其他 token(如 `theme.colorText`, `theme.borderRadius` 等),使用 `cssVar` 替代
|
||||
- `useThemeMode()` 比 `useTheme()` 更轻量,只返回 `isDarkMode` 值
|
||||
|
||||
**示例:**
|
||||
|
||||
**之前:**
|
||||
|
||||
```typescript
|
||||
import { useTheme } from 'antd-style';
|
||||
|
||||
const Component = () => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div className={theme.isDarkMode ? styles.dark : styles.light}>
|
||||
{theme.colorText}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**之后:**
|
||||
|
||||
```typescript
|
||||
import { cssVar, useThemeMode } from 'antd-style';
|
||||
|
||||
const Component = () => {
|
||||
const { isDarkMode } = useThemeMode();
|
||||
return (
|
||||
<div className={isDarkMode ? styles.dark : styles.light}>
|
||||
{cssVar.colorText}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**已优化示例:**
|
||||
|
||||
- `AuroraBackground`, `Select`, `Input`, `Button`, `DatePicker`, `AutoComplete`, `InputNumber`, `InputPassword`, `InputOPT`, `TextArea`, `SpotlightCardItem`, `Spotlight`, `HotkeyInput` - 只使用 `isDarkMode` → `useThemeMode()`
|
||||
- `Image`, `GradientButton`, `Empty`, `FileTypeIcon`, `FormSubmitFooter`, `CodeEditor`, `LobeChat`, `Drawer`, `Modal`, `Avatar`, `AvatarGroup`, `SkeletonAvatar`, `SkeletonButton`, `SkeletonTags`, `Callout`, `LobeHub`, `GridBackground`, `FolderIcon`, `FileIcon`, `TokenTag`, `ChatSendButton`, `AvatarUploader` - 使用 token → `cssVar`
|
||||
|
||||
**无法优化的文件(需要保留 `useTheme()`):**
|
||||
|
||||
- `useMermaid`, `useStreamMermaid`, `useHighlight`, `useStreamHighlight` - 需要完整的 theme 对象传给第三方库
|
||||
- `Alert`, `Tag`, `Menu`, `EmojiPicker` - 需要实际颜色值传给颜色计算函数
|
||||
- `SkeletonTitle`, `SkeletonTags` - 需要数值进行数学运算
|
||||
- `GridShowcase`, `GridBackground/demos` - 需要实际颜色值传给 `rgba()` 函数
|
||||
- `CustomFonts` - 需要实际字符串值进行字符串拼接
|
||||
- `Giscus/style.ts` - 需要实际颜色值传给 `readableColor()` 和 `rgba()` 函数(其他 token 已优化为 `cssVar`)
|
||||
|
||||
**注意事项:**
|
||||
|
||||
- `useThemeMode()` 只返回 `{ isDarkMode }`,不返回完整的 theme 对象
|
||||
- `cssVar` 的值是字符串(如 `"14px"`, `"#ffffff"`),可以直接在 JSX 中使用
|
||||
- 如果 token 需要用于数值计算(如 `Math.round(theme.fontSize * 1.5)`),需要保留 `useTheme()`
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
`createStaticStyles` 迁移是一个渐进式的优化过程。对于简单的静态样式,可以直接转换;对于复杂的动态场景,需要根据具体情况选择合适的优化策略。关键是要理解每种场景的处理方式,并灵活运用 CSS 变量、静态样式拆分等技术。
|
||||
|
||||
### useTheme () 优化总结
|
||||
|
||||
- ✅ **使用 `useThemeMode()`**:当组件只使用 `theme.isDarkMode` 时
|
||||
- ✅ **使用 `cssVar`**:当组件使用其他 token 值(颜色、尺寸等)时
|
||||
- ⚠️ **保留 `useTheme()`**:当 token 需要用于数值计算或传给第三方库时
|
||||
+1
-2
@@ -74,12 +74,11 @@ export default defineConfig({
|
||||
define: {
|
||||
'process.env': process.env,
|
||||
},
|
||||
exportStatic: {},
|
||||
extraBabelPlugins: ['babel-plugin-antd-style'],
|
||||
favicons: ['https://lobehub.com/favicon.ico'],
|
||||
jsMinifier: 'swc',
|
||||
locales: [{ id: 'en-US', name: 'English' }],
|
||||
mako: isWin || isProduction ? false : {},
|
||||
mfsu: isWin ? false : {},
|
||||
npmClient: 'pnpm',
|
||||
publicPath: '/',
|
||||
resolve: isProduction
|
||||
|
||||
+4
-4
@@ -85,7 +85,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"antd-style": "^4.0.0",
|
||||
"antd-style": "^4.1.0",
|
||||
"lucide-react": "^0.469.0",
|
||||
"polished": "^4.3.1"
|
||||
},
|
||||
@@ -93,7 +93,7 @@
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@commitlint/cli": "^19.8.1",
|
||||
"@lobehub/lint": "^1.26.3",
|
||||
"@lobehub/ui": "^4.1.2",
|
||||
"@lobehub/ui": "^4.3.3",
|
||||
"@testing-library/react": "^16.3.1",
|
||||
"@types/pangu": "^4.0.2",
|
||||
"@types/react": "^19.2.7",
|
||||
@@ -108,7 +108,7 @@
|
||||
"dotenv": "^17.2.3",
|
||||
"dpdm": "^3.14.0",
|
||||
"dumi": "^2.4.21",
|
||||
"dumi-theme-lobehub": "^4.0.0",
|
||||
"dumi-theme-lobehub": "^4.0.1",
|
||||
"es-toolkit": "^1.43.0",
|
||||
"eslint": "^8.57.1",
|
||||
"father": "^4.6.11",
|
||||
@@ -134,7 +134,7 @@
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@lobehub/ui": "^4.0.0",
|
||||
"@lobehub/ui": "^4.3.3",
|
||||
"antd": "^6.1.1",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { CopyButton, Flexbox, Tooltip } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles, cx } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
const useStyles = createStyles(({ css, token, cx }) => {
|
||||
const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
return {
|
||||
btn: cx(
|
||||
'copy-button',
|
||||
@@ -20,10 +20,10 @@ const useStyles = createStyles(({ css, token, cx }) => {
|
||||
|
||||
width: 98px;
|
||||
height: 98px;
|
||||
border: 1px solid ${token.colorBorder};
|
||||
border-radius: ${token.borderRadius}px;
|
||||
border: 1px solid ${cssVar.colorBorder};
|
||||
border-radius: ${cssVar.borderRadius};
|
||||
|
||||
background: ${token.colorBgContainer};
|
||||
background: ${cssVar.colorBgContainer};
|
||||
|
||||
&:hover {
|
||||
.copy-button {
|
||||
@@ -39,8 +39,6 @@ export interface IconPreviewProps {
|
||||
}
|
||||
|
||||
const IconPreview = memo<IconPreviewProps>(({ color }) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
return (
|
||||
<Tooltip title={color}>
|
||||
<Flexbox
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { ActionIcon, Block, Center, CopyButton, Flexbox, Text } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles, cx } from 'antd-style';
|
||||
import { Link } from 'dumi';
|
||||
import { DownloadIcon, SearchIcon } from 'lucide-react';
|
||||
import { readableColor } from 'polished';
|
||||
@@ -9,7 +9,7 @@ import { ReactNode, memo, useCallback, useRef } from 'react';
|
||||
|
||||
import { customKebabCase } from '@/components/Dashboard/utils';
|
||||
|
||||
const useStyles = createStyles(({ cx, css, token }) => {
|
||||
const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
const colorText = cx(
|
||||
'color-text',
|
||||
css`
|
||||
@@ -29,8 +29,8 @@ const useStyles = createStyles(({ cx, css, token }) => {
|
||||
overflow: hidden;
|
||||
`,
|
||||
color: css`
|
||||
border-inline-end: 1px solid ${token.colorBorderSecondary};
|
||||
font-family: ${token.fontFamilyCode};
|
||||
border-inline-end: 1px solid ${cssVar.colorBorderSecondary};
|
||||
font-family: ${cssVar.fontFamilyCode};
|
||||
|
||||
&:hover {
|
||||
.color-text {
|
||||
@@ -45,7 +45,7 @@ const useStyles = createStyles(({ cx, css, token }) => {
|
||||
colorText,
|
||||
copy,
|
||||
row: css`
|
||||
border-block-start: 1px solid ${token.colorFillSecondary};
|
||||
border-block-start: 1px solid ${cssVar.colorFillSecondary};
|
||||
`,
|
||||
title: css`
|
||||
margin: 0;
|
||||
@@ -72,7 +72,6 @@ interface IconItemProps {
|
||||
}
|
||||
|
||||
const IconItem = memo<IconItemProps>(({ children, title, color, id }) => {
|
||||
const { styles } = useStyles();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleDownload = useCallback((iconId: string) => {
|
||||
|
||||
@@ -3,24 +3,23 @@
|
||||
import * as Icons from '@lobehub/icons';
|
||||
import { Flexbox } from '@lobehub/ui';
|
||||
import { StoryBook, useControls, useCreateStore } from '@lobehub/ui/storybook';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
import IconPreview from '@/components/IconPreview';
|
||||
|
||||
const data = Object.values(Icons).filter((icon: any) => icon?.colorPrimary);
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
item: css`
|
||||
height: 96px;
|
||||
padding: 16px;
|
||||
border: none;
|
||||
background: ${token.colorBgContainer};
|
||||
background: ${cssVar.colorBgContainer};
|
||||
`,
|
||||
}));
|
||||
|
||||
const Dashboard = memo<{ className: string }>(({ className }) => {
|
||||
const { styles } = useStyles();
|
||||
const store = useCreateStore();
|
||||
|
||||
const { size, color } = useControls(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import * as Icons from '@lobehub/icons';
|
||||
import { Flexbox, Grid, SearchBar } from '@lobehub/ui';
|
||||
import { Empty, Segmented } from 'antd';
|
||||
import { useTheme } from 'antd-style';
|
||||
import { cssVar } from 'antd-style';
|
||||
import { memo, useMemo, useState } from 'react';
|
||||
|
||||
import IconItem from './IconItem';
|
||||
@@ -14,7 +14,6 @@ enum ColorType {
|
||||
}
|
||||
|
||||
const Dashboard = memo(() => {
|
||||
const theme = useTheme();
|
||||
const [type, setType] = useState<ColorType>(ColorType.Color);
|
||||
const [keyword, setKeyword] = useState<string>();
|
||||
|
||||
@@ -60,7 +59,7 @@ const Dashboard = memo(() => {
|
||||
},
|
||||
]}
|
||||
style={{
|
||||
border: `1px solid ${theme.colorBorder}`,
|
||||
border: `1px solid ${cssVar.colorBorder}`,
|
||||
}}
|
||||
/>
|
||||
</Flexbox>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { Flexbox, Highlighter } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
const useStyles = createStyles(({ css }) => {
|
||||
const styles = createStaticStyles(({ css }) => {
|
||||
return {
|
||||
code: css`
|
||||
pre {
|
||||
@@ -27,7 +27,6 @@ interface PreviewProps {
|
||||
}
|
||||
|
||||
const Preview = memo<PreviewProps>(({ svg, title, viewbox, textMode }) => {
|
||||
const { styles } = useStyles();
|
||||
const sizeAttributes = textMode
|
||||
? `height={size}
|
||||
style={{ flex: 'none', lineHeight: 1, ...style }}`
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { Flexbox, Highlighter } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
const useStyles = createStyles(({ css }) => {
|
||||
const styles = createStaticStyles(({ css }) => {
|
||||
return {
|
||||
code: css`
|
||||
pre {
|
||||
@@ -26,8 +26,6 @@ interface PreviewProps {
|
||||
}
|
||||
|
||||
const Preview = memo<PreviewProps>(({ svg, title, viewbox }) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
return (
|
||||
<Flexbox flex={'none'} gap={16} style={{ position: 'relative', width: '100%' }}>
|
||||
<Flexbox align={'center'} gap={8} horizontal>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Flexbox, Highlighter, Tag } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { Ref, memo } from 'react';
|
||||
|
||||
import IconPreview from '@/components/IconPreview';
|
||||
|
||||
const useStyles = createStyles(({ css }) => {
|
||||
const styles = createStaticStyles(({ css }) => {
|
||||
return {
|
||||
code: css`
|
||||
flex: 1;
|
||||
@@ -30,8 +30,6 @@ interface PreviewProps {
|
||||
}
|
||||
|
||||
const Preview = memo<PreviewProps>(({ svg, title, precent, ref }) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
return (
|
||||
<Flexbox flex={'none'} gap={16} style={{ position: 'relative', width: '100%' }}>
|
||||
<Flexbox align={'center'} gap={8} horizontal>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { Flexbox, Highlighter } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
const useStyles = createStyles(({ css }) => {
|
||||
const styles = createStaticStyles(({ css }) => {
|
||||
return {
|
||||
code: css`
|
||||
pre {
|
||||
@@ -26,8 +26,6 @@ interface PreviewProps {
|
||||
}
|
||||
|
||||
const Preview = memo<PreviewProps>(({ svg, title, viewbox }) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
return (
|
||||
<Flexbox flex={'none'} gap={16} style={{ position: 'relative', width: '100%' }}>
|
||||
<Flexbox align={'center'} gap={8} horizontal>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Flexbox } from '@lobehub/ui';
|
||||
import { StoryBook, useControls, useCreateStore } from '@lobehub/ui/storybook';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useSvgo } from '@/components/Editor/useSvgo';
|
||||
@@ -12,11 +12,11 @@ import Text from './Text';
|
||||
import { svgIcon } from './data';
|
||||
import { defaultPlugins } from './svgo';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => {
|
||||
const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
return {
|
||||
container: css`
|
||||
border: 1px solid ${token.colorBorder};
|
||||
border-radius: ${token.borderRadius}px;
|
||||
border: 1px solid ${cssVar.colorBorder};
|
||||
border-radius: ${cssVar.borderRadius};
|
||||
`,
|
||||
};
|
||||
});
|
||||
@@ -26,7 +26,6 @@ export default () => {
|
||||
const [viewbox, setViewbox] = useState<string>('0 0 24 24');
|
||||
const [colorSvg, setColorSvg] = useState<string>('');
|
||||
const [monoSvg, setMonoSvg] = useState<string>('');
|
||||
const { styles } = useStyles();
|
||||
const levaStore = useCreateStore();
|
||||
const { svg, text } = useControls(
|
||||
{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { CopyButton, Flexbox } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles, cx } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
const useStyles = createStyles(({ css, token, cx }) => {
|
||||
const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
return {
|
||||
btn: cx(
|
||||
'copy-button',
|
||||
@@ -22,13 +22,13 @@ const useStyles = createStyles(({ css, token, cx }) => {
|
||||
|
||||
width: 98px;
|
||||
height: 98px;
|
||||
border: 1px solid ${token.colorBorder};
|
||||
border-radius: ${token.borderRadius}px;
|
||||
border: 1px solid ${cssVar.colorBorder};
|
||||
border-radius: ${cssVar.borderRadius};
|
||||
|
||||
font-family: ${token.fontFamilyCode};
|
||||
font-family: ${cssVar.fontFamilyCode};
|
||||
line-height: 1;
|
||||
|
||||
background: ${token.colorBgContainer};
|
||||
background: ${cssVar.colorBgContainer};
|
||||
|
||||
&:hover {
|
||||
.copy-button {
|
||||
@@ -44,8 +44,6 @@ export interface IconPreviewProps {
|
||||
}
|
||||
|
||||
const IconPreview = memo<IconPreviewProps>(({ color }) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
return (
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Flexbox, FlexboxProps } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles, cx } from 'antd-style';
|
||||
import { ReactNode, Ref, memo, useRef } from 'react';
|
||||
|
||||
import DownloadButton from '@/components/DownloadButton';
|
||||
|
||||
const useStyles = createStyles(({ css, token, cx }) => {
|
||||
const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
return {
|
||||
btn: cx(
|
||||
'copy-button',
|
||||
@@ -21,12 +21,12 @@ const useStyles = createStyles(({ css, token, cx }) => {
|
||||
flex: none;
|
||||
|
||||
padding: 12px;
|
||||
border: 1px solid ${token.colorBorder};
|
||||
border-radius: ${token.borderRadius}px;
|
||||
border: 1px solid ${cssVar.colorBorder};
|
||||
border-radius: ${cssVar.borderRadius};
|
||||
|
||||
line-height: 1;
|
||||
|
||||
background: ${token.colorBgContainer};
|
||||
background: ${cssVar.colorBgContainer};
|
||||
|
||||
&:hover {
|
||||
.copy-button {
|
||||
@@ -56,7 +56,6 @@ export interface IconPreviewProps extends FlexboxProps {
|
||||
}
|
||||
|
||||
const IconPreview = memo<IconPreviewProps>(({ className, children, ...rest }) => {
|
||||
const { cx, styles } = useStyles();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const isString = typeof children === 'string';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Center } from '@lobehub/ui';
|
||||
import { useTheme } from 'antd-style';
|
||||
import { cssVar } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { IconAvatarProps } from '@/features/IconAvatar';
|
||||
@@ -17,12 +17,11 @@ const DefaultAvatar = memo<Omit<IconAvatarProps, 'Icon'>>(
|
||||
iconStyle,
|
||||
iconClassName,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Center
|
||||
flex={'none'}
|
||||
style={{
|
||||
background: background || theme.colorFillSecondary,
|
||||
background: background || cssVar.colorFillSecondary,
|
||||
borderRadius: shape === 'circle' ? '50%' : Math.floor(size * 0.1),
|
||||
color,
|
||||
height: size,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Icon } from '@lobehub/ui';
|
||||
import { useTheme } from 'antd-style';
|
||||
import { cssVar } from 'antd-style';
|
||||
import { Brain } from 'lucide-react';
|
||||
import { CSSProperties, memo } from 'react';
|
||||
|
||||
@@ -11,8 +11,7 @@ interface DefaultIconProps {
|
||||
}
|
||||
|
||||
const DefaultAvatar = memo<DefaultIconProps>(({ color, size = 12, ...rest }) => {
|
||||
const theme = useTheme();
|
||||
return <Icon color={color || theme.colorTextDescription} icon={Brain} size={size} {...rest} />;
|
||||
return <Icon color={color || cssVar.colorTextDescription} icon={Brain} size={size} {...rest} />;
|
||||
});
|
||||
|
||||
export default DefaultAvatar;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Center } from '@lobehub/ui';
|
||||
import { useTheme } from 'antd-style';
|
||||
import { cssVar } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
import type { IconAvatarProps } from '../IconAvatar';
|
||||
@@ -16,12 +16,11 @@ const DefaultAvatar = memo<Omit<IconAvatarProps, 'Icon'>>(
|
||||
iconStyle,
|
||||
iconClassName,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Center
|
||||
flex={'none'}
|
||||
style={{
|
||||
background: background || theme.colorFillSecondary,
|
||||
background: background || cssVar.colorFillSecondary,
|
||||
borderRadius: shape === 'circle' ? '50%' : Math.floor(size * 0.1),
|
||||
color,
|
||||
height: size,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Icon } from '@lobehub/ui';
|
||||
import { useTheme } from 'antd-style';
|
||||
import { cssVar } from 'antd-style';
|
||||
import { Radar } from 'lucide-react';
|
||||
import { type CSSProperties, memo } from 'react';
|
||||
|
||||
@@ -11,8 +11,7 @@ interface DefaultIconProps {
|
||||
}
|
||||
|
||||
const DefaultIcon = memo<DefaultIconProps>(({ color, size = 12, ...rest }) => {
|
||||
const theme = useTheme();
|
||||
return <Icon color={color || theme.colorTextDescription} icon={Radar} size={size} {...rest} />;
|
||||
return <Icon color={color || cssVar.colorTextDescription} icon={Radar} size={size} {...rest} />;
|
||||
});
|
||||
|
||||
export default DefaultIcon;
|
||||
|
||||
Reference in New Issue
Block a user