Skip to content

Commit

Permalink
✨ feat: support iconPrefixCls and prefixCls on createStyles (#125)
Browse files Browse the repository at this point in the history
* ✨ feat: support iconPrefixCls and prefixCls on createStyles

* 📸 test: update snapshot

* ✅ test: fix test
  • Loading branch information
arvinxx committed Dec 8, 2023
1 parent 6f83860 commit f2e72a3
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 33 deletions.
24 changes: 15 additions & 9 deletions docs/api/create-styles.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,19 +192,25 @@ Type: `string`
The prefix marked on the ThemeProvider. By using this parameter, you can flexibly override the antd prefix.

```ts
const useStyles = createStyles(({ css, prefixCls }) => {
return {
primary: css`
.${prefixCls}-btn {
border: 12px;
}
`,
};
});
const useStyles = createStyles(({ css, prefixCls, iconPrefixCls }) => ({
button: css`
&.${prefixCls}-btn {
background: lightsteelblue;
border: none;
color: royalblue;
}
.${iconPrefixCls} {
color: darkblue;
}
`,
}));
```

The above style code can accurately override regardless of the value of the prefixCls wrapped by the outer ThemeProvider.

<code src="../demos/api/createStyles/with-antd-cp.tsx"></code>

## ClassNameGeneratorOption

The second parameter of `createStyles` can be used to control the generated className.
Expand Down
30 changes: 18 additions & 12 deletions docs/api/create-styles.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,25 +185,31 @@ declare module 'antd-style' {

判断亮暗色主题的语法糖,实现上等价于 `appearance === 'dark'`,直接使用 isDarkMode 可以降低外观的判断成本。

### prefixCls
### prefixCls 与 iconPrefixCls

类型:`string`

在 ThemeProvider 上标记的 prefixCls,利用该参数,可以实现灵活的 antd 前缀覆盖。
在 ThemeProvider 上标记的 prefixCls/iconPrefixCls,利用该参数,可以实现灵活的 antd 前缀覆盖。

```ts
const useStyles = createStyles(({ css, prefixCls }) => {
return {
primary: css`
.${prefixCls}-btn {
border: 12px;
}
`,
};
});
const useStyles = createStyles(({ css, prefixCls, iconPrefixCls }) => ({
button: css`
&.${prefixCls}-btn {
background: lightsteelblue;
border: none;
color: royalblue;
}
.${iconPrefixCls} {
color: darkblue;
}
`,
}));
```

上述样式代码,无论外层包裹的 ThemeProvider prefixCls 改成什么值,均可准确覆盖到。
上述样式代码,无论是包裹的 `ThemeProvider` / `ConfigProvider` prefixCls 改成什么值,均可准确覆盖到。

<code src="../demos/api/createStyles/with-antd-cp.tsx"></code>

## ClassNameGeneratorOption

Expand Down
32 changes: 32 additions & 0 deletions docs/demos/api/createStyles/with-antd-cp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { SmileOutlined } from '@ant-design/icons';
import { Button, ConfigProvider } from 'antd';
import { createStyles } from 'antd-style';

const useStyles = createStyles(({ css, prefixCls, iconPrefixCls }) => ({
button: css`
&.${prefixCls}-btn {
background: lightsteelblue;
border: none;
color: royalblue;
}
.${iconPrefixCls} {
color: darkblue;
}
`,
}));

const App = () => {
const { styles } = useStyles();

return (
<Button className={styles.button} icon={<SmileOutlined />}>
CP Button
</Button>
);
};
export default () => (
<ConfigProvider prefixCls={'cp'} iconPrefixCls={'cpicon'}>
<App />
</ConfigProvider>
);
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"@types/react-dom": "^18",
"@types/testing-library__jest-dom": "^5",
"@umijs/lint": "^4",
"@vitest/coverage-v8": "latest",
"@vitest/coverage-v8": "0.34.6",
"antd": "^5",
"babel-plugin-antd-style": "^1",
"chalk": "^4",
Expand Down Expand Up @@ -133,7 +133,7 @@
"ts-node": "^10",
"typescript": "^5",
"vite": "^4.5.0",
"vitest": "latest",
"vitest": "0.34.6",
"zustand": "^4"
},
"peerDependencies": {
Expand Down
7 changes: 4 additions & 3 deletions src/factories/createStyles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const createStylesFactory =

// 函数场景
if (styleOrGetStyle instanceof Function) {
const { stylish, appearance, isDarkMode, prefixCls, ...token } = theme;
const { stylish, appearance, isDarkMode, prefixCls, iconPrefixCls, ...token } = theme;

// 创建响应式断点选择器的工具函数
// @ts-ignore
Expand All @@ -73,6 +73,7 @@ export const createStylesFactory =
appearance,
isDarkMode,
prefixCls,
iconPrefixCls,
// 工具函数们
cx,
css: serializeCSS,
Expand Down Expand Up @@ -121,8 +122,8 @@ export const createStylesFactory =
}, [props, theme]);

return useMemo(() => {
const { prefixCls, ...res } = theme;
return { styles, cx, theme: res, prefixCls };
const { prefixCls, iconPrefixCls, ...res } = theme;
return { styles, cx, theme: res, prefixCls, iconPrefixCls };
}, [styles, theme]);
};
};
2 changes: 2 additions & 0 deletions src/factories/createStyles/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface CreateStylesUtils extends CommonStyleUtils {
* @default ant
*/
prefixCls: string;
iconPrefixCls: string;
}

