mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 12:39:49 +08:00
docs: bump theme-editor (#44171)
* chore: fix plugin antd import * chore: code clean * chore: fix image test * chore: fix image test
This commit is contained in:
parent
8368458bf7
commit
ddfe37a14e
@ -1,17 +1,11 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
import { ThemeEditor, enUS, zhCN } from 'antd-token-previewer';
|
||||
import { enUS, zhCN } from 'antd-token-previewer';
|
||||
import { Helmet } from 'dumi';
|
||||
import React, { Suspense, useCallback, useEffect, useState } from 'react';
|
||||
import type { JSONContent, TextContent } from 'vanilla-jsoneditor';
|
||||
import React, { Suspense, useEffect } from 'react';
|
||||
import type { ThemeConfig } from 'antd/es/config-provider/context';
|
||||
import { Button, ConfigProvider, Modal, Spin, Typography, message } from 'antd';
|
||||
import { Button, message, Skeleton } from 'antd';
|
||||
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 ThemeEditor = React.lazy(() => import('antd-token-previewer/lib/ThemeEditor'));
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
@ -38,17 +32,6 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ css }) => ({
|
||||
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 = () => {
|
||||
@ -57,13 +40,6 @@ const CustomTheme = () => {
|
||||
|
||||
const [theme, setTheme] = React.useState<ThemeConfig>({});
|
||||
|
||||
const [editModelOpen, setEditModelOpen] = useState<boolean>(false);
|
||||
const [editThemeFormatRight, setEditThemeFormatRight] = useState<boolean>(true);
|
||||
const [themeConfigContent, setThemeConfigContent] = useState<JSONContent & TextContent>({
|
||||
text: '{}',
|
||||
json: undefined,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const storedConfig = localStorage.getItem(ANT_DESIGN_V5_THEME_EDITOR_THEME);
|
||||
if (storedConfig) {
|
||||
@ -77,61 +53,11 @@ const CustomTheme = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
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);
|
||||
setEditThemeFormatRight(!status.contentErrors);
|
||||
};
|
||||
|
||||
const editSave = useCallback(() => {
|
||||
const contentFormatError = !editThemeFormatRight;
|
||||
|
||||
if (contentFormatError) {
|
||||
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, editThemeFormatRight]);
|
||||
|
||||
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 (
|
||||
<div>
|
||||
<Helmet>
|
||||
@ -139,58 +65,22 @@ const CustomTheme = () => {
|
||||
<meta property="og:title" content={`${locale.title} - Ant Design`} />
|
||||
</Helmet>
|
||||
{contextHolder}
|
||||
<ConfigProvider theme={{ inherit: false }}>
|
||||
<div className={styles.header}>
|
||||
<Typography.Title level={5} style={{ margin: 0 }}>
|
||||
{locale.title}
|
||||
</Typography.Title>
|
||||
<div>
|
||||
<Modal
|
||||
open={editModelOpen}
|
||||
title={locale.editModelTitle}
|
||||
width={600}
|
||||
okText={locale.save}
|
||||
onOk={editSave}
|
||||
onCancel={editModelClose}
|
||||
>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div style={{ textAlign: 'center', width: '100%', padding: '24px 0' }}>
|
||||
<Spin tip={locale.initialEditor} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<JSONEditor
|
||||
content={themeConfigContent}
|
||||
onChange={handleEditConfigChange}
|
||||
mainMenuBar={false}
|
||||
/>
|
||||
</Suspense>
|
||||
</Modal>
|
||||
<Button onClick={handleExport} style={{ marginRight: 8 }}>
|
||||
{locale.export}
|
||||
</Button>
|
||||
<Button onClick={handleEditConfig} style={{ marginRight: 8 }}>
|
||||
{locale.edit}
|
||||
</Button>
|
||||
<Suspense fallback={<Skeleton style={{ margin: 24 }} />}>
|
||||
<ThemeEditor
|
||||
advanced
|
||||
theme={{ name: 'Custom Theme', key: 'test', config: theme }}
|
||||
style={{ height: 'calc(100vh - 64px)' }}
|
||||
onThemeChange={(newTheme) => {
|
||||
setTheme(newTheme.config);
|
||||
}}
|
||||
locale={lang === 'cn' ? zhCN : enUS}
|
||||
actions={
|
||||
<Button type="primary" onClick={handleSave}>
|
||||
{locale.save}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<ThemeEditor
|
||||
theme={{ name: 'Custom Theme', key: 'test', config: theme }}
|
||||
style={{ height: 'calc(100vh - 64px - 56px)' }}
|
||||
onThemeChange={(newTheme) => {
|
||||
setTheme(newTheme.config);
|
||||
setThemeConfigContent({
|
||||
json: newTheme.config,
|
||||
text: undefined,
|
||||
});
|
||||
}}
|
||||
locale={lang === 'cn' ? zhCN : enUS}
|
||||
}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -5,9 +5,28 @@ import type { IApi, IRoute } from 'dumi';
|
||||
import ReactTechStack from 'dumi/dist/techStacks/react';
|
||||
import chalk from 'chalk';
|
||||
import sylvanas from 'sylvanas';
|
||||
import { extractStaticStyle } from 'antd-style';
|
||||
import createEmotionServer from '@emotion/server/create-instance';
|
||||
import localPackage from '../../package.json';
|
||||
|
||||
function extractEmotionStyle(html: string) {
|
||||
// copy from emotion ssr
|
||||
// https://github.com/vercel/next.js/blob/deprecated-main/examples/with-emotion-vanilla/pages/_document.js
|
||||
const styles = global.__ANTD_STYLE_CACHE_MANAGER_FOR_SSR__.getCacheList().map((cache) => {
|
||||
const result = createEmotionServer(cache).extractCritical(html);
|
||||
if (!result.css) return null;
|
||||
|
||||
const { css, ids } = result;
|
||||
|
||||
return {
|
||||
key: cache.key,
|
||||
css,
|
||||
ids,
|
||||
tag: `<style data-emotion="${cache.key} ${result.ids.join(' ')}">${result.css}</style>`,
|
||||
};
|
||||
});
|
||||
return styles.filter(Boolean);
|
||||
}
|
||||
|
||||
export const getHash = (str: string, length = 8) =>
|
||||
createHash('md5').update(str).digest('hex').slice(0, length);
|
||||
|
||||
@ -129,7 +148,7 @@ const RoutesPlugin = (api: IApi) => {
|
||||
file.content = file.content.replace('</head>', `${globalStyles}</head>`);
|
||||
|
||||
// 1. 提取 antd-style 样式
|
||||
const styles = extractStaticStyle(file.content, { includeAntd: false });
|
||||
const styles = extractEmotionStyle(file.content);
|
||||
|
||||
// 2. 提取每个样式到独立 css 文件
|
||||
styles.forEach((result) => {
|
||||
|
@ -11,6 +11,7 @@ export default defineConfig({
|
||||
},
|
||||
ssr: process.env.NODE_ENV === 'production' ? {} : false,
|
||||
hash: true,
|
||||
mfsu: false,
|
||||
crossorigin: {},
|
||||
outputPath: '_site',
|
||||
favicons: ['https://gw.alipayobjects.com/zos/rmsportal/rlpTLlbMzTNYuZGGCVYM.png'],
|
||||
@ -34,7 +35,6 @@ export default defineConfig({
|
||||
},
|
||||
extraRehypePlugins: [rehypeAntd],
|
||||
extraRemarkPlugins: [remarkAntd],
|
||||
mfsu: false,
|
||||
metas: [{ name: 'theme-color', content: '#1677ff' }],
|
||||
analytics: {
|
||||
ga_v2: 'UA-72788897-1',
|
||||
|
2
.jest.js
2
.jest.js
@ -33,6 +33,8 @@ module.exports = {
|
||||
'/\\.(css|less)$/': 'identity-obj-proxy',
|
||||
'^antd$': '<rootDir>/components/index',
|
||||
'^antd/es/(.*)$': '<rootDir>/components/$1',
|
||||
'^antd/lib/(.*)$': '<rootDir>/components/$1',
|
||||
'^antd/locale/(.*)$': '<rootDir>/components/locale/$1',
|
||||
},
|
||||
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node', 'image.test.js', 'image.test.ts'],
|
||||
transform: {
|
||||
|
@ -210,7 +210,7 @@
|
||||
"@typescript-eslint/parser": "^5.40.0",
|
||||
"antd-img-crop": "^4.9.0",
|
||||
"antd-style": "^3.4.2-beta.1",
|
||||
"antd-token-previewer": "^1.1.0-21",
|
||||
"antd-token-previewer": "^2.0.1",
|
||||
"chalk": "^4.0.0",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"circular-dependency-plugin": "^5.2.2",
|
||||
|
@ -38,7 +38,7 @@ export default function imageTest(component: React.ReactElement) {
|
||||
MockDate.set(dayjs('2016-11-22').valueOf());
|
||||
page.on('request', onRequestHandle);
|
||||
await page.goto(`file://${process.cwd()}/tests/index.html`);
|
||||
await page.addStyleTag({ path: `${process.cwd()}/dist/reset.css` });
|
||||
await page.addStyleTag({ path: `${process.cwd()}/components/style/reset.css` });
|
||||
await page.addStyleTag({ content: '*{animation: none!important;}' });
|
||||
|
||||
const cache = createCache();
|
||||
|
Loading…
Reference in New Issue
Block a user