Skip to content

Commit 94f2329

Browse files
authored
Merge pull request #5 from ant-design/init
Feat: Add new API `genStyleUtils`.
2 parents f1abb45 + 71705e5 commit 94f2329

30 files changed

+1430
-10
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# @ant-design/cssinjs-util
1+
# @ant-design/cssinjs-utils
22
A cssinjs util library to support Ant Design (antd) and its ecosystem libraries.
33

44
## Install
55
``` bash
6-
npm i @ant-design/cssinjs-util --save
6+
npm i @ant-design/cssinjs-utils --save
77
```
88

99
## Usage

docs/demos/genStyleUtils.md

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
title: genStyleUtils
3+
nav:
4+
title: API
5+
path: /genStyleUtils
6+
---
7+
# `genStyleUtils` 使用文档
8+
9+
`genStyleUtils` 提供了用于在 `antd` 生态开发中,生成和管理样式的实用工具函数集。
10+
11+
## 入参介绍
12+
13+
### `genStyleUtils<CompTokenMap>(getConfigProviderContext?, getThemeProviderContext?)`
14+
- `config`: 可选,配置
15+
- `useCSP`: 使用 CSP 的钩子函数
16+
- `usePrefix`: 使用样式前缀的钩子函数
17+
- `useToken`: 使用 token 的钩子函数
18+
- `CompTokenMap`: 范型参数,表示组件 token 映射
19+
- `DesignTokenn`: 范型参数,表示设计 token
20+
- `AliasToken`: 范型参数,表示别名 token
21+
> 使用建议:为了更好的获得 TS 类型支持,建议您在使用 `genStyleUtils` 的时候传入范型参数 `CompTokenMap` `DesignTokenn` `AliasToken`
22+
23+
## 如何使用
24+
``` typescript
25+
import React from 'react';
26+
import { genStyleUtils } from '@ant-design/cssinjs-utils';
27+
28+
// Step1: 定义组件 Token 映射
29+
interface YourCompTokenMap {
30+
Button?: {};
31+
Avatar?: {};
32+
// ...
33+
}
34+
35+
// Step2: 定义设计 Token
36+
interface YourDesignTokenn {
37+
color?: string;
38+
}
39+
40+
// Step3: 定义别名 Token
41+
interface YourAliasToken {
42+
colorFillContentHover?: string;
43+
}
44+
45+
// Step4: 使用 `genStyleUtils` 生成工具函数集
46+
const {
47+
genStyleHooks,
48+
genComponentStyleHook,
49+
genSubStyleComponent,
50+
} = genStyleUtils<YourCompTokenMap, YourDesignTokenn, YourAliasToken>({
51+
useCSP: () => {
52+
// ...
53+
},
54+
usePrefix: () => {
55+
// ...
56+
},
57+
useToken: () => {
58+
// ...
59+
},
60+
});
61+
```
62+
63+
## 工具介绍
64+
65+
### `genStyleHooks(component, styleFn, getDefaultToken?, options?)`
66+
67+
- `component`: 组件名称 `ComponentName` 或组件名称数组 `[ComponentName, ComponentName]`
68+
- `styleFn`: 根据标记和样式信息生成 CSS 插值的函数。
69+
- `getDefaultToken`: 可选,用于检索默认标记的函数或值。
70+
- `options`: 可选,包含额外的配置选项如 `resetStyle``resetFont``deprecatedTokens``clientOnly` 等。
71+
72+
### `genComponentStyleHook(component, styleFn, getDefaultToken?, options?)`
73+
74+
- `component`: 组件名称 `ComponentName` 或组件名称数组 `[ComponentName, ComponentName]`
75+
- `styleFn`: 根据标记和样式信息生成 CSS 插值的函数。
76+
- `getDefaultToken`: 可选,用于检索默认标记的函数或值。
77+
- `options`: 可选,包含额外的配置选项如 `resetStyle``resetFont``deprecatedTokens``clientOnly` 等。
78+
79+
### `genSubStyleComponent(component, styleFn, getDefaultToken?, options?)`
80+
81+
- `component`: 组件名称 `ComponentName` 或组件名称数组 `[ComponentName, ComponentName]`
82+
- `styleFn`: 根据标记和样式信息生成 CSS 插值的函数。
83+
- `getDefaultToken`: 可选,用于检索默认标记的函数或值。
84+
- `options`: 可选,包含额外的配置选项如 `resetStyle``resetFont``deprecatedTokens``clientOnly` 等。
85+
86+
## 示例用法
87+
88+
### `genStyleHooks`
89+
90+
```javascript
91+
const useStyle = genStyleHooks('Button', styleFn, getDefaultToken, { resetStyle: true });
92+
const [wrapStyle, hashId] = useStyle('button');
93+
```
94+
95+
### `genComponentStyleHook`
96+
97+
```javascript
98+
const useStyle = genComponentStyleHook('Button', styleFn, getDefaultToken, { clientOnly: true });
99+
const [wrapStyle, hashId] = useStyle('button');
100+
```
101+
102+
### `genSubStyleComponent`
103+
104+
```javascript
105+
const SubButtonStyle = genSubStyleComponent('Button', styleFn, getDefaultToken, { resetFont: true });
106+
107+
() => <SubButtonStyle prefixCls="sub-button" />;
108+
```

