【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';

        // 设置基础主题样式Token
        this.setThemeAllToken({
            fontSize: 14,
            colorLink: '#1890ff',
        }, 'token')

        // 设置组件样式Token
        this.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-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;

基础主题类

// 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;}
    /**获取当前主题色*/
    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 {}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ffmpeg入门

ffmpeg入——安装 Fmpeg地址 FFmpeg源码地址&#xff1a;https://github.com/FFmpeg/FFmpeg FFmpeg可执行文件地址&#xff1a;https://ffmpeg.org/download.html Windows平台 Windows平台下载解压后如图所示&#xff08;文件名称以-share结尾的是开发库&#xff09; FFmpeg…

Eagle for Mac v1.9.13注册版:强大的图片管理工具

Eagle for Mac是一款专为Mac用户设计的图片管理工具&#xff0c;旨在帮助用户更高效、有序地管理和查找图片资源。 Eagle for Mac v1.9.13注册版下载 Eagle支持多种图片格式&#xff0c;包括JPG、PNG、GIF、SVG、PSD、AI等&#xff0c;无论是矢量图还是位图&#xff0c;都能以清…

AndroidStudio AGP 7+, 编译aar并输出到本地仓库

1 编写构建gradle脚本代码 1.1 配置publication和repository 在指定moudle目录下新建名为"maven-publish.gradle"文件&#xff0c;其声明的publication和repository如下所示&#xff1a; apply plugin: maven-publish// This creates a task called publishReleas…

《星光对话》系列直播:带你入门数据要素

2020年12月9日&#xff0c;财政部提出企业数据资源可作为资产列入财务报表&#xff0c;打响数据要素“1N”的第一枪&#xff1b; 2022年12月2日&#xff0c;《关于构建数据基础制度更好发挥数据要素作用的意见》“数据二十条”通过提出构建数据产权、流通交易、收益分配、安全治…

维护SQLite的私有分支(二十六)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite、MySQL 和 PostgreSQL 数据库速度比较&#xff08;本文阐述时间很早比较&#xff0c;不具有最新参考性&#xff09;&#xff08;二十五&#xff09; 下一篇&#xff1a;SQLite数据库中JSON 函数和运算符 1…

# 从浅入深 学习 SpringCloud 微服务架构(三)注册中心 Eureka(1)

从浅入深 学习 SpringCloud 微服务架构&#xff08;三&#xff09;注册中心 Eureka&#xff08;1&#xff09; 段子手168 1、微服务的注册中心 注册中心可以说是微服务架构中的”通讯录”&#xff0c;它记录了服务和服务地址的映射关系。 在分布式架构中服务会注册到这里&am…

美易官方:美债美元黄金继续涨?

全球金融市场波动加剧&#xff0c;投资者对避险资产的需求不断升温。在这一背景下&#xff0c;“投行老将”们纷纷发表观点&#xff0c;认为避险情绪尚未结束&#xff0c;美债、美元和黄金等避险资产有望继续上涨。 巴克莱一位资深投资银行家表示&#xff0c;由于担心中东冲突升…

在Linux系统中搜索当前路径及其子目录下所有PDF文件中是否包含特定字符串

目录标题 方法一&#xff1a;pdfgrep方法二&#xff1a;使用find和xargs与pdftotext&#xff08;将PDF转换为文本&#xff09;组合&#xff0c;然后用grep搜索 方法一&#xff1a;pdfgrep pdfgrep -ri "rockchip" .方法二&#xff1a;使用find和xargs与pdftotext&am…

动手学深度学习11 权重衰退

动手学深度学习11 权重衰退 1. 权重衰退2. 代码实现3. QA 视频&#xff1a; https://www.bilibili.com/video/BV1UK4y1o7dy/?spm_id_fromautoNext&vd_sourceeb04c9a33e87ceba9c9a2e5f09752ef8 电子书&#xff1a; ttps://zh-v2.d2l.ai/chapter_multilayer-perceptrons/wei…

Mamba 学习

Vision Mamba U-Mamba 以后的趋势&#xff1a; 1.Mamba模型机机制上和transform一样&#xff0c;但是参数量上做了改进&#xff0c;可以直接替代 2.vision上可以实时处理