/**
Expand All @@ -39,6 +40,7 @@ export interface CreateStylesUtils extends CommonStyleUtils {
export interface ReturnStyles<T extends BaseReturnType> extends Pick<CommonStyleUtils, 'cx'> {
styles: ReturnStyleToUse<T>;
theme: Omit<Theme, 'prefixCls'>;
iconPrefixCls: string;
prefixCls: string;
}

Expand Down
2 changes: 1 addition & 1 deletion src/factories/createThemeProvider/TokenContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const TokenContainer: <T, S>(props: TokenContainerProps<T, S>) => ReactElement |
customToken: customTokenOrFn,
defaultCustomToken: defaultCustomTokenFn,
customStylish: stylishOrGetStylish,
prefixCls = 'ant',
prefixCls,
StyledThemeProvider,
}) => {
const themeState = useThemeMode();
Expand Down
1 change: 1 addition & 0 deletions src/factories/createThemeProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export const createThemeProvider = (
onThemeModeChange,
styled,
}) => {
// 从上一层的 Context 中获取上下文信息,以实现嵌套继承的效果
const {
prefixCls: defaultPrefixCls,
StyledThemeContext,
Expand Down
19 changes: 15 additions & 4 deletions src/factories/createUseTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,46 @@ import { Context, useContext, useMemo } from 'react';
import { DEFAULT_THEME_CONTEXT } from '@/functions/setupStyled';
import { useAntdTheme } from '@/hooks/useAntdTheme';
import { useThemeMode } from '@/hooks/useThemeMode';
import { ConfigProvider } from 'antd';

interface CreateUseThemeOptions {
StyleEngineContext: Context<StyleEngine>;
}

export const createUseTheme = (options: CreateUseThemeOptions) => (): Theme => {
const { StyleEngineContext } = options;
const { StyledThemeContext, CustomThemeContext, prefixCls } = useContext(StyleEngineContext);
const {
StyledThemeContext,
CustomThemeContext,
prefixCls: outPrefixCls,
} = useContext(StyleEngineContext);

const antdTheme = useAntdTheme();
const themeState = useThemeMode();

const defaultCustomTheme = useContext(CustomThemeContext);
const styledTheme = useContext(StyledThemeContext ?? DEFAULT_THEME_CONTEXT) || {};

const { iconPrefixCls, getPrefixCls } = useContext(ConfigProvider.ConfigContext);

const antdPrefixCls = getPrefixCls();
const prefixCls = outPrefixCls ?? antdPrefixCls;

const initTheme = useMemo<Theme>(
() => ({
...antdTheme,
...themeState,
...defaultCustomTheme,
prefixCls: prefixCls || 'ant',
prefixCls,
iconPrefixCls,
}),
[antdTheme, themeState, prefixCls, defaultCustomTheme],
[antdTheme, themeState, defaultCustomTheme, prefixCls, iconPrefixCls],
);

// 如果是个空值,说明没有套 Provider,返回 antdTheme 的默认值
if (!styledTheme || Object.keys(styledTheme).length === 0) {
return initTheme;
}

return styledTheme as Theme;
return { ...styledTheme, prefixCls, iconPrefixCls } as Theme;
};
2 changes: 2 additions & 0 deletions src/functions/createInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface CreateOptions<T> {
* 默认的组件 prefixCls
*/
prefixCls?: string;
iconPrefixCls?: string;
/**
* 是否开启急速模式
*
Expand Down Expand Up @@ -87,6 +88,7 @@ export const createInstance = <T = any>(options: CreateOptions<T>) => {
CustomThemeContext,
StyledThemeContext: styledThemeContext,
prefixCls: internalOptions?.prefixCls,
iconPrefixCls: internalOptions?.iconPrefixCls,
});

const useTheme = createUseTheme({ StyleEngineContext });
Expand Down
1 change: 1 addition & 0 deletions src/types/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ export interface StyleEngine {
* @description 当前组件的 CSS 类名前缀
*/
prefixCls?: string;
iconPrefixCls?: string;
}
3 changes: 2 additions & 1 deletion src/types/theme.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ThemeConfig, MappingAlgorithm } from 'antd';
import { MappingAlgorithm, ThemeConfig } from 'antd';
import { AliasToken } from 'antd/es/theme/interface';

