Skip to content

Commit

Permalink
✨ feat: 简化 ThemeProvider 注入 styled 的接口配置
Browse files Browse the repository at this point in the history
BREAKING CHANGES: 移除 styled.useTheme 的用法,需要调整为 styled.ThemeContext,且 styled 的ThemeProvider 可以无需注入
  • Loading branch information
arvinxx committed Feb 26, 2023
1 parent a4bdc50 commit 84f828c
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 51 deletions.
15 changes: 15 additions & 0 deletions src/factories/createStyledThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { StyledConfig, StyledThemeProvider } from '@/types';

/**
* 创建一个 styled api 的 ThemeProvider
* 如果用户有设定 ThemeProvider,就使用用户的,否则使用 ThemeContext.Provider
* @param styledConfig
*/
export const createStyledThemeProvider = (styledConfig: StyledConfig): StyledThemeProvider => {
if (styledConfig.ThemeProvider) return styledConfig.ThemeProvider;

const { ThemeContext } = styledConfig;
return (props) => (
<ThemeContext.Provider value={props.theme}>{props.children}</ThemeContext.Provider>
);
};
78 changes: 46 additions & 32 deletions src/factories/createThemeProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Context, memo, ReactElement } from 'react';

import { createUseTheme } from '@/factories/createUseTheme';
import { DEFAULT_THEME_PROVIDER, DEFAULT_USE_THEME } from '@/functions/setupStyled';
import { DEFAULT_THEME_CONTEXT, DEFAULT_THEME_PROVIDER } from '@/functions/setupStyled';
import { StyledConfig } from '@/types';

import { createStyledThemeProvider } from '@/factories/createStyledThemeProvider';
import AntdProvider from './AntdProvider';
import ThemeSwitcher from './ThemeSwitcher';
import TokenContainer from './TokenContainer';
Expand All @@ -20,8 +21,13 @@ interface CreateThemeProviderOptions {

export const createThemeProvider = (
option: CreateThemeProviderOptions,
): (<T = any, S = any>(props: ThemeProviderProps<T, S>) => ReactElement | null) =>
memo(
): (<T = any, S = any>(props: ThemeProviderProps<T, S>) => ReactElement | null) => {
// 如果有全局配置 styledConfig,那么 ThemeProvider
const DefaultStyledThemeProvider = option.styledConfig
? createStyledThemeProvider(option.styledConfig)
: undefined;

return memo(
({
children,

Expand All @@ -38,36 +44,44 @@ export const createThemeProvider = (
onAppearanceChange,
themeMode,
styled,
}) => (
<ThemeSwitcher
themeMode={themeMode}
defaultAppearance={defaultAppearance}
appearance={appearance}
onAppearanceChange={onAppearanceChange}
useTheme={createUseTheme({
prefixCls: prefixCls || option.prefixCls,
styledUseTheme: styled?.useTheme || option.styledConfig?.useTheme || DEFAULT_USE_THEME,
CustomThemeContext: option.CustomThemeContext,
})}
>
<AntdProvider
prefixCls={prefixCls}
staticInstanceConfig={staticInstanceConfig}
theme={theme}
getStaticInstance={getStaticInstance}
}) => {
const useTheme = createUseTheme({
prefixCls: prefixCls || option.prefixCls,
styledThemeContext:
styled?.ThemeContext || option.styledConfig?.ThemeContext || DEFAULT_THEME_CONTEXT,
CustomThemeContext: option.CustomThemeContext,
});

const StyledThemeProvider = styled
? createStyledThemeProvider(styled)
: DefaultStyledThemeProvider || DEFAULT_THEME_PROVIDER;

return (
<ThemeSwitcher
themeMode={themeMode}
defaultAppearance={defaultAppearance}
appearance={appearance}
onAppearanceChange={onAppearanceChange}
useTheme={useTheme}
>
<TokenContainer
<AntdProvider
prefixCls={prefixCls}
customToken={customToken}
defaultCustomToken={option.customToken}
customStylish={customStylish}
StyledThemeProvider={
styled?.ThemeProvider || option.styledConfig?.ThemeProvider || DEFAULT_THEME_PROVIDER
}
staticInstanceConfig={staticInstanceConfig}
theme={theme}
getStaticInstance={getStaticInstance}
>
{children}
</TokenContainer>
</AntdProvider>
</ThemeSwitcher>
),
<TokenContainer
prefixCls={prefixCls}
customToken={customToken}
defaultCustomToken={option.customToken}
customStylish={customStylish}
StyledThemeProvider={StyledThemeProvider}
>
{children}
</TokenContainer>
</AntdProvider>
</ThemeSwitcher>
);
},
);
};
10 changes: 5 additions & 5 deletions src/factories/createUseTheme.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { Theme, UseTheme } from '@/types';
import { Theme } from '@/types';
import { Context, useContext, useMemo } from 'react';

