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

news2024/11/25 10:50:23

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 {}

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新经济助推高质量发展“大有云钞”聚焦未来趋势

近日&#xff0c;由大有云钞科技&#xff08;北京&#xff09;有限公司主办的一场关于“新经济助力高质量发展法治研讨会”在北京国家会议中心隆重举行。此次研讨会汇聚了来自政府、企业、学术界和法律界的众多专家学者&#xff0c;共同探讨新经济背景下的法治建设和高质量发展…

0基础如何入门编程?

0基础如何进入IT行业 &#xff1f; 前言 简介&#xff1a;对于没有任何相关背景知识的人来说&#xff0c;如何才能成功进入IT行业&#xff1f;是否有一些特定的方法或技巧可以帮助他们实现这一目标&#xff1f; 主要方法有如下几点建议提供给宝子们 目录 免费视频网课学习…

static+单例模式+类的复合继承

汇编语言 汇编语言是最靠谱的验证“编程语言相关知识点”正确性的方式 汇编语言与机器语言一一对应&#xff0c;每一条机器语言都有与之对应的汇编指令 机器语言是计算机使用的语言&#xff0c;它是一串二进制数字 汇编语言可以通过汇编得到机器语言机器语言可以通过反汇编得到…

Shell循环以及条件语句使用

Shell脚本基础已经发过&#xff0c;可在主页查找&#xff0c;现在讲解case&#xff0c;for&#xff0c;while语句&#xff0c;以及语句的练习。 1.case语句 等同于C语⾔的switch-case 格式&#xff1a; case $变量 in # 判断变量的值 a) # 值是什么语句;; # 相当于break 但…

docker网路和主机通讯问题

#注 1&#xff0c;安装docker和启动容器服务的时候如果防火墙处于开启状态&#xff0c;那么重启docker里面的容器的时候必须开启防火墙&#xff0c;否则会出现iptable错误&#xff1b; 2&#xff0c;linux开启防火墙会导致主机和docker网络之间单向通讯&#xff0c;主机可以访…

文献速递:深度学习肝脏肿瘤诊断---基于深度学习的表型分类重新划分联合肝细胞胆管癌

Title 题目 Deep learning-based phenotyping reclassifies combined hepatocellular cholangiocarcinoma 基于深度学习的表型分类重新划分联合肝细胞胆管癌 01文献速递介绍 Primary liver cancer arises either from hepatocytic or biliary lineage cells, giving rise to…

会议室预约小程序开源版开发

会议室预约小程序开源版开发 支持设置免费预约和付费预约、积分兑换商城、积分签到等 会议室类目&#xff0c;提供多种类型和设施的会议室选择&#xff0c;满足不同会议需求。 预约日历&#xff0c;展示会议室预约情况&#xff0c;方便用户选择空闲时段。 预约记录&#xff0…

计算机网络(六)应用层

应用层 基本概念 服务器端&#xff08;Server&#xff09;&#xff1a; 服务器是网络中提供服务的计算机或软件程序。服务器通常具有更高的性能、更大的存储空间和更高的带宽&#xff0c;用于提供各种服务&#xff0c;如文件存储、数据库管理、Web托管、电子邮件传递等。服务…

5-pytorch-torch.nn.Sequential()快速搭建神经网络

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言torch.nn.Sequential()快速搭建网络法1 生成数据2 快速搭建网络3 训练、输出结果 总结 前言 本文内容还是基于4-pytorch前馈网络简单&#xff08;分类&#xf…

SQL刷题---2021年11月每天新用户的次日留存率

解题思路&#xff1a; 1.首先算出每个新用户注册的日期,将其命名为表a select uid,min(date(in_time)) dt from tb_user_log group by uid2.计算出每个用户登录的天数,将其命名为表b select uid,date(in_time) dt from tb_user_log union select uid,date(out_time) dt fro…

【Windows10】Anaconda3安装+pytorch+tensorflow+pycharm