import { BrowserPrefers, ThemeAppearance, ThemeMode } from './appearance';
Expand Down Expand Up @@ -76,4 +76,5 @@ export interface Theme extends FullToken, ThemeContextState {
* antd 组件的 prefixCls
*/
prefixCls: string;
iconPrefixCls: string;
}
44 changes: 44 additions & 0 deletions tests/functions/__snapshots__/createStyles.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,50 @@ exports[`createStyles > styles 对象的使用 > createStyleFn 通过函数方
</div>
`;

exports[`createStyles > styles 对象的使用 > createStyleFn 通过函数方式可以拿到 token 等信息 > 可以获取 prefixCls 与 iconPrefixCls 1`] = `
.emotion-0.cp-btn {
background: lightsteelblue;
border: none;
color: royalblue;
}
.emotion-0 .cpicon {
color: darkblue;
}
<button
class="cp-btn cp-btn-default emotion-0"
type="button"
>
<span
class="cp-btn-icon"
>
<span
aria-label="smile"
class="cpicon cpicon-smile"
role="img"
>
<svg
aria-hidden="true"
data-icon="smile"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
/>
</svg>
</span>
</span>
<span>
CP Button
</span>
</button>
`;

exports[`createStyles > styles 对象的使用 > createStyleFn 通过函数方式可以拿到 token 等信息 > 字符串模板的对象模式用法 1`] = `
.emotion-0 {
background-color: #f5f5f5;
Expand Down
41 changes: 40 additions & 1 deletion tests/functions/createStyles.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { SmileOutlined } from '@ant-design/icons';
import { render } from '@testing-library/react';
import { createStyles, css, GetCustomToken, ThemeProvider } from 'antd-style';
import { Button, ConfigProvider } from 'antd';
import { GetCustomToken, ThemeProvider, createStyles, css } from 'antd-style';
import { FC, PropsWithChildren } from 'react';

describe('createStyles', () => {
Expand Down Expand Up @@ -51,6 +53,43 @@ describe('createStyles', () => {
expect(container.firstChild).toMatchSnapshot();
expect(container.firstChild).toHaveStyle({ backgroundColor: '#fff' });
});

it('可以获取 prefixCls 与 iconPrefixCls', () => {
const useStyles = createStyles(({ css, prefixCls, iconPrefixCls }) => {
return {
button: css`
&.${prefixCls}-btn {
background: lightsteelblue;
border: none;
color: royalblue;
}
.${iconPrefixCls} {
color: darkblue;
}
`,
};
});

const App = () => {
const { styles } = useStyles();

return (
<Button className={styles.button} icon={<SmileOutlined />}>
CP Button
</Button>
);
};
const wrapper = ({ children }: PropsWithChildren) => (
<ConfigProvider prefixCls={'cp'} iconPrefixCls={'cpicon'}>
{children}
</ConfigProvider>
);

const { container } = render(<App />, { wrapper });

expect(container.firstChild).toMatchSnapshot();
});
});

describe('styleObject 方法', () => {
Expand Down

0 comments on commit f2e72a3

Please sign in to comment.