视频太大怎么压缩变小?8种方法随时压缩视频大小

视频太大怎么压缩变小&#xff1f;视频压缩方式分为两种&#xff0c;有损压缩和无损压缩&#xff0c;什么是有损什么是无损压缩&#xff0c;什么时候视频用无损压缩更好&#xff1f;什么时候用有损压缩更好&#xff1f;如何调整视频参数实现基本无损压缩&#xff1f; 今天就借助…

小红书笔记写作方法和技巧分享,纯干货!

很多小伙伴感叹小红书笔记流量就是一个玄学&#xff0c;有时精心撰写的笔记却没有人看&#xff0c;自己随便写的笔记却轻轻松松上热门。实际上你还是欠点火候&#xff0c;小红书笔记写作是有一套方法和技巧的&#xff0c;总归是有套路的&#xff0c;如果你不知道&#xff0c;请…

数仓建模—物理数据模型

文章目录 数仓建模—物理数据模型什么是物理数据模型物理数据模型示例如何构建物理数据模型物理数据模型与逻辑数据模型逻辑模型和物理模型之间有什么关系逻辑数据模型的好处物理数据模型的好处数仓建模—物理数据模型 前面我们讲了数据模型和逻辑数据模型,你可以参考前面的文…

【JAVA进阶篇教学】第四篇:JDK8中函数式接口

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第四篇&#xff1a;JDK8中函数式接口。 在 Java 8 中&#xff0c;函数式接口是指只包含一个抽象方法的接口。这种接口可以用作 Lambda 表达式的类型&#xff0c;从而使得函数式编程在 Java 中变得更加方便和灵活。下面…

【题解】NC398 腐烂的苹果(多源BFS)

https://www.nowcoder.com/practice/54ab9865ce7a45968b126d6968a77f34?tpId196&tqId40529&ru/exam/oj 从每个腐烂的苹果开始使用广度优先遍历&#xff08;bfs&#xff09; class Solution {int n, m;int dx[4] {0, 0, 1, -1};int dy[4] {1, -1, 0, 0};vector<v…

C++ STL 容器 deque

目录 1. deque 对象1.1 内部表示1.2 底层数据结构1.3 分段连续1.4 重新分配 2. deque 迭代器 本文测试环境 gcc 13.1 1. deque 对象 1.1 内部表示 deque 为我们提供了一个双端队列&#xff0c;即可以在队头进行 push、pop&#xff0c;可以在队尾进行 push、pop deque 容器擅…

电弧的产生机理

目录&#xff1a; 1、起弧机理 2、电弧特点 3、电弧放电特点 4、实际意义 1&#xff09;电力开关装置 2&#xff09;保险丝 1、起弧机理 电弧的本质是一种气体放电现象&#xff0c;可以理解为绝缘情况下产生的高强度瞬时电流。起弧效果如下图所示&#xff1a; 在电场的…

pyplot+pandas实现操作excel及画图

1、安装jupyter lab pip install jupyterlab # 启动 建议在指定的项目文件夹下 开启cmd窗口并执行 jupyter lab 启动后会自动打开浏览器访问 2、安装依赖 pip install matplotlib pip install xlrd pip install pandas 3、读取excel import pandas as pddf pd.read_excel(hi…

一文带你了解什么是国际短信

什么是国际短信&#xff1f; 国际短信&#xff0c;也叫国际短消息&#xff0c;是指中国大陆以外的国家或地区运营商与用户之间发送和接收短信息的业务&#xff0c;是一种实现国际间沟通交流与信息触达的便捷方式&#xff0c;可广泛应用于出海游戏、跨境电商、在线社交、实体零…

「探索C语言内存:动态内存管理解析」

&#x1f320;先赞后看&#xff0c;不足指正!&#x1f320; &#x1f388;这将对我有很大的帮助&#xff01;&#x1f388; &#x1f4dd;所属专栏&#xff1a;C语言知识 &#x1f4dd;阿哇旭的主页&#xff1a;Awas-Home page 目录 引言 1. 静态内存 2. 动态内存 2.1 动态内…