文章目录 一、下载anaconda0.双击下载的文件1. 选择All users2. 安装路径3. 勾选环境变量和安装python4.安装完成5.添加环境变量6.测试是否安装成功 二、安装pytorch&#xff08;先看四&#xff01;先检查一下自己电脑是不是只能安装GPU版的1.查看conda图形化界面2.在安装pytor…

PHP-extract变量覆盖

[题目信息]&#xff1a; 题目名称题目难度PHP-extract变量覆盖1 [题目考点]&#xff1a; 变量覆盖指的是用我们自定义的参数值替换程序原有的变量值&#xff0c;一般变量覆盖漏洞需要结合程序的其它功能来实现完整的攻击。 经常导致变量覆盖漏洞场景有&#xff1a;$$&#x…

【Git】安装 Git

文章目录 1. CentOS 下安装2. Ubuntu 下安装 Git 是开放源代码的代码托管工具&#xff0c;最早是在 Linux 下开发的。开始也只能应用于 Linux 平台&#xff0c;后面慢慢的被移植到 Windows 下。现在&#xff0c;Git 可以在 Linux、Unix、Mac 和 Windows 这几大平台上正常运行了…

RabbitMQ的简单

前言 RabbitMQ是一套开源&#xff08;MPL&#xff09;的消息队列服务软件&#xff0c;是由 LShift 提供的一个 Advanced Message Queuing Protocol (AMQP) 的开源实现&#xff0c;由以高性能、健壮以及可伸缩性出名的 Erlang 写成。 目录 介绍 RabbitMQ系统结构 RabbitMQ成员…

Flutter 插件站新升级: 加入优秀 GitHub 开源项目

Flutter 插件站新升级: 加入优秀 GitHub 开源项目 视频 https://youtu.be/qa49W6FaDGs https://www.bilibili.com/video/BV1L1421o7fV/ 前言 原文 https://ducafecat.com/blog/flutter-awesome-github-repo-download 这几天晚上抽空把 Flutter 插件站升级&#xff0c;现在支…

超越GPT-4V,苹果多模态大模型上新,神经网络形态加速MLLM(一)

4月8日&#xff0c;苹果发布了其最新的多模态大语言模型&#xff08;MLLM &#xff09;——Ferret-UI&#xff0c;能够更有效地理解和与屏幕信息进行交互&#xff0c;在所有基本UI任务上都超过了GPT-4V&#xff01; 苹果开发的多模态模型Ferret-UI增强了对屏幕的理解和交互&am…

智谱AI通用大模型:官方开放API开发基础

目录 一、模型介绍 1.1主要模型 1.2 计费单价 二、前置条件 2.1 申请API Key 三、基于SDK开发 3.1 Maven引入SDK 3.2 代码实现 3.3 运行代码 一、模型介绍 GLM-4是智谱AI发布的新一代基座大模型&#xff0c;整体性能相比GLM3提升60%&#xff0c;支持128K上下文&#x…

实用图像视频修复工具:完善细节、提高分辨率 | 开源日报 No.225

xinntao/Real-ESRGAN Stars: 25.6k License: BSD-3-Clause Real-ESRGAN 是一个旨在开发实用的图像/视频恢复算法的项目。 该项目主要功能、关键特性和核心优势包括&#xff1a; 提供动漫视频小模型和动漫插图模型支持在线 Colab 演示和便携式 Windows/Linux/MacOS 可执行文件…

Flex弹性盒子布局案例(认识弹性布局)

一、导航菜单 此示例创建了一个水平导航菜单&#xff0c;其中链接在 Flex 容器中等距分布。 HTML结构&#xff1a; <nav class"nav-menu"><a href"#">Home</a><a href"#">About</a><a href"#">…

【云计算】混合云分类

《混合云》系列&#xff0c;共包含以下 3 篇文章&#xff1a; 【云计算】混合云概述【云计算】混合云分类【云计算】混合云组成、应用场景、风险挑战 &#x1f60a; 如果您觉得这篇文章有用 ✔️ 的话&#xff0c;请给博主一个一键三连 &#x1f680;&#x1f680;&#x1f68…