import React, { useCallback, useEffect, useState, Suspense, useLayoutEffect } from 'react'; import { enUS, ThemeEditor, zhCN } from 'antd-token-previewer'; import { Button, ConfigProvider, message, Modal, Spin, Typography } from 'antd'; import type { ThemeConfig } from 'antd/es/config-provider/context'; import { Helmet } from 'dumi'; import { css } from '@emotion/react'; import type { JSONContent, TextContent } from 'vanilla-jsoneditor'; import useLocale from '../../hooks/useLocale'; const JSONEditor = React.lazy(() => import('../../theme/common/JSONEditor')); function isObject(target: any) { return Object.prototype.toString.call(target) === '[object Object]'; } const locales = { cn: { title: '主题编辑器', save: '保存', edit: '编辑', export: '导出', editModelTitle: '编辑主题配置', editJsonContentTypeError: '主题 JSON 格式错误', editSuccessfully: '编辑成功', saveSuccessfully: '保存成功', initialEditor: '正在初始化编辑器...', }, en: { title: 'Theme Editor', save: 'Save', edit: 'Edit', export: 'Export', editModelTitle: 'edit Theme Config', editJsonContentTypeError: 'The theme of the JSON format is incorrect', editSuccessfully: 'Edited successfully', saveSuccessfully: 'Saved successfully', initialEditor: 'Initializing Editor...', }, }; const useStyle = () => ({ header: css({ display: 'flex', height: 56, alignItems: 'center', padding: '0 24px', justifyContent: 'space-between', borderBottom: '1px solid #F0F0F0', }), }); const ANT_DESIGN_V5_THEME_EDITOR_THEME = 'ant-design-v5-theme-editor-theme'; const CustomTheme = () => { const [messageApi, contextHolder] = message.useMessage(); const [locale, lang] = useLocale(locales); const [theme, setTheme] = React.useState({}); const [editModelOpen, setEditModelOpen] = useState(false); const [editThemeFormatRight, setEditThemeFormatRight] = useState(true); const [themeConfigContent, setThemeConfigContent] = useState({ text: '{}', json: undefined, }); useLayoutEffect(() => { const storedConfig = localStorage.getItem(ANT_DESIGN_V5_THEME_EDITOR_THEME); if (storedConfig) { setTheme(() => JSON.parse(storedConfig)); } }, []); useEffect(() => { if (editModelOpen === true) return; setThemeConfigContent({ json: theme as any, text: undefined, }); }, [theme, editModelOpen]); const styles = useStyle(); const handleSave = () => { localStorage.setItem(ANT_DESIGN_V5_THEME_EDITOR_THEME, JSON.stringify(theme)); messageApi.success(locale.saveSuccessfully); }; const handleEditConfig = () => { setEditModelOpen(true); }; const editModelClose = useCallback(() => { setEditModelOpen(false); }, [themeConfigContent]); const handleEditConfigChange = (newcontent, preContent, status) => { setThemeConfigContent(newcontent); if ( Array.isArray(status.contentErrors.validationErrors) && status.contentErrors.validationErrors.length === 0 ) { setEditThemeFormatRight(true); } else { setEditThemeFormatRight(false); } }; const editSave = useCallback(() => { if (!editThemeFormatRight) { message.error(locale.editJsonContentTypeError); return; } const themeConfig = themeConfigContent.text ? JSON.parse(themeConfigContent.text) : themeConfigContent.json; if (!isObject(themeConfig)) { message.error(locale.editJsonContentTypeError); return; } setTheme(themeConfig); editModelClose(); messageApi.success(locale.editSuccessfully); }, [themeConfigContent]); const handleExport = () => { const file = new File([JSON.stringify(theme, null, 2)], `Ant Design Theme.json`, { type: 'text/json; charset=utf-8;', }); const tmpLink = document.createElement('a'); const objectUrl = URL.createObjectURL(file); tmpLink.href = objectUrl; tmpLink.download = file.name; document.body.appendChild(tmpLink); tmpLink.click(); document.body.removeChild(tmpLink); URL.revokeObjectURL(objectUrl); }; return (
{`${locale.title} - Ant Design`} {contextHolder}
{locale.title}
} >
{ setTheme(newTheme.config); }} locale={lang === 'cn' ? zhCN : enUS} /> ); }; export default CustomTheme;