【React】Ant Design自定义主题风格及主题切换

Ant Design 的自定义主题,对于刚入手的时候感觉真是一脸蒙圈,那今天给它梳理倒腾下;

在这里插入图片描述

1、自定义主题要点

整体样式变化,主要两个部分:

1.1、Design Token

https://ant.design/docs/react/customize-theme-cn#theme

官方介绍一大堆,咱们粗暴点就当作key=>value配置内容来看和理解!
大体分为四块配置项:

分类涉及配置项
通用/基本样式token可查阅SeedToken、MapToken、AliasToken
组件样式components查阅各个组件最底下的主题变量(Design Token)内容
样式算法algorithm这块其实就算UI库内部自动帮你换算不同“等级”颜色color值
扩展配置inherit、cssVar、hashed这块应该很少会去改它,做主题切换的时候建议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、实战

以下做个实验性案例,不要纠结细节哈!

2.1、实验环境

  • 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、目录结构

| - app|- page.tsx
|- theme|- default|- index.ts|- custom|- index.ts|- index.ts|- themeCenter.ts

2.3、相关文件编写

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

// 这里仅演示主题切换,其他业务逻辑~~~自己探索哈!
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管理

这部分主要涉及两个部分:基础主题类继承主题类

继承主题类
这部分主要用于不同主题样式的具体化配置
按主题目录区分,方便主题做其他更复杂的扩展预留空间

// 案例file: theme/default/index.ts
import ThemeCenter from "../themeCenter"class ThemeConfiguration extends ThemeCenter
{// 这是父级ThemeCenter下放的初始化方法,主题初始化在这里进行// 除_initThemeConfig方法外,其他的可自行定义protected _initThemeConfig(){ // 设置主题色this.ThemeColor = '#FF5C00';// 设置基础主题样式Tokenthis.setThemeAllToken({fontSize: 14,colorLink: '#1890ff',}, 'token')// 设置组件样式Tokenthis.LayoutComponentsToken();this.MenuComponentsToken();// ConfigProvider组件默认配置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-tokenthis.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;

基础主题类

// file: /theme/themeCenter.tsimport 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#apicssVar: {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;}/**获取当前主题色*/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;}/**设置明暗主题色配置*/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;}/*** 设置主题Token配置** @param {ThemeConfig|ThemeComponentsConfig} token 全局主题token或组件token* @param {'token'|'algorithm'|'components'} field 可选,若指定配置名称,则仅更新对应配置** @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 {}

没啦!学废了嘛??!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/625237.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pytorch搭建GoogleNet神经网络

一、创建卷积模板文件 因为每次使用卷积层都需要调用Con2d和relu激活函数&#xff0c;每次都调用非常麻烦&#xff0c;就将他们打包在一起写成一个类。 in_channels&#xff1a;输入矩阵深度作为参数输入 out_channels: 输出矩阵深度作为参数输入 经过卷积层和relu激活函数…

计算机网络----第十二天

交换机端口安全技术和链路聚合技术 1、端口隔离技术&#xff1a; 用于在同vlan内部隔离用户&#xff1b; 同一隔离组端口不能通讯&#xff0c;不同隔离组端口可以通讯; 2、链路聚合技术&#xff1a; 含义&#xff1a;把连接到同一台交换机的多个物理端口捆绑为一个逻辑端口…

KNIME 国际化支持投票

你的投票也许能让 KNIME 中文化快一点点。 i18n 是个很搞笑的单词&#xff0c;它是英文 internationalization 国际化的缩写。18 指的是首字母i和末字母n中间有18个字母。另外还有什么 K8s 也是一样&#xff0c;中间省去了8个字母 ... 真是懒的可以。指北君还想起一个类似的笑话…

冯喜运:4.16晚间关注原油EIA数据黄金原油分析

【 黄金技术面分析】&#xff1a;周二黄金价格日内走跌&#xff0c;白盘低点触及2363一线止跌反弹&#xff0c;实时现报价2372一线。目前小时线布林带逐步收口&#xff0c;上轨位于2392一线&#xff0c;下轨布林带在2351位置。今晚阻力关注2389和今日高点2392区域&#xff0c;其…

24年重庆三支一扶报名个人信息如何填写?

⏰报名时间&#xff1a;2024年4月15日上午9:00至4月19日上午9:00 &#x1f535;报名路径&#xff1a;重庆人力资源和社会保障局官网——首页下方找到 “我要办”——点击进入 【人事考试网上报名】板块。 &#x1f447;&#x1f3fb;开始报名&#xff0c;个人信息填写模板有&am…

STM32 PB3 PB4 无法作为 GPIO 使用解决办法

如下所示&#xff0c;PA13 PA14 PB3 PB4 PB5, 默认是JTAG SWD的 PIN, 需要引脚ReMap 才能作为GPIO 使用。 HAL库解决办法 // __HAL_AFIO_REMAP_SWJ_ENABLE(); //Full SWJ (JTAG-DP SW-DP):// __HAL_AFIO_REMAP_SWJ_NONJTRST(); //Full SWJ (JTAG-DP SW-DP) but without NJTR…

FAGLL03H 新增自定义字段

1、SGLPOS_N_GL_CT、SGLPOS_N_CT两个结构新增自定义字段 2、执行t-code:HDBVIEWS 3、实施增强 FAGL_LIB 4、使用select data方法 5、代码示例: method IF_FAGL_LIB~SELECT_DATA.FIELD-SYMBOLS: <fs> TYPE any.FIELD-SYMBOLS <ls_data> TYPE any.F…

【javaWeb 第七篇】后端-Spring

Spring SpringspringBoot请求简单参数实体参数数组集合参数日期参数JSON参数路径参数 响应数据分层解耦三层架构解耦操作Bean的声明Bean组件扫描问题DI详解 Spring 详细介绍结合官网查看&#xff1a;https://spring.io/why-spring Spring发展到今天已经形成一种开发生态圈&…

计算机组成原理-->真值,机器数,定点数,浮点数,原反补

1.真值和机器数 真值&#xff1a;正、负号加某进制数绝对值的形式&#xff0c;即机器数所代表的实际值。如-1100 机器数&#xff1a;一个数值数据的机内编码&#xff0c;即符号和数值都数码化的数。常用的有原码和补码表示法。如11100. 2.定点数和浮点数的定义 在计算机中&…

redis五种类型介绍

Redis是一种内存数据存储系统&#xff0c;它支持五种不同的数据类型&#xff1a; 1. String String是Redis中最基本的数据类型&#xff0c;它可以存储任何形式的字符串数据&#xff0c;例如普通的文本字符串&#xff0c;二进制数据或JSON格式的数据。除此之外&#xff0c;还可以…

STM32有什么高速接口吗?

STM32系列微控制器在高速接口方面也提供了一些强大的功能&#xff0c;虽然没有像Zynq那样的可编程逻辑部分&#xff0c;但有一些特性值得注意。我这里有一套嵌入式入门教程&#xff0c;不仅包含了详细的视频 讲解&#xff0c;项目实战。如果你渴望学习嵌入式&#xff0c;不妨点…

python创建word文档并向word中写数据

一、docx库的安装方法 python创建word文档需要用到docx库&#xff0c;安装命令如下&#xff1a; pip install python-docx 注意&#xff0c;安装的是python-docx。 二、使用方法 使用方法有很多&#xff0c;这里只介绍创建文档并向文档中写入数据。 import docxmydocdocx.Do…