Ant Design
的自定義主題,對于剛?cè)胧值臅r候感覺真是一臉蒙圈,那今天給它梳理倒騰下;
1、自定義主題要點
整體樣式變化,主要兩個部分:
1.1、Design Token
https://ant.design/docs/react/customize-theme-cn#theme
官方介紹一大堆,咱們粗暴點就當(dāng)作key=>value
配置內(nèi)容來看和理解!
大體分為四塊配置項:
分類 | 涉及配置項 | |
---|---|---|
通用/基本樣式 | token | 可查閱SeedToken、MapToken、AliasToken |
組件樣式 | components | 查閱各個組件最底下的主題變量(Design Token) 內(nèi)容 |
樣式算法 | algorithm | 這塊其實就算UI庫內(nèi)部自動幫你換算不同“等級”顏色color值 |
擴展配置 | inherit、cssVar、hashed | 這塊應(yīng)該很少會去改它,做主題切換的時候建議cssVar 開啟 |
1.2、全局配置 ConfigProvider 組件
https://ant.design/components/config-provider-cn
import React from 'react';
import { ConfigProvider } from 'antd';
// ...
const Demo: React.FC = () => (
<ConfigProvider componentSize={"middle"}>
// 界面組件
</ConfigProvider>
);
export default Demo;
這塊涉及到主題樣式的主要是componentSize
配置項和組件配置
2、實戰(zhàn)
以下做個實驗性案例,不要糾結(jié)細(xì)節(jié)哈!
2.1、實驗環(huán)境
- next: 14.1.0
- react:^18
- antd:^5.14.1
√ Would you like to use TypeScript? ... Yes
√ Would you like to use ESLint? ... No
√ Would you like to use Tailwind CSS? ... No
√ Would you like to use `src/` directory? ... No
√ Would you like to use App Router? (recommended) ... Yes
√ Would you like to customize the default import alias (@/*)? ... Yes
√ What import alias would you like configured? ... @/*
2.2、目錄結(jié)構(gòu)
| - app
|- page.tsx
|- theme
|- default
|- index.ts
|- custom
|- index.ts
|- index.ts
|- themeCenter.ts
2.3、相關(guān)文件編寫
2.3.1、page.tsx
主要方便實驗展示
"use client";
import React, { useState } from "react";
import {
SearchOutlined,
AppstoreOutlined,
MailOutlined,
SettingOutlined,
} from "@ant-design/icons";
import { Button, Flex, Tooltip, Menu, Pagination, Divider } from "antd";
import ThemeComponents from "@/theme";
const items: any = [
{
label: "Navigation One",
key: "mail",
icon: <MailOutlined />,
},
];
const App: React.FC = () => {
const [theme, setTheme] = useState("default");
return (
<>
<Flex gap="small">
<Button type="primary" onClick={() => setTheme("default")}>
主題1
</Button>
<Button type="primary" onClick={() => setTheme("custom")}>
主題2
</Button>
</Flex>
<Divider />
<ThemeComponents theme={theme} dark={'light'}>
<Flex gap="small" vertical>
<Flex wrap="wrap" gap="small">
<Button type="primary" shape="circle">
A
</Button>
<Button type="primary" icon={<SearchOutlined />}>
Search
</Button>
<Tooltip title="search">
<Button shape="circle" icon={<SearchOutlined />} />
</Tooltip>
<Button icon={<SearchOutlined />}>Search</Button>
</Flex>
<Menu mode="horizontal" items={items} selectedKeys={["mail"]} />
<Pagination defaultCurrent={1} total={50} />
</Flex>
</ThemeComponents>
</>
);
};
export default App;
2.3.1、ThemeComponents
// 這里僅演示主題切換,其他業(yè)務(wù)邏輯~~~自己探索哈!
import React, { useEffect, useState } from "react";
import { ConfigProvider } from "antd";
import { ThemeModeEnum } from "@/theme/themeCenter.d";
import DefaultTheme from "./default";
import CustomTheme from "./custom";
type Props = {
theme: string;
dark?: boolean,
children: React.ReactNode;
};
const ThemeMap = {
default: DefaultTheme,
custom: CustomTheme,
};
const ThemeComponents = ({ theme = "default", dark, children }: Props) => {
theme = theme ? theme : "default";
const [themeCenter, setThemeCenter] = useState(new ThemeMap[theme]());
const [darkTheme, setDarkTheme] = useState(dark);
useEffect(() => {
console.log("theme:", theme);
setThemeCenter(new ThemeMap[theme]());
}, [theme]);
useEffect(() => {
console.log("darkTheme:", dark);
if(themeCenter){
themeCenter.ThemeMode = dark;
setDarkTheme(dark);
}
}, [dark]);
return (
<ConfigProvider {...themeCenter?.ThemeConfigProvider}>
{children}
</ConfigProvider>
);
};
export default ThemeComponents;
2.3.1、Theme管理
這部分主要涉及兩個部分:基礎(chǔ)主題類
和繼承主題類
繼承主題類
這部分主要用于不同主題樣式的具體化配置
按主題目錄區(qū)分,方便主題做其他更復(fù)雜的擴展預(yù)留空間
// 案例file: theme/default/index.ts
import ThemeCenter from "../themeCenter"
class ThemeConfiguration extends ThemeCenter
{
// 這是父級ThemeCenter下放的初始化方法,主題初始化在這里進(jìn)行
// 除_initThemeConfig方法外,其他的可自行定義
protected _initThemeConfig(){
// 設(shè)置主題色
this.ThemeColor = '#FF5C00';
// 設(shè)置基礎(chǔ)主題樣式Token
this.setThemeAllToken({
fontSize: 14,
colorLink: '#1890ff',
}, 'token')
// 設(shè)置組件樣式Token
this.LayoutComponentsToken();
this.MenuComponentsToken();
// ConfigProvider組件默認(rèn)配置
this.setThemeConfigProvider({
componentSize: 'small'
})
}
protected LayoutComponentsToken(){
this.setThemeComponentsToken('Layout',{
headerBg: '#fff',
headerColor: '#333',
headerHeight: 35,
headerPadding: '0 16px 0 0',
lightSiderBg: 'transparent',
siderBg: '#fff',
bodyBg: 'transparent',
// footerBg: '#f2f3f5',
// footerPadding: '24px 0'
});
}
protected MenuComponentsToken(){
// @link https://ant.design/components/menu-cn#%E4%B8%BB%E9%A2%98%E5%8F%98%E9%87%8Fdesign-token
this.setThemeComponentsToken('Menu',{
collapsedWidth: 46
// itemBg: "rgba(255,255,255, .85)",
// darkItemBg: 'var(--ant-layout-sider-bg)',
// darkItemColor: 'rgba(0,0,0, .65)',
// darkItemDisabledColor: 'rgba(0,0,0, .25)',
// darkItemHoverBg: 'rgba(255,92,0, .65)',
// darkItemHoverColor: '#fff',
// darkPopupBg: '#181818',
// darkSubMenuItemBg: 'var(--ant-layout-sider-bg)',
})
}
}
export default ThemeConfiguration;
基礎(chǔ)主題類文章來源:http://www.zghlxwxcb.cn/news/detail-855549.html
// file: /theme/themeCenter.ts
import type {
ThemeConfig,
ThemeComponentsConfig,
ThemeConfigProviderProps
} from "./themeCenter.d"
import { ThemeModeEnum } from "./themeCenter.d"
import {theme} from "antd";
class ThemeCenter {
private themeName = "default";
private themeColor:string = '#FF5C00';
private themeMode:ThemeModeEnum = ThemeModeEnum.AUTO;
/**
* 明暗算法配置
* @var {object} _algorithm
*/
private _algorithm = {
light: theme.defaultAlgorithm,
dark: theme.darkAlgorithm
}
private _lightAlgorithm = this._algorithm['light'];
private _darkAlgorithm = this._algorithm['dark'];
/**
* 自定義主題配置
* @link https://ant.design/docs/react/customize-theme-cn#theme
* @var {ThemeConfig} _customThemeToken
*/
private _customThemeToken:ThemeConfig = {
token: {},
// 繼承上層 ConfigProvider 中配置的主題
inherit: true,
algorithm: this._algorithm['light'],
components: {},
// 開啟 CSS 變量,參考使用 CSS 變量
// @link https://ant.design/docs/react/css-variables-cn#api
cssVar: {
prefix: 'bogoo',
key: 'theme',
},
// 組件 class Hash 值
hashed: true,
}
/**
* 自定義ConfigProvider組件配置
*
* @var {ThemeConfigProviderProps} _customConfigProvider
*/
private _customConfigProvider:ThemeConfigProviderProps = {
componentSize: undefined,
theme: this._customThemeToken
}
constructor() {this._initThemeConfig();}
protected _initThemeConfig(){}
/**獲取主題名稱*/
public get ThemeName(){return this.themeName;}
/**獲取當(dāng)前主題色*/
public get ThemeColor(){return this.themeColor;}
public set ThemeColor(color: string){
this.themeColor = color;
this.setThemeAllToken({colorPrimary: color}, 'token')
}
/**獲取明暗色系名稱*/
public get ThemeMode(){return this.themeMode;}
/**設(shè)置明暗主題色配置*/
public set ThemeMode(mode: ThemeModeEnum){
this.themeMode = mode;
let _algorithm: any = this._lightAlgorithm;
if (mode === ThemeModeEnum.AUTO) {
// _algorithm = this._darkAlgorithm;
}else{
_algorithm = mode===ThemeModeEnum.LIGHT ? this._lightAlgorithm : this._darkAlgorithm;
}
this.setThemeAllToken({algorithm: _algorithm});
}
/**主題Token配置*/
public get ThemeToken(){return this._customThemeToken;}
/**
* 設(shè)置主題Token配置
*
* @param {ThemeConfig|ThemeComponentsConfig} token 全局主題token或組件token
* @param {'token'|'algorithm'|'components'} field 可選,若指定配置名稱,則僅更新對應(yīng)配置
*
* @return {ThemeConfig}
*/
public setThemeAllToken(
token: ThemeConfig|ThemeComponentsConfig,
field?:'token'|'algorithm'|'components'
){
let _token:any = {};
let _updateToken = token;
if (field){
if (!['token','algorithm','components'].includes(field))return this._customThemeToken;
if (_updateToken instanceof Array){
// @ts-ignore
_token[field] = this._customThemeToken[field].concat(_updateToken)
}else if(typeof _updateToken === 'object'){
_token[field] = Object.assign({}, this._customThemeToken[field]||{}, _updateToken)
}else{
_token[field] = _updateToken
}
}else{
_token = _updateToken;
}
console.log('_token:', _token)
Object.assign(this._customThemeToken, _token);
return this._customThemeToken;
}
/**
* 組件主題Token配置
*
* @param {string} componentsName 組件名稱(首字母大小)
* @param {ThemeComponentsConfig} token 主題樣式配置
*
* @return {void}
*/
public setThemeComponentsToken(componentsName:string, token: ThemeComponentsConfig){
this.setThemeAllToken({
// @ts-ignore
[componentsName]: Object.assign({} ,this._customThemeToken?.components[componentsName]||undefined, token)
}, 'components')
}
/**ConfigProvider組件配置*/
public get ThemeConfigProvider(){return this._customConfigProvider;}
public setThemeConfigProvider(config:ThemeConfigProviderProps){
Object.assign(this._customConfigProvider, config);
}
}
export default ThemeCenter;
補充
themeCenter.d.ts
import type {
ThemeConfig as AntdThemeConfig,
ComponentsConfig as AntdComponentsConfig,
} from "antd/es/config-provider/context";
import type {SizeType} from "antd/es/config-provider/SizeContext";
import type {ReactNode} from "react";
export enum ThemeModeEnum {
AUTO = 'auto',
DARK = 'dark',
LIGHT = 'light'
}
export interface ThemeConfigProviderProps {
componentSize?: SizeType;
theme?:AntdThemeConfig;
}
export interface ThemeConfig extends AntdThemeConfig {}
export interface ThemeComponentsConfig extends AntdComponentsConfig {}
沒啦!學(xué)廢了嘛???。?!文章來源地址http://www.zghlxwxcb.cn/news/detail-855549.html
到了這里,關(guān)于【React】Ant Design自定義主題風(fēng)格及主題切換的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!