import { DEFAULT_USE_THEME } from '@/functions/setupStyled';
import { DEFAULT_THEME_CONTEXT } from '@/functions/setupStyled';
import { useAntdTheme } from '@/hooks/useAntdTheme';
import { useThemeMode } from '@/hooks/useThemeMode';

interface CreateUseThemeOptions {
prefixCls?: string;
CustomThemeContext: Context<any>;
styledUseTheme?: UseTheme;
styledThemeContext?: Context<any>;
}

export const createUseTheme = (options: CreateUseThemeOptions) => (): Theme => {
const { prefixCls, styledUseTheme, CustomThemeContext } = options;
const { prefixCls, styledThemeContext, CustomThemeContext } = options;
const antdTheme = useAntdTheme();
const themeState = useThemeMode();

const defaultCustomTheme = useContext(CustomThemeContext);

const styledTheme = styledUseTheme ? styledUseTheme() : DEFAULT_USE_THEME() || {};
const styledTheme = useContext(styledThemeContext ?? DEFAULT_THEME_CONTEXT) || {};

const initTheme = useMemo<Theme>(
() => ({
Expand Down
6 changes: 3 additions & 3 deletions src/functions/createInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { createStylesFactory } from '@/factories/createStyles';
import { createThemeProvider, ThemeProviderProps } from '@/factories/createThemeProvider';
import { createUseTheme } from '@/factories/createUseTheme';

import { HashPriority, StyledConfig, StyleManager, Theme } from '@/types';
import { HashPriority, StyledConfig, StyleManager } from '@/types';

// 为 SSR 添加一个全局的 cacheManager,用于统一抽取 ssr 样式
declare global {
Expand Down Expand Up @@ -70,12 +70,12 @@ export const createInstance = <T = any>(options: CreateOptions<T>) => {
(options.customToken ? options.customToken : {}) as T,
);

const styledUseTheme = options.styled?.useTheme as () => Theme;
const styledThemeContext = options.styled?.ThemeContext;

const useTheme = createUseTheme({
prefixCls: options?.prefixCls,
CustomThemeContext,
styledUseTheme,
styledThemeContext,
});

const createStyles = createStylesFactory({
Expand Down
16 changes: 9 additions & 7 deletions src/functions/setupStyled.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { ThemeProvider, useTheme } from '@emotion/react';
import { ThemeContext, ThemeProvider } from '@emotion/react';
import { Context } from 'react';

import { createStyledThemeProvider } from '@/factories/createStyledThemeProvider';
import { StyledConfig, StyledThemeProvider, Theme } from '@/types';

export let DEFAULT_THEME_PROVIDER = ThemeProvider as StyledThemeProvider;
export let DEFAULT_USE_THEME = useTheme as () => Theme;
export let DEFAULT_THEME_CONTEXT = ThemeContext as Context<Theme>;

export const setupStyled = (config: StyledConfig) => {
if (config?.ThemeProvider) {
DEFAULT_THEME_PROVIDER = config.ThemeProvider;
}
if (config.useTheme) {
DEFAULT_USE_THEME = config.useTheme;
if (!config.ThemeContext) {
throw 'ThemeContext is required. Please check your config.';
}

DEFAULT_THEME_CONTEXT = config.ThemeContext;
DEFAULT_THEME_PROVIDER = createStyledThemeProvider(config);
};
13 changes: 10 additions & 3 deletions src/types/styled.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { Theme } from 'antd-style/src';
import { FC, ReactNode } from 'react';
import { Context, FC, ReactNode } from 'react';
import { Theme } from './theme';

export interface StyledConfig {
/**
* styled 对象所对应的 ThemeContext
* @requires
*/
ThemeContext: Context<any>;
/**
* 可以注入相应 styled 方法的 ThemeProvider,或其他自己定义的ThemeProvider
*/
ThemeProvider?: StyledThemeProvider;
useTheme?: () => any;
}

export type StyledThemeProvider = FC<{ theme: Theme; children: ReactNode }>;
8 changes: 7 additions & 1 deletion tests/functions/styled.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import styled from '@emotion/styled';
import { render } from '@testing-library/react';
import { ThemeProvider } from 'antd-style';
import { Theme as AntdStyleTheme, ThemeProvider } from 'antd-style';

// 为 emotion 的 styled 注入 antd-style 的主题类型
declare module '@emotion/react' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Theme extends AntdStyleTheme {}
}

describe('styled', () => {
it('类型定义正常', () => {
Expand Down

0 comments on commit 84f828c

Please sign in to comment.