update
This commit is contained in:
parent
24f7f9524e
commit
18d79b79a7
@ -1,38 +1,60 @@
|
|||||||
import { StyleProvider } from '@ant-design/cssinjs';
|
import { StyleProvider } from '@ant-design/cssinjs';
|
||||||
import { ConfigProvider, theme, App as AntdApp } from 'antd';
|
import { ConfigProvider, theme, App as AntdApp } from 'antd';
|
||||||
// import 'dayjs/locale/zh-cn';
|
// import 'dayjs/locale/zh-cn';
|
||||||
import zhCN from 'antd/locale/zh_CN';
|
|
||||||
|
|
||||||
import { FC } from 'react';
|
import { FC, useMemo } from 'react';
|
||||||
|
|
||||||
import $styles from './app.module.css';
|
import $styles from './app.module.css';
|
||||||
import { CallbackDemo } from './components/demo/callback';
|
import { localeData } from './components/demo/constants';
|
||||||
import EffectDemo from './components/demo/effect';
|
import ContextDemo, { Locale } from './components/demo/context';
|
||||||
import MemoDemo from './components/demo/memo';
|
import CustomDemo from './components/demo/custom';
|
||||||
import RefDemo from './components/demo/ref';
|
import { useLocale, useTheme } from './components/demo/hooks';
|
||||||
import StateDemo from './components/demo/state';
|
import ReducerDemo, { Theme } from './components/demo/reducer';
|
||||||
|
|
||||||
const App: FC = () => {
|
const Wrapper: FC = () => {
|
||||||
|
const locale = useLocale();
|
||||||
|
const antdLocaleData = useMemo(() => {
|
||||||
|
if (!Object.keys(localeData).find((v) => v === locale.name)) {
|
||||||
|
return localeData[0];
|
||||||
|
}
|
||||||
|
return localeData[locale.name];
|
||||||
|
}, [locale.name]);
|
||||||
|
const themeState = useTheme();
|
||||||
|
const algorithm = useMemo(() => {
|
||||||
|
const result = [themeState.compact ? theme.compactAlgorithm : theme.defaultAlgorithm];
|
||||||
|
if (themeState.mode === 'dark') result.push(theme.darkAlgorithm);
|
||||||
|
return result;
|
||||||
|
}, [themeState]);
|
||||||
return (
|
return (
|
||||||
<ConfigProvider
|
<ConfigProvider
|
||||||
locale={zhCN}
|
locale={antdLocaleData}
|
||||||
theme={{
|
theme={{
|
||||||
algorithm: theme.defaultAlgorithm,
|
algorithm,
|
||||||
token: {},
|
token: {},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<StyleProvider hashPriority="high">
|
<StyleProvider hashPriority="high">
|
||||||
<AntdApp>
|
<AntdApp>
|
||||||
<div className={$styles.app}>
|
<div className={$styles.app}>
|
||||||
<StateDemo />
|
{/* <StateDemo />
|
||||||
<EffectDemo />
|
<EffectDemo />
|
||||||
<RefDemo />
|
<RefDemo />
|
||||||
<MemoDemo />
|
<MemoDemo />
|
||||||
<CallbackDemo />
|
<CallbackDemo /> */}
|
||||||
|
<ContextDemo />
|
||||||
|
<ReducerDemo />
|
||||||
|
<CustomDemo />
|
||||||
</div>
|
</div>
|
||||||
</AntdApp>
|
</AntdApp>
|
||||||
</StyleProvider>
|
</StyleProvider>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
const App: FC = () => (
|
||||||
|
<Locale>
|
||||||
|
<Theme>
|
||||||
|
<Wrapper />
|
||||||
|
</Theme>
|
||||||
|
</Locale>
|
||||||
|
);
|
||||||
export default App;
|
export default App;
|
||||||
|
27
apps/admin/src/components/demo/constants.ts
Normal file
27
apps/admin/src/components/demo/constants.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Locale } from 'antd/es/locale';
|
||||||
|
import enUS from 'antd/es/locale/en_US';
|
||||||
|
import zhCN from 'antd/es/locale/zh_CN';
|
||||||
|
|
||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
import { LocaleType, ThemeContextType, ThemeState } from './types';
|
||||||
|
|
||||||
|
export const localeData: Record<string, Locale> = {
|
||||||
|
en_US: enUS,
|
||||||
|
zh_CN: zhCN,
|
||||||
|
};
|
||||||
|
export const locales: LocaleType[] = [
|
||||||
|
{
|
||||||
|
name: 'en_US',
|
||||||
|
label: '🇺🇸 english(US)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'zh_CN',
|
||||||
|
label: '🇨🇳 简体中文',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
export const defaultThemeConfig: ThemeState = {
|
||||||
|
mode: 'light',
|
||||||
|
compact: false,
|
||||||
|
};
|
||||||
|
export const ThemeContext = createContext<ThemeContextType | null>(null);
|
71
apps/admin/src/components/demo/context.tsx
Normal file
71
apps/admin/src/components/demo/context.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { Pagination, Select } from 'antd';
|
||||||
|
import { createContext, FC, ReactNode, useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { localeData, locales } from './constants';
|
||||||
|
import { useLocale, useLocaleAction } from './hooks';
|
||||||
|
import $styles from './style.module.css';
|
||||||
|
import { LocaleState, LocaleType } from './types';
|
||||||
|
|
||||||
|
export const LocaleContext = createContext<LocaleState>({
|
||||||
|
locale: locales[0],
|
||||||
|
setLocale: (_locale: LocaleType) => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const LocaleProvider: FC<LocaleState & { children?: ReactNode }> = ({
|
||||||
|
locale,
|
||||||
|
setLocale,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const value = useMemo(() => ({ locale, setLocale }), [locale]);
|
||||||
|
return <LocaleContext.Provider value={value}>{children}</LocaleContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Locale: FC<{ children?: ReactNode }> = ({ children }) => {
|
||||||
|
const [locale, setLocale] = useState<LocaleType>(locales[0]);
|
||||||
|
const changeLocale = useCallback((value: LocaleType) => {
|
||||||
|
if (Object.keys(localeData).find((v) => v === value.name)) {
|
||||||
|
setLocale(value);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<LocaleProvider locale={locale} setLocale={changeLocale}>
|
||||||
|
{children}
|
||||||
|
</LocaleProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LocaleConfig: FC = () => {
|
||||||
|
const locale = useLocale();
|
||||||
|
const setLocale = useLocaleAction();
|
||||||
|
const changeLocale = (value: string) => {
|
||||||
|
const current = locales.find((item) => item.name === value);
|
||||||
|
current && setLocale(current);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Select defaultValue={locale.name} style={{ width: 120 }} onChange={changeLocale}>
|
||||||
|
{locales.map(({ name, label }) => (
|
||||||
|
<Select.Option key={name} value={name}>
|
||||||
|
{label}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ContextDemo: FC = () => {
|
||||||
|
const locale = useLocale();
|
||||||
|
return (
|
||||||
|
<div className={$styles.container}>
|
||||||
|
<h2 className="tw-text-center">useContext Demo</h2>
|
||||||
|
<p className="tw-text-center tw-py-5">当前语言: {locale.label}</p>
|
||||||
|
<div className="tw-w-full">
|
||||||
|
<h3>Antd语言切换测试</h3>
|
||||||
|
<div className="tw-w-full tw-my-4">
|
||||||
|
<LocaleConfig />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Pagination defaultCurrent={0} total={500} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ContextDemo;
|
25
apps/admin/src/components/demo/custom.tsx
Normal file
25
apps/admin/src/components/demo/custom.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Button } from 'antd';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { FC, useState } from 'react';
|
||||||
|
|
||||||
|
import { useUpdateEffect } from './hooks';
|
||||||
|
import $styles from './style.module.css';
|
||||||
|
|
||||||
|
const CustomDemo: FC = () => {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
useUpdateEffect(() => {
|
||||||
|
console.log('changed');
|
||||||
|
}, [count]);
|
||||||
|
return (
|
||||||
|
<div className={clsx($styles.container, 'tw-w-[20rem]')}>
|
||||||
|
<h2 className="tw-text-center">Custom Demo</h2>
|
||||||
|
<p className="tw-text-center tw-py-5">{count}</p>
|
||||||
|
<div className="tw-flex tw-justify-around">
|
||||||
|
<Button onClick={() => setCount(Math.ceil(Math.random() * 10))} type="dashed">
|
||||||
|
变化
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default CustomDemo;
|
46
apps/admin/src/components/demo/hooks.tsx
Normal file
46
apps/admin/src/components/demo/hooks.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { isEqual, isNil } from 'lodash';
|
||||||
|
import {
|
||||||
|
DependencyList,
|
||||||
|
EffectCallback,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
import { defaultThemeConfig, locales, ThemeContext } from './constants';
|
||||||
|
import { LocaleContext } from './context';
|
||||||
|
|
||||||
|
export const useUpdateEffect = (effect: EffectCallback, deps?: DependencyList) => {
|
||||||
|
const inited = useRef(deps);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isEqual(inited.current, deps)) {
|
||||||
|
inited.current = deps;
|
||||||
|
effect();
|
||||||
|
}
|
||||||
|
}, [deps]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useTheme = () => {
|
||||||
|
const context = useContext(ThemeContext) ?? ({} as Record<string, any>);
|
||||||
|
return useMemo(
|
||||||
|
() => (isNil(context.state) ? defaultThemeConfig : context.state),
|
||||||
|
[context.state],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useThemeAction = () => {
|
||||||
|
const context = useContext(ThemeContext) ?? ({} as Record<string, any>);
|
||||||
|
return useCallback(isNil(context.dispatch) ? null : context.dispatch, [context.dispatch]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLocale = () => {
|
||||||
|
const context = useContext(LocaleContext) ?? ({} as Record<string, any>);
|
||||||
|
return useMemo(() => (isNil(context.locale) ? locales[0] : context.locale), [context.locale]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLocaleAction = () => {
|
||||||
|
const context = useContext(LocaleContext) ?? ({} as Record<string, any>);
|
||||||
|
return useCallback(isNil(context.setLocale) ? null : context.setLocale, [context.setLocale]);
|
||||||
|
};
|
90
apps/admin/src/components/demo/reducer.tsx
Normal file
90
apps/admin/src/components/demo/reducer.tsx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { Calendar, Switch } from 'antd';
|
||||||
|
import { produce } from 'immer';
|
||||||
|
import { isNil } from 'lodash';
|
||||||
|
import { FC, ReactNode, Reducer, useContext, useEffect, useMemo, useReducer } from 'react';
|
||||||
|
|
||||||
|
import { defaultThemeConfig, ThemeContext } from './constants';
|
||||||
|
import $styles from './style.module.css';
|
||||||
|
import { ThemeAction, ThemeState } from './types';
|
||||||
|
|
||||||
|
const ThemeReducer: Reducer<ThemeState, ThemeAction> = produce((draft, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'change_mode':
|
||||||
|
draft.mode = action.value;
|
||||||
|
break;
|
||||||
|
case 'change_compact':
|
||||||
|
draft.compact = action.value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Theme: FC<{ data?: ThemeState; children?: ReactNode }> = ({ data = {}, children }) => {
|
||||||
|
const [state, dispatch] = useReducer(ThemeReducer, data, (initData) => ({
|
||||||
|
...defaultThemeConfig,
|
||||||
|
...initData,
|
||||||
|
}));
|
||||||
|
useEffect(() => {
|
||||||
|
const body = document.getElementsByTagName('body');
|
||||||
|
if (body.length) {
|
||||||
|
body[0].classList.remove('light');
|
||||||
|
body[0].classList.remove('dark');
|
||||||
|
body[0].classList.add(state.mode === 'dark' ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
}, [state.mode]);
|
||||||
|
const value = useMemo(() => ({ state, dispatch }), [state]);
|
||||||
|
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ThemeConfig: FC = () => {
|
||||||
|
const context = useContext(ThemeContext);
|
||||||
|
if (isNil(context)) return null;
|
||||||
|
const { state, dispatch } = context;
|
||||||
|
const toggleMode = () =>
|
||||||
|
dispatch({ type: 'change_mode', value: state.mode === 'light' ? 'dark' : 'light' });
|
||||||
|
const toggleCompact = () => dispatch({ type: 'change_compact', value: !state.compact });
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Switch
|
||||||
|
checkedChildren="🌛"
|
||||||
|
unCheckedChildren="☀️"
|
||||||
|
onChange={toggleMode}
|
||||||
|
checked={state.mode === 'dark'}
|
||||||
|
defaultChecked={state.mode === 'dark'}
|
||||||
|
/>
|
||||||
|
<Switch
|
||||||
|
checkedChildren="紧凑"
|
||||||
|
unCheckedChildren="正常"
|
||||||
|
onChange={toggleCompact}
|
||||||
|
checked={state.compact}
|
||||||
|
defaultChecked={state.compact}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ReducerDemo: FC = () => {
|
||||||
|
const context = useContext(ThemeContext);
|
||||||
|
if (isNil(context)) return null;
|
||||||
|
const {
|
||||||
|
state: { mode, compact },
|
||||||
|
} = context;
|
||||||
|
return (
|
||||||
|
<div className={$styles.container}>
|
||||||
|
<h2 className="tw-text-center">useReducer Demo</h2>
|
||||||
|
<div className="tw-flex tw-items-center tw-flex-col">
|
||||||
|
<p>主题模式: 「{mode === 'dark' ? '暗黑' : '明亮'}」</p>
|
||||||
|
<p>是否紧凑: 「{compact ? '是' : '否'}」</p>
|
||||||
|
<div className="tw-flex-auto tw-mb-5">
|
||||||
|
<ThemeConfig />
|
||||||
|
</div>
|
||||||
|
<div className="tw-max-w-md">
|
||||||
|
<Calendar fullscreen={false} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReducerDemo;
|
34
apps/admin/src/components/demo/types.ts
Normal file
34
apps/admin/src/components/demo/types.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Dispatch } from 'react';
|
||||||
|
|
||||||
|
export type LocaleType = {
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
export type LocaleState = {
|
||||||
|
locale: LocaleType;
|
||||||
|
setLocale: (locale: LocaleType) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum ThemeMode {
|
||||||
|
LIGHT = 'light',
|
||||||
|
DARK = 'dark',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ThemeState = {
|
||||||
|
mode: `${ThemeMode}`;
|
||||||
|
compact: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum ThemeActionType {
|
||||||
|
CHANGE_MODE = 'change_mode',
|
||||||
|
CHANGE_COMPACT = 'change_compact',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ThemeAction =
|
||||||
|
| { type: `${ThemeActionType.CHANGE_MODE}`; value: `${ThemeMode}` }
|
||||||
|
| { type: `${ThemeActionType.CHANGE_COMPACT}`; value: boolean };
|
||||||
|
|
||||||
|
export type ThemeContextType = {
|
||||||
|
state: ThemeState;
|
||||||
|
dispatch: Dispatch<ThemeAction>;
|
||||||
|
};
|
@ -8,3 +8,7 @@ body,
|
|||||||
body {
|
body {
|
||||||
@apply tw-bg-[url(@/assets/images/bg-light.png)];
|
@apply tw-bg-[url(@/assets/images/bg-light.png)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.dark {
|
||||||
|
@apply tw-bg-[url(@/assets/images/bg-dark.png)];
|
||||||
|
}
|
||||||
|
12076
pnpm-lock.yaml
12076
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user