docs/examples/.gitkeep

Whitespace-only changes.

jest.config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
module.exports = {
2-
setupFiles: ['./tests/setup.js'],
2+
testEnvironment: 'jsdom',
3+
setupFiles: ['./tests/setup.ts'],
34
};

package.json

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "@ant-design/cssinjs-util",
2+
"name": "@ant-design/cssinjs-utils",
33
"version": "1.0.0",
44
"description": "A cssinjs util library to support Ant Design (antd) and its ecosystem libraries.",
55
"keywords": [
@@ -30,15 +30,14 @@
3030
"start": "dumi dev",
3131
"compile": "father build",
3232
"prepublishOnly": "npm run compile && np --yolo --no-publish",
33-
"lint": "eslint src/ docs/examples/ --ext .tsx,.ts,.jsx,.js",
33+
"lint": "eslint src/ --ext .tsx,.ts,.jsx,.js",
3434
"test": "rc-test",
3535
"coverage": "rc-test --coverage"
3636
},
3737
"devDependencies": {
3838
"@testing-library/jest-dom": "^6.1.4",
3939
"@rc-component/father-plugin": "^1.0.1",
4040
"@testing-library/react": "^15.0.4",
41-
"@types/classnames": "^2.2.10",
4241
"@types/jest": "^29.5.2",
4342
"@types/node": "^20.11.6",
4443
"@types/react": "^18.0.0",
@@ -57,9 +56,8 @@
5756
"typescript": "^5.1.6"
5857
},
5958
"dependencies": {
60-
"@babel/runtime": "^7.23.2",
6159
"@ant-design/cssinjs": "^1.21.0",
62-
"classnames": "^2.3.2",
60+
"@babel/runtime": "^7.23.2",
6361
"rc-util": "^5.38.0"
6462
},
6563
"peerDependencies": {

src/_util/hooks/useUniqueMemo.tsx

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import React from 'react';
2+
3+
const BEAT_LIMIT = 1000 * 60 * 10;
4+
5+
/**
6+
* A helper class to map keys to values.
7+
* It supports both primitive keys and object keys.
8+
*/
9+
class ArrayKeyMap<T> {
10+
map = new Map<string, T>();
11+
12+
// Use WeakMap to avoid memory leak
13+
objectIDMap = new WeakMap<object, number>();
14+
15+
nextID = 0;
16+
17+
lastAccessBeat = new Map<string, number>();
18+
19+
// We will clean up the cache when reach the limit
20+
accessBeat = 0;
21+
22+
set(keys: React.DependencyList, value: any) {
23+
// New set will trigger clear
24+
this.clear();
25+
26+
// Set logic
27+
const compositeKey = this.getCompositeKey(keys);
28+
this.map.set(compositeKey, value);
29+
this.lastAccessBeat.set(compositeKey, Date.now());
30+
}
31+
32+
get(keys: React.DependencyList) {
33+
const compositeKey = this.getCompositeKey(keys);
34+
35+
const cache = this.map.get(compositeKey);
36+
this.lastAccessBeat.set(compositeKey, Date.now());
37+
this.accessBeat += 1;
38+
39+
return cache;
40+
}
41+
42+
getCompositeKey(keys: React.DependencyList) {
43+
const ids = keys.map<string>((key) => {
44+
if (key && typeof key === 'object') {
45+
return `obj_${this.getObjectID(key)}`;
46+
}
47+
return `${typeof key}_${key}`;
48+
});
49+
return ids.join('|');
50+
}
51+
52+
getObjectID(obj: object) {
53+
if (this.objectIDMap.has(obj)) {
54+
return this.objectIDMap.get(obj);
55+
}
56+
const id = this.nextID;
57+
this.objectIDMap.set(obj, id);
58+
59+
this.nextID += 1;
60+
61+
return id;
62+
}
63+
64+
clear() {
65+
if (this.accessBeat > 10000) {
66+
const now = Date.now();
67+
68+
this.lastAccessBeat.forEach((beat, key) => {
69+
if (now - beat > BEAT_LIMIT) {
70+
this.map.delete(key);
71+
this.lastAccessBeat.delete(key);
72+
}
73+
});
74+
75+
this.accessBeat = 0;
76+
}
77+
}
78+
}
79+
80+
const uniqueMap = new ArrayKeyMap();
81+
82+
/**
83+
* Like `useMemo`, but this hook result will be shared across all instances.
84+
*/
85+
function useUniqueMemo<T>(memoFn: () => T, deps: React.DependencyList) {
86+
return React.useMemo<T>(() => {
87+
const cachedValue = uniqueMap.get(deps);
88+
if (cachedValue) {
89+
return cachedValue as T;
90+
}
91+
const newValue = memoFn();
92+
uniqueMap.set(deps, newValue);
93+
return newValue;
94+
}, deps);
95+
}
96+
97+
export default useUniqueMemo;

src/hooks/useCSP.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export type UseCSP = () => {
2+
nonce?: string;
3+
};
4+
5+
/**
6+
* Provide a default hook since not everyone need config this.
7+
*/
8+
const useDefaultCSP: UseCSP = () => ({});
9+
10+
export default useDefaultCSP;

src/hooks/usePrefix.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export type UsePrefix = () => {
2+
/**
3+
* All the component use `@ant-design/cssinjs-utils` should have same `rootPrefixCls`.
4+
*/
5+
rootPrefixCls: string;
6+
/**
7+
* `iconPrefixCls` comes from the setting of `@ant-design/icons`.
8+
* Here maybe little coupling but everyone need use this.
9+
*/
10+
iconPrefixCls: string;
11+
};

src/hooks/useToken.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { Theme, TokenType } from '@ant-design/cssinjs';
2+
3+
import type { OverrideTokenMap, TokenMap } from '../interface';
4+
5+
export type TokenMapWithTheme<
6+
CompTokenMap extends TokenMap,
7+
DesignToken extends TokenType,
8+
AliasToken extends TokenType,
9+
> = {
10+
[key in keyof OverrideTokenMap<CompTokenMap>]?: OverrideTokenMap<CompTokenMap>[key] & {
11+
theme?: Theme<DesignToken, AliasToken>;
12+
};
13+
};
14+
15+
export interface UseTokenReturn<
16+
CompTokenMap extends TokenMap,
17+
DesignToken extends TokenType,
18+
AliasToken extends TokenType,
19+
> {
20+
token: OverrideTokenMap<CompTokenMap>;
21+
realToken?: OverrideTokenMap<CompTokenMap>;
22+
/** Just merge `token` & `override` at top to save perf */
23+
override: { override: OverrideTokenMap<CompTokenMap> };
24+
theme?: Theme<DesignToken, AliasToken>;
25+
components?: TokenMapWithTheme<CompTokenMap, DesignToken, AliasToken>;
26+
hashId?: string;
27+
hashed?: string | boolean;
28+
cssVar?: {
29+
prefix?: string;
30+
key?: string;
31+
};
32+
}
33+
34+
export type UseToken<
35+
CompTokenMap extends TokenMap,
36+
DesignToken extends TokenType,
37+
AliasToken extends TokenType,
38+
> = () => UseTokenReturn<CompTokenMap, DesignToken, AliasToken>;

src/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
console.log('Hello world!');
1+
export { default as genStyleUtils } from './util/genStyleUtils';
2+
3+
export { default as genCalc } from './util/calc';
4+
export type { default as AbstractCalculator } from './util/calc/calculator';

src/interface/.gitkeep

Whitespace-only changes.

src/interface/components.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { TokenType } from '@ant-design/cssinjs';
2+
3+
export type TokenMap = Record<string, TokenType>;
4+
5+
export type TokenMapKey<CompTokenMap extends TokenMap> = Extract<keyof CompTokenMap, string>;
6+
7+
export type OverrideTokenMap<CompTokenMap extends TokenMap> = Partial<CompTokenMap>;
8+
9+
export type GlobalTokenWithComponent<CompTokenMap extends TokenMap, C extends TokenMapKey<CompTokenMap>> = CompTokenMap &
10+
CompTokenMap[C];
11+
12+
export type ComponentToken<CompTokenMap extends TokenMap, C extends TokenMapKey<CompTokenMap>> = Exclude<OverrideTokenMap<CompTokenMap>[C], undefined>;
13+
14+
export type ComponentTokenKey<CompTokenMap extends TokenMap, C extends TokenMapKey<CompTokenMap>> = keyof ComponentToken<CompTokenMap, C>;

src/interface/index.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export type {
2+
OverrideTokenMap,
3+
TokenMap,
4+
TokenMapKey,
5+
GlobalTokenWithComponent,
6+
ComponentToken,
7+
ComponentTokenKey,
8+
} from './components';
9+
10+
export type UseComponentStyleResult = [(node: React.ReactNode) => React.ReactElement, string];

0 commit comments

Comments
 (0)