diff --git a/CHANGELOG.md b/CHANGELOG.md index b9259b3d..213b504f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +# [3.3.0-beta.2](https://github.com/ant-design/antd-style/compare/v3.3.0-beta.1...v3.3.0-beta.2) (2023-06-03) + +### ✨ Features + +- CacheManager support delete method ([b3b74e9](https://github.com/ant-design/antd-style/commit/b3b74e9)) + +### 🐛 Bug Fixes + +- fix suspense hydration with ssr ([9f27b16](https://github.com/ant-design/antd-style/commit/9f27b16)) + +# [3.3.0-beta.1](https://github.com/ant-design/antd-style/compare/v3.2.2...v3.3.0-beta.1) (2023-06-03) + +### ✨ Features + +- CacheManager support delete method ([595a5a6](https://github.com/ant-design/antd-style/commit/595a5a6)) + +### 🐛 Bug Fixes + +- fix suspense hydration with ssr ([ef61d10](https://github.com/ant-design/antd-style/commit/ef61d10)) + ## [3.2.2](https://github.com/ant-design/antd-style/compare/v3.2.1...v3.2.2) (2023-06-02) ### 🐛 Bug Fixes diff --git a/docs/api/create-instance.md b/docs/api/create-instance.md index 32dfcff1..910e7970 100644 --- a/docs/api/create-instance.md +++ b/docs/api/create-instance.md @@ -60,9 +60,28 @@ export const { } = styleInstance; ``` +## 指定 container + +在创建时指定 container ,可以使得样式都在该容器处插入,在 iframe 等场景比较有用。 + +```ts +const { css, StyleProvider, createStyles } = createInstance({ + key: 'test', + container: document.body, +}); +``` + + + +如果你在组件库里用 `createInstance` 暴露出的 `createStyles` 定义好了样式,然后希望在不同的业务场景下指定不同的插入位置。 + +那么可以在业务应用使用时,在外部包一层 `StyleProvider` 并设置 `container` 来实现自定义位置的插入。 + + + ## 兼容 styled 主题方案 -无论是 `styled-component` 还是 `emotion/styled`,如果需要响应主题变化,都需要在组件外部包裹一个 `ThemeProvider`。这个时候,如果你的组件也需要响应主题变化,就需要在组件内部再包裹一个 `ThemeProvider`,通过在 createInstance 中传入 `styled` 的配置,即可让 `styled` 后的组件也响应自定义 Token。 +如果你使用 `styled-component` 且需要响应主题变化,都需要在组件外部包裹一个 `ThemeProvider`。这个时候,如果你的组件也需要响应主题变化,就需要在组件内部再包裹一个 `ThemeProvider`,通过在 createInstance 中传入 `styled` 的配置,即可让 `styled` 后的组件也响应自定义 Token。 ```ts | pure // styled-components 版本 @@ -75,19 +94,15 @@ const componentStyleIntanceWithSC = createInstance({ }); ``` -## 指定 container - -在创建时指定 container ,可以使得样式都在该容器处插入,在 iframe 等场景比较有用。 - -```ts -const { css, StyleProvider, createStyles } = createInstance({ - key: 'test', - container: document.body, -}); -``` - - - -还有一个场景是,组件库里用 `createInstance` 暴露出的 `createStyles` 定义好了样式,想在不同的业务场景下指定不同的插入位置,业务上通过组件外部包一层 `StyleProvider` 并设置 `container` 来实现节点的自行插入。 - - +## API + +| 属性名 | 类型 | 描述 | +| ------------- | ----------------------------------------- | ------------------------------------- | +| key | `string` | 生成的 CSS 关键词,默认为 `ant-css`。 | +| prefixCls | `string` | 默认的组件前缀。 | +| speedy | `boolean` | 是否开启急速模式,默认为 `false`。 | +| container | `Node` | 渲染容器节点。 | +| customToken | `T` | 默认的自定义 Token。 | +| hashPriority | `HashPriority` | 控制 CSS 类名生成的优先级。 | +| ThemeProvider | `Omit, 'children'>` | 主题提供者。 | +| styled | `StyledConfig` | `styled-components` 配置项。 | diff --git a/docs/api/style-provider.md b/docs/api/style-provider.md index cb475f49..b8869438 100644 --- a/docs/api/style-provider.md +++ b/docs/api/style-provider.md @@ -4,27 +4,39 @@ description: 用于全局管理样式插入相关的配置 order: 2 group: 容器组件 demo: - cols: 2 tocDepth: 4 --- ## 修改 container -指定 `container` 为 dom 节点,即可使得所有生成的样式(antd、antd-style)均插入到该节点下。 +指定 `container` 即可使得所有生成的样式(antd、antd-style)均插入到该节点下。 -### 修改样式注入点 +## 修改样式注入点 -TBD +一般情况下不太需要用到,如果你需要兼容组件覆写样式的需求时,可以考虑设定组件样式的注入点,使得其在该节点之后注入。 -### 开启 speedy 极速模式 + -TBD +## 开启 speedy 极速模式 + +开启 emotion 的 speedy 模式。建议独立应用可以开启。 + + + +:::info{title=Speedy模式} + +早期的 cssinjs 方案中,样式的插入是一个 style 标签对应一个样式,浏览器解析较慢,但便于修改与调试。 + +目前 emotion 默认使用现代化的 CSSOM api 插入样式,会把一堆 css 放到一个 标签里,插入后移除相应的内容。这种方式性能很好,支持万级别的样式插入。但与微应用(qiankun)兼容性较差。 + +antd-style 中默认关闭了 speedy 模式,如果需要,配置 `speedy` 为 `true` 即可。 +::: ## API -继承 `ant-design/cssinjs` 的 [StyleProvider](https://github.com/ant-design/cssinjs#styleprovider) api ,其余 api 如下: +继承 `ant-design/cssinjs` 的 [StyleProvider](https://github.com/ant-design/cssinjs#styleprovider) ,其余 API 如下: | 属性名 | 类型 | 描述 | | --------------- | -------------------------------------- | -------------------------------------------------------------------- | diff --git a/docs/api/use-responsive.md b/docs/api/use-responsive.md index 744a5dd9..76e5a491 100644 --- a/docs/api/use-responsive.md +++ b/docs/api/use-responsive.md @@ -8,34 +8,25 @@ group: Hooks 获取响应式媒体查询的结果。基于 antd 的 [Grid.useBreakpoint](https://ant.design/components/grid-cn#components-grid-demo-usebreakpoint) 封装。 - - ## 用法 -```ts +```tsx | pure import { useResponsive } from 'antd-style'; function Theme() { const { mobile } = useResponsive(); - useEffect(() => { - console.log(theme); - }, [theme]); - - return null; + // 使用 js 来区分显示移动端 + return mobile ?
mobile
:
desktop
; } ``` -## Typescript +## 示例 -```ts -useTheme = () => Theme; -``` + + +## 自定义断点 -### 返回值 +通过传入 antd 的断点配置,来自定义响应断点。 -| 参数 | 说明 | 类型 | 默认值 | -| ---------- | -------------- | ---------------------- | ------- | -| themeMode | 主题模式 | `dark / light / auto` | `light` | -| appearance | 显示外观 | `dark / light` | `light` | -| isDarkMode | 是否为暗色模式 | `boolean` | `false` | + diff --git a/docs/demos/StyleProvider/insertpoint.tsx b/docs/demos/StyleProvider/insertpoint.tsx new file mode 100644 index 00000000..7fdacc4b --- /dev/null +++ b/docs/demos/StyleProvider/insertpoint.tsx @@ -0,0 +1,25 @@ +/** + * iframe: 50 + */ +import { createStyles, css, StyleProvider } from 'antd-style'; + +const useStyles = createStyles({ + text: css` + color: hotpink; + `, +}); + +const Text = () => { + const { styles } = useStyles(); + return
插入的 style 节点在第一个 meta 标签之后
; +}; + +export default () => { + const firstMeta = document.getElementsByTagName('meta')[0]; + + return ( + + + + ); +}; diff --git a/docs/demos/StyleProvider/speedy.tsx b/docs/demos/StyleProvider/speedy.tsx new file mode 100644 index 00000000..9afdc97f --- /dev/null +++ b/docs/demos/StyleProvider/speedy.tsx @@ -0,0 +1,23 @@ +/** + * iframe: 50 + */ +import { createStyles, css, StyleProvider } from 'antd-style'; + +const useStyles = createStyles({ + text: css` + color: blue; + `, +}); + +const Text = () => { + const { styles } = useStyles(); + return
开启 speedy 模式后,style 标签中将不存在具体样式
; +}; + +export default () => { + return ( + + + + ); +}; diff --git a/docs/demos/api/createInstance/createInstanceWithContainer.tsx b/docs/demos/api/createInstance/withContainer.tsx similarity index 96% rename from docs/demos/api/createInstance/createInstanceWithContainer.tsx rename to docs/demos/api/createInstance/withContainer.tsx index 28a11d35..7f73f781 100644 --- a/docs/demos/api/createInstance/createInstanceWithContainer.tsx +++ b/docs/demos/api/createInstance/withContainer.tsx @@ -1,5 +1,5 @@ /** - * iframe: true + * iframe: 100 */ import { Button } from 'antd'; import { createInstance } from 'antd-style'; diff --git a/docs/demos/api/createInstance/createInstanceWithStyleProviderContainer.tsx b/docs/demos/api/createInstance/withStyleProviderContainer.tsx similarity index 82% rename from docs/demos/api/createInstance/createInstanceWithStyleProviderContainer.tsx rename to docs/demos/api/createInstance/withStyleProviderContainer.tsx index bf76a386..b22dc1cb 100644 --- a/docs/demos/api/createInstance/createInstanceWithStyleProviderContainer.tsx +++ b/docs/demos/api/createInstance/withStyleProviderContainer.tsx @@ -1,7 +1,8 @@ /** - * iframe: true + * iframe: 150 */ -import { Button } from 'antd'; + +import { Button, Divider } from 'antd'; import { createInstance } from 'antd-style'; const { css, StyleProvider, createStyles } = createInstance({ @@ -26,6 +27,7 @@ export default () => { + 下方 style 插入在 head ); diff --git a/docs/demos/api/useResponsive/DisplayTag.tsx b/docs/demos/api/useResponsive/Demo/DisplayTag.tsx similarity index 100% rename from docs/demos/api/useResponsive/DisplayTag.tsx rename to docs/demos/api/useResponsive/Demo/DisplayTag.tsx diff --git a/docs/demos/api/useResponsive/index.tsx b/docs/demos/api/useResponsive/Demo/index.tsx similarity index 85% rename from docs/demos/api/useResponsive/index.tsx rename to docs/demos/api/useResponsive/Demo/index.tsx index 33cd34f3..008130d9 100644 --- a/docs/demos/api/useResponsive/index.tsx +++ b/docs/demos/api/useResponsive/Demo/index.tsx @@ -1,12 +1,9 @@ -/** - * compact: true - */ -import { Divider, Tooltip } from 'antd'; -import { Breakpoint, ThemeProvider, useResponsive, useTheme } from 'antd-style'; +import { Tooltip } from 'antd'; +import { Breakpoint, useResponsive, useTheme } from 'antd-style'; import { Flexbox } from 'react-layout-kit'; +import { Label } from '../style'; import { DisplayTag } from './DisplayTag'; -import { Container, Label } from './style'; const Demo = () => { const responsive = useResponsive(); @@ -81,14 +78,4 @@ const Demo = () => { ); }; -export default () => { - return ( - - - - - - - - ); -}; +export default Demo; diff --git a/docs/demos/api/useResponsive/custom.tsx b/docs/demos/api/useResponsive/custom.tsx new file mode 100644 index 00000000..f98583ba --- /dev/null +++ b/docs/demos/api/useResponsive/custom.tsx @@ -0,0 +1,25 @@ +/** + * compact: true + */ + +import { ThemeProvider } from 'antd-style'; +import Demo from './Demo'; +import { Container } from './style'; + +export default () => { + return ( + + + + + + ); +}; diff --git a/docs/demos/api/useResponsive/default.tsx b/docs/demos/api/useResponsive/default.tsx new file mode 100644 index 00000000..f5b498cb --- /dev/null +++ b/docs/demos/api/useResponsive/default.tsx @@ -0,0 +1,18 @@ +/** + * compact: true + */ + +import Demo from './Demo'; +import { Container } from './style'; + +export default () => { + return ( + + + {/**/} + {/**/} + {/* */} + {/**/} + + ); +}; diff --git a/package.json b/package.json index fd685b67..631d9fd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "antd-style", - "version": "3.2.2", + "version": "3.3.0-beta.2", "description": "a css-in-js solution for application combine with antd v5 token system and emotion", "keywords": [ "antd", @@ -85,7 +85,7 @@ "@emotion/babel-plugin": "^11", "@emotion/jest": "^11", "@emotion/styled": "^11", - "@floating-ui/react": "^0.17", + "@floating-ui/react": "^0.24", "@react-three/fiber": "^8", "@testing-library/jest-dom": "^5", "@testing-library/react": "^13", diff --git a/src/core/CacheManager.ts b/src/core/CacheManager.ts index 64be7176..e9408b6f 100644 --- a/src/core/CacheManager.ts +++ b/src/core/CacheManager.ts @@ -10,7 +10,11 @@ export class CacheManager { this._cacheList.push(cache); } - private hasCache(cache: EmotionCache) { + delete(cache: EmotionCache) { + this._cacheList = this._cacheList.filter((c) => c.key !== cache.key); + } + + hasCache(cache: EmotionCache) { return this._cacheList.some((c) => c.key === cache.key); } diff --git a/src/factories/createThemeProvider/ThemeSwitcher.tsx b/src/factories/createThemeProvider/ThemeSwitcher.tsx index 0b4a14d6..378397cc 100644 --- a/src/factories/createThemeProvider/ThemeSwitcher.tsx +++ b/src/factories/createThemeProvider/ThemeSwitcher.tsx @@ -4,6 +4,7 @@ import useMergeValue from 'use-merge-value'; import { ThemeModeContext } from '@/context'; import { BrowserPrefers, ThemeAppearance, ThemeMode, UseTheme } from '@/types'; import { matchBrowserPrefers } from '@/utils/matchBrowserPrefers'; +import { safeStartTransition } from '@/utils/safeStartTransition'; let darkThemeMatch: MediaQueryList; @@ -32,7 +33,9 @@ const ThemeObserver: FC<{ useLayoutEffect(() => { // 如果不是自动,就明确设定亮暗色 if (themeMode !== 'auto') { - setAppearance(themeMode); + safeStartTransition(() => { + setAppearance(themeMode); + }); return; } // 如果是自动的话,则去做一次匹配,并开始监听 @@ -48,7 +51,7 @@ const ThemeObserver: FC<{ }; }, [themeMode]); - useEffect(() => { + useLayoutEffect(() => { if (!darkThemeMatch) { darkThemeMatch = matchBrowserPrefers('dark'); } @@ -117,7 +120,10 @@ const ThemeSwitcher: FC = memo( // Wait until after client-side hydration to show useEffect(() => { - setStartObserver(true); + // 兼容 React18 的 Suspense 问题 + safeStartTransition(() => { + setStartObserver(true); + }); }, []); return ( diff --git a/src/utils/matchBrowserPrefers.test.ts b/src/utils/matchBrowserPrefers.test.ts index d2e1a309..dc919d2f 100644 --- a/src/utils/matchBrowserPrefers.test.ts +++ b/src/utils/matchBrowserPrefers.test.ts @@ -1,3 +1,4 @@ +import { ThemeAppearance } from 'antd-style'; import { vi } from 'vitest'; import { matchBrowserPrefers } from './matchBrowserPrefers'; @@ -36,4 +37,17 @@ describe('matchBrowserPrefers', () => { // 还原全局变量 global.window = originalWindow; }); + it('should return a MediaQueryList-like object when window object is undefined', () => { + const tempWin = window; + // eslint-disable-next-line no-global-assign,no-native-reassign + window = undefined as any; + + const mode: ThemeAppearance = 'dark'; + const result = matchBrowserPrefers(mode); + + expect(result.matches).toBe(false); + + // eslint-disable-next-line no-global-assign,no-native-reassign + window = tempWin; + }); }); diff --git a/src/utils/safeStartTransition.ts b/src/utils/safeStartTransition.ts new file mode 100644 index 00000000..d2ef0e71 --- /dev/null +++ b/src/utils/safeStartTransition.ts @@ -0,0 +1,9 @@ +import { startTransition, TransitionFunction } from 'react'; + +export const safeStartTransition = (func: TransitionFunction) => { + if (typeof startTransition === 'function') { + startTransition(func); + } else { + func(); + } +};