mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-09 19:09:21 +08:00
Compare commits
371 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1cc97c90e | ||
|
|
06a4287d73 | ||
|
|
37a3ef15d8 | ||
|
|
b22a7108cf | ||
|
|
67305c0ace | ||
|
|
4ab542e7c3 | ||
|
|
07f0db2ddc | ||
|
|
2e51288638 | ||
|
|
258573ab93 | ||
|
|
3c9bb681e7 | ||
|
|
3e0b56dacf | ||
|
|
7d35e112ae | ||
|
|
6e55573b5c | ||
|
|
0e4f60dd01 | ||
|
|
0b55335da9 | ||
|
|
40c0bd6bb1 | ||
|
|
98614197cb | ||
|
|
8fd30ab4a2 | ||
|
|
e05aa23faa | ||
|
|
050d1dfa42 | ||
|
|
d90f8ed238 | ||
|
|
185398afdc | ||
|
|
b3adc9a69b | ||
|
|
fbd6fdafbe | ||
|
|
5b9b6f801a | ||
|
|
27826a8136 | ||
|
|
4f53033269 | ||
|
|
c235b1193b | ||
|
|
ff35ebb285 | ||
|
|
0d603d44fa | ||
|
|
2a8a811762 | ||
|
|
cb309acd4a | ||
|
|
e2ce447c47 | ||
|
|
f62d50533b | ||
|
|
e15b8defc9 | ||
|
|
e33444368e | ||
|
|
4a88f6a98a | ||
|
|
f324de2173 | ||
|
|
0362603ae5 | ||
|
|
8def94703b | ||
|
|
67a0e2fee7 | ||
|
|
584a7be26f | ||
|
|
3f5129ac44 | ||
|
|
d937a3930f | ||
|
|
c300c97a3e | ||
|
|
ec953cbffa | ||
|
|
caccb11d6f | ||
|
|
a863062f7b | ||
|
|
584923d35b | ||
|
|
75ddcdfd00 | ||
|
|
9b2054ffec | ||
|
|
b1bb15a753 | ||
|
|
c78056d0a4 | ||
|
|
ba47850fa0 | ||
|
|
ae98485a3e | ||
|
|
9bb27c4100 | ||
|
|
64f7963395 | ||
|
|
6fac5c24bf | ||
|
|
ee8cc27686 | ||
|
|
364bcc74d1 | ||
|
|
7130056493 | ||
|
|
8a4e89fc59 | ||
|
|
6893402469 | ||
|
|
957b027fa1 | ||
|
|
6c5036e285 | ||
|
|
4888842fc4 | ||
|
|
1463722eb5 | ||
|
|
c3710c0102 | ||
|
|
ae9fc640c6 | ||
|
|
1a3b29ec8f | ||
|
|
8ac5c5fde4 | ||
|
|
d7aac85735 | ||
|
|
0cdfaf578a | ||
|
|
c4b105c66a | ||
|
|
1e9b8a997c | ||
|
|
82622f14d5 | ||
|
|
f7a216aaa9 | ||
|
|
a6d83589db | ||
|
|
a01c1293cf | ||
|
|
1721a1af9b | ||
|
|
8e1bdc8121 | ||
|
|
283b2822e0 | ||
|
|
d353c2949c | ||
|
|
1ec7cd744d | ||
|
|
50dc6acbb2 | ||
|
|
cab5910713 | ||
|
|
fbe0a46857 | ||
|
|
8af72472fa | ||
|
|
8ec34d6204 | ||
|
|
7e2f8ce1f0 | ||
|
|
9123bbbfd7 | ||
|
|
24bd6fd311 | ||
|
|
d9eb4ccb4b | ||
|
|
da8e1ccde2 | ||
|
|
89fa656149 | ||
|
|
8399590542 | ||
|
|
e2f3cf6133 | ||
|
|
5471db8383 | ||
|
|
d188f03bb8 | ||
|
|
edb2bde901 | ||
|
|
8fbae86149 | ||
|
|
fba5f4f6cb | ||
|
|
2ccab1e984 | ||
|
|
acd131b7c4 | ||
|
|
353a94f43d | ||
|
|
09b1107c35 | ||
|
|
3abfb8c2dc | ||
|
|
38efd012a2 | ||
|
|
49c4f56100 | ||
|
|
8852b23742 | ||
|
|
f160384640 | ||
|
|
1c5b04e47b | ||
|
|
330c5be52e | ||
|
|
eac8dde1e5 | ||
|
|
3a78aacb3c | ||
|
|
2ff9d89b15 | ||
|
|
3d152a78fd | ||
|
|
0a89f9cf21 | ||
|
|
9af831fd92 | ||
|
|
3686df953a | ||
|
|
d08de4e1de | ||
|
|
ec0bdd2984 | ||
|
|
6ed7b0f045 | ||
|
|
44abdc4cda | ||
|
|
b8b365c2d1 | ||
|
|
0774e5a40a | ||
|
|
a3d31746e3 | ||
|
|
7326f7b119 | ||
|
|
886a1d19cd | ||
|
|
5235b4fe32 | ||
|
|
5c52fea0bf | ||
|
|
39b7edd337 | ||
|
|
dbf71b5059 | ||
|
|
60cd020a35 | ||
|
|
9108b85cf7 | ||
|
|
1341d29312 | ||
|
|
397cb98bd1 | ||
|
|
56ff32425e | ||
|
|
8f616c018e | ||
|
|
a2aadb3fa8 | ||
|
|
5b8fd49fba | ||
|
|
fb6d7405d0 | ||
|
|
0d0f88c71a | ||
|
|
9a62d148c7 | ||
|
|
5efbb093bc | ||
|
|
ea4d320241 | ||
|
|
2ffd8a7012 | ||
|
|
81e7f3fe98 | ||
|
|
dfd962d350 | ||
|
|
742ecee166 | ||
|
|
d680ea3d30 | ||
|
|
c0589e939d | ||
|
|
d1b47edff4 | ||
|
|
24bd72567d | ||
|
|
631ee77e5b | ||
|
|
37738e5c0e | ||
|
|
b3854ca614 | ||
|
|
2f63a43627 | ||
|
|
39380179c0 | ||
|
|
c14f05659a | ||
|
|
e3e26cc1ad | ||
|
|
cec0bd1cd3 | ||
|
|
8393f59cd3 | ||
|
|
c3f41c19ba | ||
|
|
a1cc627635 | ||
|
|
b0a52d7f4d | ||
|
|
92dcdc628e | ||
|
|
60efb8e170 | ||
|
|
e7937847e1 | ||
|
|
6a79a6769b | ||
|
|
c312adea5d | ||
|
|
d12899aa14 | ||
|
|
004d8f2df4 | ||
|
|
3903f93e8a | ||
|
|
6c24803a45 | ||
|
|
e407a9a917 | ||
|
|
d62ec62c1e | ||
|
|
d0f06ba494 | ||
|
|
290add5d08 | ||
|
|
ab3569c7ad | ||
|
|
8bedd4cc82 | ||
|
|
3d0a5b485c | ||
|
|
9f45cb32c2 | ||
|
|
1fdc49c89c | ||
|
|
8703e7e040 | ||
|
|
3becb847d7 | ||
|
|
993d782b70 | ||
|
|
97ef4e304e | ||
|
|
9ec61eff15 | ||
|
|
cf57a72896 | ||
|
|
cb59d0a6f1 | ||
|
|
f792a10eb8 | ||
|
|
e6c8f3629f | ||
|
|
7ddf6bf1e7 | ||
|
|
2069037982 | ||
|
|
cf3d732f5b | ||
|
|
e461682e58 | ||
|
|
e80676a684 | ||
|
|
b11e0bfcf2 | ||
|
|
26b995a8d4 | ||
|
|
1004fa28ea | ||
|
|
915ce1eee1 | ||
|
|
1843baba69 | ||
|
|
ef91a44893 | ||
|
|
84d5c11d52 | ||
|
|
683033afde | ||
|
|
af27ca20fb | ||
|
|
122707938c | ||
|
|
bd149e4574 | ||
|
|
c606b9700a | ||
|
|
7fac2ac5db | ||
|
|
e2be0dd056 | ||
|
|
d6ee579f1f | ||
|
|
19e9833484 | ||
|
|
0bdacb180a | ||
|
|
26bab6fa4c | ||
|
|
bc1db71a0d | ||
|
|
174b88acee | ||
|
|
7936b7ab84 | ||
|
|
7cfc9c637b | ||
|
|
6294ce8f7c | ||
|
|
6caff180c5 | ||
|
|
7a919eb971 | ||
|
|
d4c12a14b4 | ||
|
|
8357f74bbb | ||
|
|
8ba958963e | ||
|
|
2c67f5d0ac | ||
|
|
93419fa663 | ||
|
|
0c293a722b | ||
|
|
56c7c9fe6a | ||
|
|
68928fbd9a | ||
|
|
cf80ca8347 | ||
|
|
8141e78ad8 | ||
|
|
6e0a6f0a46 | ||
|
|
e0689811c5 | ||
|
|
25660f12a2 | ||
|
|
3b545437b0 | ||
|
|
74b22fd416 | ||
|
|
025b9da8e2 | ||
|
|
805bf9d789 | ||
|
|
2231701584 | ||
|
|
1e120afc6e | ||
|
|
2576b441bf | ||
|
|
cf3b82c2b2 | ||
|
|
d5e1e16db2 | ||
|
|
523a911e6c | ||
|
|
88c49f73e9 | ||
|
|
7ed842f1bc | ||
|
|
0349356447 | ||
|
|
82ebd4490e | ||
|
|
6e4db1ed55 | ||
|
|
de4416b3d7 | ||
|
|
263548e3d2 | ||
|
|
37363751ae | ||
|
|
58e3f85dcc | ||
|
|
408afb0626 | ||
|
|
25a37ed20a | ||
|
|
5474b0e2a3 | ||
|
|
cd267f0a3e | ||
|
|
b2f0595c6f | ||
|
|
03eebadee4 | ||
|
|
8dee6d9c97 | ||
|
|
a108f76d5f | ||
|
|
e121c18278 | ||
|
|
39470af064 | ||
|
|
c48295750c | ||
|
|
aefc8bb541 | ||
|
|
0767e87b86 | ||
|
|
c0a23c150c | ||
|
|
2389620077 | ||
|
|
1d9d977936 | ||
|
|
aab7fbf40c | ||
|
|
76fa62258b | ||
|
|
e4661d0f93 | ||
|
|
bff2570b81 | ||
|
|
070ba779b7 | ||
|
|
f8dea9b9c2 | ||
|
|
ca5817ac33 | ||
|
|
8434193ab5 | ||
|
|
0cce449039 | ||
|
|
3ce4a1022a | ||
|
|
ff8df51823 | ||
|
|
08cd435a67 | ||
|
|
7bff382088 | ||
|
|
f9f21e4197 | ||
|
|
8742fec2dd | ||
|
|
b933425527 | ||
|
|
f9720bc975 | ||
|
|
cfc2f8ed8b | ||
|
|
384e4763f0 | ||
|
|
35ddedad6d | ||
|
|
358e3cacd1 | ||
|
|
922bd19ddd | ||
|
|
e41f13c14c | ||
|
|
3e2dac3e1f | ||
|
|
1cc64c50ab | ||
|
|
fd79fd4de5 | ||
|
|
5839651cf8 | ||
|
|
868140ff20 | ||
|
|
0c461f13ec | ||
|
|
964e21fc62 | ||
|
|
b7afbe367b | ||
|
|
44b91c38fe | ||
|
|
d2251f2d1a | ||
|
|
09e60f8e6a | ||
|
|
18a8dadfaa | ||
|
|
0be4d1b38d | ||
|
|
04833f17d8 | ||
|
|
1b271fcafd | ||
|
|
1eaa8464cf | ||
|
|
6a1c837019 | ||
|
|
02669912b7 | ||
|
|
06619d5010 | ||
|
|
fadc5d0f84 | ||
|
|
7c5d0fdd11 | ||
|
|
b2574bdf2b | ||
|
|
09d9a92398 | ||
|
|
f3fcb1416f | ||
|
|
2311d31d1a | ||
|
|
3d89cedb1b | ||
|
|
bcb8f068d5 | ||
|
|
d49b6955c3 | ||
|
|
0f869011bd | ||
|
|
78547e2412 | ||
|
|
6e725877f4 | ||
|
|
8ab213efc4 | ||
|
|
4522b50347 | ||
|
|
283484949b | ||
|
|
55d6950873 | ||
|
|
4fdb00b436 | ||
|
|
e5d7b12977 | ||
|
|
967f9eeca3 | ||
|
|
d36292d02b | ||
|
|
8b536eb128 | ||
|
|
a26733f44f | ||
|
|
481ff1ae31 | ||
|
|
d4c0d3946d | ||
|
|
c7b96d7529 | ||
|
|
594524b4af | ||
|
|
816912441c | ||
|
|
e6656e51b2 | ||
|
|
68090237f1 | ||
|
|
8eb75ab338 | ||
|
|
da2ddb9955 | ||
|
|
f9a543f295 | ||
|
|
04fee95816 | ||
|
|
0b15c2834c | ||
|
|
ec6471a0e1 | ||
|
|
e81da48f84 | ||
|
|
ec44ee3eea | ||
|
|
ef52e15a93 | ||
|
|
37d934c507 | ||
|
|
c4f0f4bd57 | ||
|
|
11170a3133 | ||
|
|
46b45f5fd4 | ||
|
|
33110d500f | ||
|
|
30299ce6f6 | ||
|
|
774aa7c92d | ||
|
|
4daf900b4a | ||
|
|
6b79b3a91f | ||
|
|
f96282a4f9 | ||
|
|
3de02eeb62 | ||
|
|
c64c7211cb | ||
|
|
c89307c505 | ||
|
|
8c9368c9f2 | ||
|
|
c2e000f89d | ||
|
|
1ef709a356 | ||
|
|
7c278a32bf | ||
|
|
a83362af98 | ||
|
|
098b81e9ef | ||
|
|
4719fe9f3a |
@@ -1,7 +1,7 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { Flex, Tag, version } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
import { useFullSidebarData, useSidebarData } from 'dumi';
|
||||
|
||||
@@ -12,29 +12,28 @@ import useLocation from './useLocation';
|
||||
const locales = {
|
||||
cn: {
|
||||
deprecated: '废弃',
|
||||
update: '更新',
|
||||
updated: '更新',
|
||||
new: '新增',
|
||||
},
|
||||
en: {
|
||||
deprecated: 'DEPRECATED',
|
||||
update: 'UPDATE',
|
||||
updated: 'UPDATED',
|
||||
new: 'NEW',
|
||||
},
|
||||
};
|
||||
|
||||
const getTagColor = (val?: string) => {
|
||||
switch (val?.toUpperCase()) {
|
||||
case 'UPDATE':
|
||||
case 'UPDATED':
|
||||
return 'processing';
|
||||
case 'DEPRECATED':
|
||||
return 'red';
|
||||
|
||||
default:
|
||||
return 'success';
|
||||
}
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
link: css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -47,6 +46,7 @@ const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
font-weight: normal;
|
||||
font-size: ${cssVar.fontSizeSM};
|
||||
opacity: 0.8;
|
||||
margin-inline-start: ${cssVar.marginSM};
|
||||
`,
|
||||
}));
|
||||
|
||||
@@ -62,10 +62,10 @@ interface MenuItemLabelProps {
|
||||
}
|
||||
|
||||
const MenuItemLabelWithTag: React.FC<MenuItemLabelProps> = (props) => {
|
||||
const { styles } = useStyle();
|
||||
const { before, after, link, title, subtitle, search, tag, className } = props;
|
||||
|
||||
const [locale] = useLocale(locales);
|
||||
|
||||
const getLocale = (name: string) => {
|
||||
return (locale as any)[name.toLowerCase()] ?? name;
|
||||
};
|
||||
@@ -73,7 +73,7 @@ const MenuItemLabelWithTag: React.FC<MenuItemLabelProps> = (props) => {
|
||||
if (!before && !after) {
|
||||
return (
|
||||
<Link to={`${link}${search}`} className={clsx(className, { [styles.link]: tag })}>
|
||||
<Flex justify="flex-start" align="center" gap="small">
|
||||
<Flex justify="flex-start" align="center">
|
||||
<span>{title}</span>
|
||||
{subtitle && <span className={styles.subtitle}>{subtitle}</span>}
|
||||
</Flex>
|
||||
|
||||
@@ -3,10 +3,14 @@ import { removeCSS, updateCSS } from '@rc-component/util/lib/Dom/dynamicCSS';
|
||||
|
||||
import theme from '../../components/theme';
|
||||
|
||||
const duration = 0.5;
|
||||
const viewTransitionStyle = `
|
||||
@keyframes keepAlive {100% { z-index: -1 }}
|
||||
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation: none;
|
||||
animation: keepAlive ${duration}s linear;
|
||||
animation-fill-mode: forwards;
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
|
||||
@@ -50,7 +54,7 @@ const useThemeAnimation = () => {
|
||||
clipPath: isDark ? [...clipPath].reverse() : clipPath,
|
||||
},
|
||||
{
|
||||
duration: 500,
|
||||
duration: duration * 1000,
|
||||
easing: 'ease-in',
|
||||
pseudoElement: isDark ? '::view-transition-old(root)' : '::view-transition-new(root)',
|
||||
},
|
||||
|
||||
@@ -49,7 +49,8 @@ const useStyle = createStyles(({ cssVar, css, cx }) => {
|
||||
max-width: 100%;
|
||||
margin-inline: auto;
|
||||
box-sizing: border-box;
|
||||
column-gap: calc(${cssVar.paddingMD} * 2);
|
||||
column-gap: ${cssVar.paddingMD};
|
||||
padding: 0 ${cssVar.padding};
|
||||
align-items: stretch;
|
||||
text-align: start;
|
||||
min-height: 178px;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import { CustomerServiceOutlined, QuestionCircleOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Alert,
|
||||
Card,
|
||||
Carousel,
|
||||
DatePicker,
|
||||
Flex,
|
||||
FloatButton,
|
||||
Modal,
|
||||
Progress,
|
||||
Masonry,
|
||||
Splitter,
|
||||
Tag,
|
||||
Tour,
|
||||
Typography,
|
||||
@@ -21,7 +21,6 @@ import SiteContext from '../../../theme/slots/SiteContext';
|
||||
import { DarkContext } from './../../../hooks/useDark';
|
||||
import { getCarouselStyle } from './util';
|
||||
|
||||
const { _InternalPanelDoNotUseOrYouWillBeFired: ModalDoNotUseOrYouWillBeFired } = Modal;
|
||||
const { _InternalPanelDoNotUseOrYouWillBeFired: DatePickerDoNotUseOrYouWillBeFired } = DatePicker;
|
||||
const { _InternalPanelDoNotUseOrYouWillBeFired: TourDoNotUseOrYouWillBeFired } = Tour;
|
||||
const { _InternalPanelDoNotUseOrYouWillBeFired: FloatButtonDoNotUseOrYouWillBeFired } = FloatButton;
|
||||
@@ -148,24 +147,26 @@ const ComponentsList: React.FC = () => {
|
||||
const { styles } = useStyle();
|
||||
const [locale] = useLocale(locales);
|
||||
const { isMobile } = React.use(SiteContext);
|
||||
const isDark = React.use(DarkContext);
|
||||
const COMPONENTS = React.useMemo<Omit<ComponentItemProps, 'index'>[]>(
|
||||
() => [
|
||||
{
|
||||
title: 'Modal',
|
||||
type: 'update',
|
||||
node: (
|
||||
<ModalDoNotUseOrYouWillBeFired title="Ant Design" width={300}>
|
||||
{locale.sampleContent}
|
||||
</ModalDoNotUseOrYouWillBeFired>
|
||||
),
|
||||
},
|
||||
// {
|
||||
// title: 'Modal',
|
||||
// type: 'update',
|
||||
// node: (
|
||||
// <ModalDoNotUseOrYouWillBeFired title="Ant Design" width={300}>
|
||||
// {locale.sampleContent}
|
||||
// </ModalDoNotUseOrYouWillBeFired>
|
||||
// ),
|
||||
// },
|
||||
|
||||
{
|
||||
title: 'DatePicker',
|
||||
type: 'update',
|
||||
node: (
|
||||
<DatePickerDoNotUseOrYouWillBeFired
|
||||
value={dayjs('2022-11-18 14:00:00')}
|
||||
value={dayjs('2025-11-22 00:00:00')}
|
||||
// defaultValue={dayjs('2025-11-22 00:00:00')}
|
||||
showToday={false}
|
||||
presets={
|
||||
isMobile
|
||||
@@ -180,30 +181,30 @@ const ComponentsList: React.FC = () => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
// {
|
||||
// title: 'Progress',
|
||||
// type: 'update',
|
||||
// node: (
|
||||
// <Flex gap="small" vertical>
|
||||
// <Flex gap="small" align="center">
|
||||
// <Progress type="circle" railColor="#e6f4ff" percent={60} size={14} />
|
||||
// {locale.inProgress}
|
||||
// </Flex>
|
||||
// <Flex gap="small" align="center">
|
||||
// <Progress type="circle" percent={100} size={14} />
|
||||
// {locale.success}
|
||||
// </Flex>
|
||||
// <Flex gap="small" align="center">
|
||||
// <Progress type="circle" status="exception" percent={88} size={14} />
|
||||
// {locale.taskFailed}
|
||||
// </Flex>
|
||||
// </Flex>
|
||||
// ),
|
||||
// },
|
||||
|
||||
{
|
||||
title: 'Progress',
|
||||
type: 'update',
|
||||
node: (
|
||||
<Flex gap="small" vertical>
|
||||
<Flex gap="small" align="center">
|
||||
<Progress type="circle" railColor="#e6f4ff" percent={60} size={14} />
|
||||
{locale.inProgress}
|
||||
</Flex>
|
||||
<Flex gap="small" align="center">
|
||||
<Progress type="circle" percent={100} size={14} />
|
||||
{locale.success}
|
||||
</Flex>
|
||||
<Flex gap="small" align="center">
|
||||
<Progress type="circle" status="exception" percent={88} size={14} />
|
||||
{locale.taskFailed}
|
||||
</Flex>
|
||||
</Flex>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Tour',
|
||||
type: 'new',
|
||||
type: 'update',
|
||||
node: (
|
||||
<TourDoNotUseOrYouWillBeFired
|
||||
title="Ant Design"
|
||||
@@ -214,9 +215,10 @@ const ComponentsList: React.FC = () => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
title: 'FloatButton',
|
||||
type: 'new',
|
||||
type: 'update',
|
||||
node: (
|
||||
<Flex align="center" gap="large">
|
||||
<FloatButtonDoNotUseOrYouWillBeFired
|
||||
@@ -246,19 +248,83 @@ const ComponentsList: React.FC = () => {
|
||||
// },
|
||||
|
||||
{
|
||||
title: 'Alert',
|
||||
type: 'update',
|
||||
title: 'Splitter',
|
||||
type: 'new',
|
||||
node: (
|
||||
<Alert
|
||||
style={{ width: 400 }}
|
||||
title="Ant Design"
|
||||
description={locale.sampleContent}
|
||||
closable={{ closeIcon: true, disabled: true }}
|
||||
<Splitter
|
||||
orientation="vertical"
|
||||
style={{
|
||||
height: 320,
|
||||
width: 200,
|
||||
background: isDark ? '#1f1f1f' : '#ffffff',
|
||||
boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)',
|
||||
}}
|
||||
>
|
||||
<Splitter.Panel defaultSize="40%" min="20%" max="70%">
|
||||
<Flex justify="center" align="center" style={{ height: '100%' }}>
|
||||
<Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
|
||||
First
|
||||
</Typography.Title>
|
||||
</Flex>
|
||||
</Splitter.Panel>
|
||||
|
||||
<Splitter.Panel>
|
||||
<Flex justify="center" align="center" style={{ height: '100%' }}>
|
||||
<Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
|
||||
Second
|
||||
</Typography.Title>
|
||||
</Flex>
|
||||
</Splitter.Panel>
|
||||
</Splitter>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Masonry',
|
||||
type: 'new',
|
||||
node: (
|
||||
<Masonry
|
||||
columns={2}
|
||||
gutter={8}
|
||||
style={{
|
||||
width: 300,
|
||||
height: 320,
|
||||
}}
|
||||
items={[
|
||||
{ key: '1', data: 80 },
|
||||
{ key: '2', data: 60 },
|
||||
{ key: '3', data: 40 },
|
||||
{ key: '4', data: 120 },
|
||||
{ key: '5', data: 90 },
|
||||
{ key: '6', data: 40 },
|
||||
{ key: '7', data: 60 },
|
||||
{ key: '8', data: 70 },
|
||||
{ key: '9', data: 120 },
|
||||
]}
|
||||
itemRender={({ data, index }) => (
|
||||
<Card size="small" style={{ height: data }}>
|
||||
{index + 1}
|
||||
</Card>
|
||||
)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
||||
// {
|
||||
// title: 'Alert',
|
||||
// type: 'update',
|
||||
// node: (
|
||||
// <Alert
|
||||
// style={{ width: 400 }}
|
||||
// title="Ant Design"
|
||||
// description={locale.sampleContent}
|
||||
// closable={{ closeIcon: true, disabled: true }}
|
||||
// />
|
||||
// ),
|
||||
// },
|
||||
],
|
||||
[
|
||||
isDark,
|
||||
isMobile,
|
||||
locale.inProgress,
|
||||
locale.lastMonth,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import { Typography } from 'antd';
|
||||
import { createStyles, useTheme } from 'antd-style';
|
||||
import { createStaticStyles, useTheme } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
import SiteContext from '../../../theme/slots/SiteContext';
|
||||
import GroupMaskLayer from './GroupMaskLayer';
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
box: css`
|
||||
position: relative;
|
||||
transition: all ${cssVar.motionDurationSlow};
|
||||
@@ -46,7 +46,6 @@ export interface GroupProps {
|
||||
const Group: React.FC<React.PropsWithChildren<GroupProps>> = (props) => {
|
||||
const { id, title, titleColor, description, children, decoration, background, collapse } = props;
|
||||
const token = useTheme();
|
||||
const { styles } = useStyle();
|
||||
const { isMobile } = React.use(SiteContext);
|
||||
return (
|
||||
<div style={{ backgroundColor: background }} className={styles.box}>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
const useStyle = createStyles(({ css }) => ({
|
||||
const classNames = createStaticStyles(({ css }) => ({
|
||||
siteMask: css`
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
@@ -19,11 +19,10 @@ export interface GroupMaskLayerProps {
|
||||
|
||||
const GroupMaskLayer: React.FC<React.PropsWithChildren<GroupMaskLayerProps>> = (props) => {
|
||||
const { children, className, style, onMouseMove, onMouseEnter, onMouseLeave } = props;
|
||||
const { styles } = useStyle();
|
||||
return (
|
||||
<div
|
||||
style={style}
|
||||
className={clsx(className, styles.siteMask)}
|
||||
className={clsx(className, classNames.siteMask)}
|
||||
onMouseMove={onMouseMove}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Button,
|
||||
Checkbox,
|
||||
ColorPicker,
|
||||
ConfigProvider,
|
||||
Dropdown,
|
||||
Input,
|
||||
message,
|
||||
@@ -17,9 +18,11 @@ import {
|
||||
Switch,
|
||||
Tooltip,
|
||||
} from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import type { ConfigProviderProps } from 'antd';
|
||||
|
||||
import useLocale from '../../../../hooks/useLocale';
|
||||
|
||||
import Tilt from './Tilt';
|
||||
|
||||
const { _InternalPanelDoNotUseOrYouWillBeFired: ModalPanel } = Modal;
|
||||
@@ -71,7 +74,7 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => {
|
||||
const styles = createStaticStyles(({ cssVar, css }) => {
|
||||
const gap = cssVar.padding;
|
||||
return {
|
||||
holder: css`
|
||||
@@ -103,118 +106,123 @@ const useStyle = createStyles(({ cssVar, css }) => {
|
||||
`,
|
||||
};
|
||||
});
|
||||
interface ComponentsBlockProps {
|
||||
config: ConfigProviderProps;
|
||||
}
|
||||
|
||||
const ComponentsBlock: React.FC = () => {
|
||||
const ComponentsBlock: React.FC<ComponentsBlockProps> = (props) => {
|
||||
const [locale] = useLocale(locales);
|
||||
const { styles } = useStyle();
|
||||
const { config } = props;
|
||||
|
||||
return (
|
||||
<Tilt options={{ max: 4, glare: false, scale: 0.98 }} className={styles.holder}>
|
||||
<ModalPanel title="Ant Design" width="100%">
|
||||
{locale.text}
|
||||
</ModalPanel>
|
||||
<Alert title={locale.infoText} type="info" />
|
||||
{/* Line */}
|
||||
<div className={styles.flex}>
|
||||
<ColorPicker style={{ flex: 'none' }} />
|
||||
<div style={{ flex: 'none' }}>
|
||||
<Space.Compact>
|
||||
<Button>{locale.dropdown}</Button>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: Array.from({ length: 5 }).map((_, index) => ({
|
||||
key: `opt${index}`,
|
||||
label: `${locale.option} ${index}`,
|
||||
})),
|
||||
}}
|
||||
>
|
||||
<Button icon={<DownOutlined />} />
|
||||
</Dropdown>
|
||||
</Space.Compact>
|
||||
<ConfigProvider {...config}>
|
||||
<Tilt options={{ max: 4, glare: false, scale: 0.98 }} className={styles.holder}>
|
||||
<ModalPanel title="Ant Design" width="100%">
|
||||
{locale.text}
|
||||
</ModalPanel>
|
||||
<Alert title={locale.infoText} type="info" />
|
||||
{/* Line */}
|
||||
<div className={styles.flex}>
|
||||
<ColorPicker style={{ flex: 'none' }} />
|
||||
<div style={{ flex: 'none' }}>
|
||||
<Space.Compact>
|
||||
<Button>{locale.dropdown}</Button>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: Array.from({ length: 5 }).map((_, index) => ({
|
||||
key: `opt${index}`,
|
||||
label: `${locale.option} ${index}`,
|
||||
})),
|
||||
}}
|
||||
>
|
||||
<Button icon={<DownOutlined />} />
|
||||
</Dropdown>
|
||||
</Space.Compact>
|
||||
</div>
|
||||
<Select
|
||||
style={{ flex: 'auto' }}
|
||||
mode="multiple"
|
||||
maxTagCount="responsive"
|
||||
defaultValue={[{ value: 'apple' }, { value: 'banana' }]}
|
||||
options={[
|
||||
{ value: 'apple', label: locale.apple },
|
||||
{ value: 'banana', label: locale.banana },
|
||||
{ value: 'orange', label: locale.orange },
|
||||
{ value: 'watermelon', label: locale.watermelon },
|
||||
]}
|
||||
/>
|
||||
<Input style={{ flex: 'none', width: 120 }} />
|
||||
</div>
|
||||
<Select
|
||||
style={{ flex: 'auto' }}
|
||||
mode="multiple"
|
||||
maxTagCount="responsive"
|
||||
defaultValue={[{ value: 'apple' }, { value: 'banana' }]}
|
||||
options={[
|
||||
{ value: 'apple', label: locale.apple },
|
||||
{ value: 'banana', label: locale.banana },
|
||||
{ value: 'orange', label: locale.orange },
|
||||
{ value: 'watermelon', label: locale.watermelon },
|
||||
<Progress
|
||||
style={{ margin: 0 }}
|
||||
percent={100}
|
||||
strokeColor={{ '0%': '#108ee9', '100%': '#87d068' }}
|
||||
/>
|
||||
<Progress style={{ margin: 0 }} percent={33} status="exception" />
|
||||
<Steps
|
||||
current={1}
|
||||
items={[
|
||||
{ title: locale.finished },
|
||||
{ title: locale.inProgress },
|
||||
{ title: locale.waiting },
|
||||
]}
|
||||
/>
|
||||
<Input style={{ flex: 'none', width: 120 }} />
|
||||
</div>
|
||||
<Progress
|
||||
style={{ margin: 0 }}
|
||||
percent={100}
|
||||
strokeColor={{ '0%': '#108ee9', '100%': '#87d068' }}
|
||||
/>
|
||||
<Progress style={{ margin: 0 }} percent={33} status="exception" />
|
||||
<Steps
|
||||
current={1}
|
||||
items={[
|
||||
{ title: locale.finished },
|
||||
{ title: locale.inProgress },
|
||||
{ title: locale.waiting },
|
||||
]}
|
||||
/>
|
||||
{/* Line */}
|
||||
<div className={styles.block}>
|
||||
<Slider
|
||||
style={{ marginInline: 20 }}
|
||||
range
|
||||
marks={{
|
||||
0: '0°C',
|
||||
26: '26°C',
|
||||
37: '37°C',
|
||||
100: {
|
||||
style: { color: '#f50' },
|
||||
label: <strong>100°C</strong>,
|
||||
},
|
||||
}}
|
||||
defaultValue={[26, 37]}
|
||||
/>
|
||||
</div>
|
||||
{/* Line */}
|
||||
<div className={styles.flex}>
|
||||
<Button className={styles.ptg_20} type="primary">
|
||||
{locale.primary}
|
||||
</Button>
|
||||
<Button className={styles.ptg_20} type="primary" danger>
|
||||
{locale.danger}
|
||||
</Button>
|
||||
<Button className={styles.ptg_20}>{locale.default}</Button>
|
||||
<Button className={styles.ptg_20} type="dashed">
|
||||
{locale.dashed}
|
||||
</Button>
|
||||
<Button className={styles.ptg_20} icon={<AntDesignOutlined />}>
|
||||
{locale.icon}
|
||||
</Button>
|
||||
</div>
|
||||
{/* Line */}
|
||||
<div className={styles.block}>
|
||||
<div className={styles.flex}>
|
||||
<Switch
|
||||
className={styles.ptg_none}
|
||||
defaultChecked
|
||||
checkedChildren={<CheckOutlined />}
|
||||
unCheckedChildren={<CloseOutlined />}
|
||||
/>
|
||||
<Checkbox.Group
|
||||
className={styles.ptg_none}
|
||||
options={[locale.apple, locale.banana, locale.orange]}
|
||||
defaultValue={[locale.apple]}
|
||||
{/* Line */}
|
||||
<div className={styles.block}>
|
||||
<Slider
|
||||
style={{ marginInline: 20 }}
|
||||
range
|
||||
marks={{
|
||||
0: '0°C',
|
||||
26: '26°C',
|
||||
37: '37°C',
|
||||
100: {
|
||||
style: { color: '#f50' },
|
||||
label: <strong>100°C</strong>,
|
||||
},
|
||||
}}
|
||||
defaultValue={[26, 37]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<InternalMessage content={locale.release} type="success" />
|
||||
</div>
|
||||
<InternalTooltip title={locale.hello} placement="topLeft" className={styles.noMargin} />
|
||||
<Alert title="Ant Design love you!" type="success" />
|
||||
</Tilt>
|
||||
{/* Line */}
|
||||
<div className={styles.flex}>
|
||||
<Button className={styles.ptg_20} type="primary">
|
||||
{locale.primary}
|
||||
</Button>
|
||||
<Button className={styles.ptg_20} type="primary" danger>
|
||||
{locale.danger}
|
||||
</Button>
|
||||
<Button className={styles.ptg_20}>{locale.default}</Button>
|
||||
<Button className={styles.ptg_20} type="dashed">
|
||||
{locale.dashed}
|
||||
</Button>
|
||||
<Button className={styles.ptg_20} icon={<AntDesignOutlined />}>
|
||||
{locale.icon}
|
||||
</Button>
|
||||
</div>
|
||||
{/* Line */}
|
||||
<div className={styles.block}>
|
||||
<div className={styles.flex}>
|
||||
<Switch
|
||||
className={styles.ptg_none}
|
||||
defaultChecked
|
||||
checkedChildren={<CheckOutlined />}
|
||||
unCheckedChildren={<CloseOutlined />}
|
||||
/>
|
||||
<Checkbox.Group
|
||||
className={styles.ptg_none}
|
||||
options={[locale.apple, locale.banana, locale.orange]}
|
||||
defaultValue={[locale.apple]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<InternalMessage content={locale.release} type="success" />
|
||||
</div>
|
||||
<InternalTooltip title={locale.hello} placement="topLeft" className={styles.noMargin} />
|
||||
<Alert title="Ant Design love you!" type="success" />
|
||||
</Tilt>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import React, { Suspense, use } from 'react';
|
||||
import { Flex, Typography } from 'antd';
|
||||
import React, { Suspense, use, useState } from 'react';
|
||||
import { Button, Flex, Typography } from 'antd';
|
||||
import type { ConfigProviderProps, ThemeConfig } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
import { useLocation } from 'dumi';
|
||||
|
||||
import { DarkContext } from '../../../../hooks/useDark';
|
||||
import useLocale from '../../../../hooks/useLocale';
|
||||
import LinkButton from '../../../../theme/common/LinkButton';
|
||||
import PromptDrawer from '../../../../theme/common/ThemeSwitch/PromptDrawer';
|
||||
import ThemeIcon from '../../../../theme/common/ThemeSwitch/ThemeIcon';
|
||||
import SiteContext from '../../../../theme/slots/SiteContext';
|
||||
import type { SiteContextProps } from '../../../../theme/slots/SiteContext';
|
||||
import * as utils from '../../../../theme/utils';
|
||||
import GroupMaskLayer from '../GroupMaskLayer';
|
||||
import { muiComponentConfig, muiDark, muiLight } from './themes/mui';
|
||||
import { shadcnComponentConfig, shadcnDark, shadcnLight } from './themes/shadcn';
|
||||
|
||||
import '../SiteContext';
|
||||
|
||||
@@ -119,64 +125,251 @@ const useStyle = createStyles(({ cssVar, css, cx }, siteConfig: SiteContextProps
|
||||
bottom: 120px;
|
||||
inset-inline-end: ${siteConfig.isMobile ? 0 : '40%'};
|
||||
`,
|
||||
themeBar: css`
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin: 24px 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`,
|
||||
themeLabel: css`
|
||||
background: ${cssVar.colorBgElevated};
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
box-shadow: ${cssVar.boxShadowSecondary};
|
||||
color: ${cssVar.colorText};
|
||||
font-weight: 600;
|
||||
font-size: ${cssVar.fontSizeSM};
|
||||
`,
|
||||
presets: css`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
`,
|
||||
presetButton: cx(css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-radius: 14px;
|
||||
background: ${cssVar.colorBgElevated};
|
||||
color: ${cssVar.colorText};
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: ${cssVar.boxShadowSecondary};
|
||||
transition: all 0.2s ease;
|
||||
font-size: ${cssVar.fontSizeSM};
|
||||
padding: 7px 18px;
|
||||
line-height: 18px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
`),
|
||||
swatches: css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
`,
|
||||
swatch: css`
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
|
||||
`,
|
||||
name: css`
|
||||
margin-left: 10px;
|
||||
text-transform: capitalize;
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
type THEME_MAP = 'antd' | 'shadcn' | 'mui';
|
||||
|
||||
interface Theme {
|
||||
name: THEME_MAP;
|
||||
theme: ThemeConfig;
|
||||
componentsConfig: Partial<ConfigProviderProps>;
|
||||
style?: React.CSSProperties;
|
||||
activeStyle: React.CSSProperties;
|
||||
swatches: string[];
|
||||
}
|
||||
|
||||
const PreviewBanner: React.FC<Readonly<React.PropsWithChildren>> = (props) => {
|
||||
const { updateSiteConfig } = use<SiteContextProps>(SiteContext);
|
||||
const [isMarketDrawerOpen, setIsMarketDrawerOpen] = useState(false);
|
||||
const { children } = props;
|
||||
const [locale] = useLocale(locales);
|
||||
const siteConfig = use(SiteContext);
|
||||
const { styles } = useStyle(siteConfig);
|
||||
const { pathname, search } = useLocation();
|
||||
const isZhCN = utils.isZhCN(pathname);
|
||||
const [theme, setTheme] = useState<THEME_MAP>('antd');
|
||||
const isDark = React.use(DarkContext);
|
||||
|
||||
const themeMap: Record<THEME_MAP, Theme> = {
|
||||
antd: {
|
||||
name: 'antd',
|
||||
theme: {},
|
||||
componentsConfig: {},
|
||||
activeStyle: {
|
||||
border: '1px solid #1677ff',
|
||||
},
|
||||
swatches: ['#1677ff', '#91d5ff', '#f0f5ff'],
|
||||
},
|
||||
shadcn: {
|
||||
name: 'shadcn',
|
||||
theme: isDark ? shadcnDark : shadcnLight,
|
||||
style: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
activeStyle: {
|
||||
border: '1px solid oklch(0.205 0 0)',
|
||||
},
|
||||
componentsConfig: shadcnComponentConfig,
|
||||
swatches: ['oklch(0.205 0 0)', 'oklch(0.556 0 0)', 'rgba(0, 0, 0, 0.05)'],
|
||||
},
|
||||
mui: {
|
||||
name: 'mui',
|
||||
theme: isDark ? muiDark : muiLight,
|
||||
componentsConfig: muiComponentConfig,
|
||||
style: {
|
||||
color: '#fff',
|
||||
backgroundColor: 'rgb(2, 136, 209, 0.5)',
|
||||
},
|
||||
activeStyle: {
|
||||
border: '1px solid rgb(25, 118, 210)',
|
||||
},
|
||||
swatches: ['#1677ff', '#91d5ff', '#f0f5ff'],
|
||||
},
|
||||
};
|
||||
const config: ConfigProviderProps = {
|
||||
theme: themeMap[theme].theme,
|
||||
...themeMap[theme].componentsConfig,
|
||||
};
|
||||
|
||||
return (
|
||||
<GroupMaskLayer>
|
||||
{/* Image Left Top */}
|
||||
<img
|
||||
alt="bg"
|
||||
src="https://gw.alipayobjects.com/zos/bmw-prod/49f963db-b2a8-4f15-857a-270d771a1204.svg"
|
||||
draggable={false}
|
||||
className={clsx(styles.bgImg, styles.bgImgTop)}
|
||||
/>
|
||||
{/* Image Right Top */}
|
||||
<img
|
||||
alt="bg"
|
||||
src="https://gw.alipayobjects.com/zos/bmw-prod/e152223c-bcae-4913-8938-54fda9efe330.svg"
|
||||
draggable={false}
|
||||
className={clsx(styles.bgImg, styles.bgImgBottom)}
|
||||
/>
|
||||
<div
|
||||
className={styles.holder}
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'stretch',
|
||||
justifyContent: 'center',
|
||||
height: 640,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
perspective: 800,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="bg"
|
||||
src="https://gw.alipayobjects.com/zos/bmw-prod/49f963db-b2a8-4f15-857a-270d771a1204.svg"
|
||||
draggable={false}
|
||||
className={clsx(styles.bgImg, styles.bgImgTop)}
|
||||
style={{ position: 'absolute', left: 0, top: 0, zIndex: 0 }}
|
||||
/>
|
||||
<div className={styles.mask} />
|
||||
|
||||
<div className={styles.holder}>
|
||||
{/* Mobile not show the component preview */}
|
||||
<Suspense fallback={null}>
|
||||
{siteConfig.isMobile ? null : (
|
||||
<div className={styles.block}>
|
||||
<ComponentsBlock />
|
||||
<Typography className={styles.typography}>
|
||||
<h1>Ant Design</h1>
|
||||
<p>{locale.slogan}</p>
|
||||
</Typography>
|
||||
<div className={styles.themeBar}>
|
||||
<div className={styles.presets}>
|
||||
{Object.keys(themeMap).map((v, i) => {
|
||||
const key = v as THEME_MAP;
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setTheme(themeMap[key].name)}
|
||||
className={clsx(styles.presetButton)}
|
||||
style={{
|
||||
...themeMap[key].style,
|
||||
...(theme === themeMap[key].name ? themeMap[key].activeStyle : {}),
|
||||
}}
|
||||
>
|
||||
<div className={styles.swatches}>
|
||||
{themeMap[key].swatches?.map((s: any) => (
|
||||
<span key={s} className={styles.swatch} style={{ background: s }} />
|
||||
))}
|
||||
</div>
|
||||
<span className={styles.name}>{themeMap[key].name}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<Button
|
||||
variant="solid"
|
||||
icon={<ThemeIcon />}
|
||||
onClick={() => setIsMarketDrawerOpen(true)}
|
||||
style={{ fontSize: 16 }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Suspense>
|
||||
<div className={styles.mask} />
|
||||
<Typography className={styles.typography}>
|
||||
<h1>Ant Design</h1>
|
||||
<p>{locale.slogan}</p>
|
||||
</Typography>
|
||||
<Flex gap="middle" className={styles.btnWrap}>
|
||||
<LinkButton
|
||||
size="large"
|
||||
type="primary"
|
||||
to={utils.getLocalizedPathname('/components/overview/', isZhCN, search)}
|
||||
>
|
||||
{locale.start}
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
size="large"
|
||||
to={utils.getLocalizedPathname('/docs/spec/introduce/', isZhCN, search)}
|
||||
>
|
||||
{locale.designLanguage}
|
||||
</LinkButton>
|
||||
</Flex>
|
||||
<div className={styles.child}>{children}</div>
|
||||
</div>
|
||||
<Flex gap="middle" className={styles.btnWrap}>
|
||||
<LinkButton
|
||||
size="large"
|
||||
type="primary"
|
||||
to={utils.getLocalizedPathname('/components/overview/', isZhCN, search)}
|
||||
>
|
||||
{locale.start}
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
size="large"
|
||||
to={utils.getLocalizedPathname('/docs/spec/introduce/', isZhCN, search)}
|
||||
>
|
||||
{locale.designLanguage}
|
||||
</LinkButton>
|
||||
</Flex>
|
||||
<div className={styles.child}>{children}</div>
|
||||
</div>
|
||||
<PromptDrawer
|
||||
open={isMarketDrawerOpen}
|
||||
onClose={() => setIsMarketDrawerOpen(false)}
|
||||
onThemeChange={(nextTheme) => {
|
||||
updateSiteConfig({
|
||||
dynamicTheme: nextTheme,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
width: '40%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
backgroundColor: isDark ? '#393F4A' : '#f4f8ff',
|
||||
borderRadius: '0 0px 0px 12px',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="bg"
|
||||
src="https://gw.alipayobjects.com/zos/bmw-prod/e152223c-bcae-4913-8938-54fda9efe330.svg"
|
||||
draggable={false}
|
||||
className={clsx(styles.bgImg, styles.bgImgBottom)}
|
||||
style={{ position: 'absolute', right: 0, top: 0, zIndex: 0 }}
|
||||
/>
|
||||
<Suspense fallback={null}>
|
||||
{siteConfig.isMobile ? null : (
|
||||
<div className={styles.block} style={{ position: 'relative', zIndex: 1 }}>
|
||||
<ComponentsBlock config={config} />
|
||||
</div>
|
||||
)}
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</GroupMaskLayer>
|
||||
);
|
||||
|
||||
244
.dumi/pages/index/components/PreviewBanner/themes/mui.ts
Normal file
244
.dumi/pages/index/components/PreviewBanner/themes/mui.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import type { ConfigProviderProps, ThemeConfig } from 'antd';
|
||||
import { theme } from 'antd';
|
||||
|
||||
const { darkAlgorithm, defaultAlgorithm } = theme;
|
||||
|
||||
export const mui: ThemeConfig = {
|
||||
token: {
|
||||
colorPrimary: '#1976d2',
|
||||
colorSuccess: '#2e7d32',
|
||||
colorWarning: '#ed6c02',
|
||||
colorError: '#d32f2f',
|
||||
colorInfo: '#0288d1',
|
||||
colorTextBase: '#212121',
|
||||
colorBgBase: '#fafafa',
|
||||
colorPrimaryBg: '#e3f2fd',
|
||||
colorPrimaryBgHover: '#bbdefb',
|
||||
colorPrimaryBorder: '#90caf9',
|
||||
colorPrimaryBorderHover: '#64b5f6',
|
||||
colorPrimaryHover: '#42a5f5',
|
||||
colorPrimaryActive: '#1565c0',
|
||||
colorPrimaryText: '#1976d2',
|
||||
colorPrimaryTextHover: '#42a5f5',
|
||||
colorPrimaryTextActive: '#1565c0',
|
||||
colorSuccessBg: '#e8f5e9',
|
||||
colorSuccessBgHover: '#c8e6c9',
|
||||
colorSuccessBorder: '#a5d6a7',
|
||||
colorSuccessBorderHover: '#81c784',
|
||||
colorSuccessHover: '#4caf50',
|
||||
colorSuccessActive: '#1b5e20',
|
||||
colorSuccessText: '#2e7d32',
|
||||
colorSuccessTextHover: '#4caf50',
|
||||
colorSuccessTextActive: '#1b5e20',
|
||||
colorWarningBg: '#fff3e0',
|
||||
colorWarningBgHover: '#ffe0b2',
|
||||
colorWarningBorder: '#ffcc02',
|
||||
colorWarningBorderHover: '#ffb74d',
|
||||
colorWarningHover: '#ff9800',
|
||||
colorWarningActive: '#e65100',
|
||||
colorWarningText: '#ed6c02',
|
||||
colorWarningTextHover: '#ff9800',
|
||||
colorWarningTextActive: '#e65100',
|
||||
colorErrorBg: '#ffebee',
|
||||
colorErrorBgHover: '#ffcdd2',
|
||||
colorErrorBorder: '#ef9a9a',
|
||||
colorErrorBorderHover: '#e57373',
|
||||
colorErrorHover: '#ef5350',
|
||||
colorErrorActive: '#c62828',
|
||||
colorErrorText: '#d32f2f',
|
||||
colorErrorTextHover: '#ef5350',
|
||||
colorErrorTextActive: '#c62828',
|
||||
colorInfoBg: '#e1f5fe',
|
||||
colorInfoBgHover: '#b3e5fc',
|
||||
colorInfoBorder: '#81d4fa',
|
||||
colorInfoBorderHover: '#4fc3f7',
|
||||
colorInfoHover: '#03a9f4',
|
||||
colorInfoActive: '#01579b',
|
||||
colorInfoText: '#0288d1',
|
||||
colorInfoTextHover: '#03a9f4',
|
||||
colorInfoTextActive: '#01579b',
|
||||
colorText: 'rgba(33, 33, 33, 0.87)',
|
||||
colorTextSecondary: 'rgba(33, 33, 33, 0.6)',
|
||||
colorTextTertiary: 'rgba(33, 33, 33, 0.38)',
|
||||
colorTextQuaternary: 'rgba(33, 33, 33, 0.26)',
|
||||
colorTextDisabled: 'rgba(33, 33, 33, 0.38)',
|
||||
colorBgContainer: '#ffffff',
|
||||
colorBgElevated: '#ffffff',
|
||||
colorBgLayout: '#f5f5f5',
|
||||
colorBgSpotlight: 'rgba(33, 33, 33, 0.85)',
|
||||
colorBgMask: 'rgba(33, 33, 33, 0.5)',
|
||||
colorBorder: '#e0e0e0',
|
||||
colorBorderSecondary: '#eeeeee',
|
||||
borderRadius: 4,
|
||||
borderRadiusXS: 1,
|
||||
borderRadiusSM: 2,
|
||||
borderRadiusLG: 6,
|
||||
padding: 16,
|
||||
paddingSM: 8,
|
||||
paddingLG: 24,
|
||||
margin: 16,
|
||||
marginSM: 8,
|
||||
marginLG: 24,
|
||||
boxShadow:
|
||||
'0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)',
|
||||
boxShadowSecondary:
|
||||
'0px 3px 3px -2px rgba(0,0,0,0.2),0px 3px 4px 0px rgba(0,0,0,0.14),0px 1px 8px 0px rgba(0,0,0,0.12)',
|
||||
},
|
||||
components: {
|
||||
Button: {
|
||||
primaryShadow:
|
||||
'0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)',
|
||||
defaultShadow:
|
||||
'0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)',
|
||||
dangerShadow:
|
||||
'0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)',
|
||||
fontWeight: 500,
|
||||
defaultBorderColor: 'rgba(0, 0, 0, 0.23)',
|
||||
defaultColor: 'rgba(0, 0, 0, 0.87)',
|
||||
defaultBg: '#ffffff',
|
||||
defaultHoverBg: 'rgba(25, 118, 210, 0.04)',
|
||||
defaultHoverBorderColor: 'rgba(0, 0, 0, 0.23)',
|
||||
paddingInline: 16,
|
||||
paddingBlock: 6,
|
||||
contentFontSize: 14,
|
||||
borderRadius: 4,
|
||||
},
|
||||
Alert: {
|
||||
borderRadiusLG: 4,
|
||||
},
|
||||
Modal: {
|
||||
borderRadiusLG: 4,
|
||||
},
|
||||
Progress: {
|
||||
defaultColor: '#1976d2',
|
||||
remainingColor: 'rgba(25, 118, 210, 0.12)',
|
||||
},
|
||||
Steps: {
|
||||
iconSize: 24,
|
||||
},
|
||||
Checkbox: {
|
||||
borderRadiusSM: 2,
|
||||
},
|
||||
Slider: {
|
||||
trackBg: 'rgba(25, 118, 210, 0.26)',
|
||||
trackHoverBg: 'rgba(25, 118, 210, 0.38)',
|
||||
handleSize: 20,
|
||||
handleSizeHover: 20,
|
||||
railSize: 4,
|
||||
},
|
||||
ColorPicker: {
|
||||
borderRadius: 4,
|
||||
},
|
||||
},
|
||||
};
|
||||
export const muiLight: ThemeConfig = {
|
||||
algorithm: defaultAlgorithm,
|
||||
token: mui.token,
|
||||
components: mui.components,
|
||||
};
|
||||
|
||||
export const muiDark: ThemeConfig = {
|
||||
algorithm: darkAlgorithm,
|
||||
token: {
|
||||
...mui.token,
|
||||
},
|
||||
components: {
|
||||
...mui.components,
|
||||
Message: {
|
||||
contentBg: '#212121',
|
||||
contentPadding: '8px 16px',
|
||||
zIndexPopup: 1010,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const muiComponentConfig: Partial<ConfigProviderProps> = {
|
||||
button: {
|
||||
styles: (info) => {
|
||||
const { props } = info;
|
||||
if (props.type === 'primary') {
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: '#1976d2',
|
||||
color: '#ffffff',
|
||||
border: 'none',
|
||||
fontWeight: 500,
|
||||
textTransform: 'uppercase' as const,
|
||||
letterSpacing: '0.02857em',
|
||||
boxShadow:
|
||||
'0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)',
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
},
|
||||
};
|
||||
}
|
||||
if (props.type === 'default') {
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: '#ffffff',
|
||||
color: 'rgba(0, 0, 0, 0.87)',
|
||||
border: '1px solid rgba(0, 0, 0, 0.23)',
|
||||
fontWeight: 500,
|
||||
textTransform: 'uppercase' as const,
|
||||
letterSpacing: '0.02857em',
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
},
|
||||
};
|
||||
}
|
||||
if (props.danger) {
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: '#d32f2f',
|
||||
color: '#ffffff',
|
||||
border: 'none',
|
||||
fontWeight: 500,
|
||||
textTransform: 'uppercase' as const,
|
||||
letterSpacing: '0.02857em',
|
||||
boxShadow:
|
||||
'0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)',
|
||||
},
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
},
|
||||
input: {
|
||||
styles: (info) => {
|
||||
const { props } = info;
|
||||
const baseStyle = {
|
||||
root: {
|
||||
borderColor: 'rgba(0, 0, 0, 0.23)',
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
},
|
||||
input: {
|
||||
color: 'rgba(0, 0, 0, 0.87)',
|
||||
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
|
||||
},
|
||||
};
|
||||
if (props.status === 'error') {
|
||||
return {
|
||||
...baseStyle,
|
||||
root: {
|
||||
...baseStyle.root,
|
||||
borderColor: '#d32f2f',
|
||||
},
|
||||
};
|
||||
}
|
||||
return baseStyle;
|
||||
},
|
||||
},
|
||||
select: {
|
||||
styles: {
|
||||
root: {
|
||||
borderColor: 'rgba(0, 0, 0, 0.23)',
|
||||
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
|
||||
},
|
||||
popup: {
|
||||
root: {
|
||||
borderRadius: 4,
|
||||
boxShadow:
|
||||
'0px 5px 5px -3px rgba(0,0,0,0.2), 0px 8px 10px 1px rgba(0,0,0,0.14), 0px 3px 14px 2px rgba(0,0,0,0.12)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
243
.dumi/pages/index/components/PreviewBanner/themes/shadcn.ts
Normal file
243
.dumi/pages/index/components/PreviewBanner/themes/shadcn.ts
Normal file
@@ -0,0 +1,243 @@
|
||||
import type { ConfigProviderProps, ThemeConfig } from 'antd';
|
||||
import { theme } from 'antd';
|
||||
|
||||
const { darkAlgorithm, defaultAlgorithm } = theme;
|
||||
|
||||
export const shadcn: ThemeConfig = {
|
||||
algorithm: defaultAlgorithm,
|
||||
token: {
|
||||
colorPrimary: '#262626',
|
||||
colorSuccess: '#22c55e',
|
||||
colorWarning: '#f97316',
|
||||
colorError: '#ef4444',
|
||||
colorInfo: '#262626',
|
||||
colorTextBase: '#262626',
|
||||
colorBgBase: '#ffffff',
|
||||
colorPrimaryBg: '#f5f5f5',
|
||||
colorPrimaryBgHover: '#e5e5e5',
|
||||
colorPrimaryBorder: '#d4d4d4',
|
||||
colorPrimaryBorderHover: '#a3a3a3',
|
||||
colorPrimaryHover: '#404040',
|
||||
colorPrimaryActive: '#171717',
|
||||
colorPrimaryText: '#262626',
|
||||
colorPrimaryTextHover: '#404040',
|
||||
colorPrimaryTextActive: '#171717',
|
||||
colorSuccessBg: '#f0fdf4',
|
||||
colorSuccessBgHover: '#dcfce7',
|
||||
colorSuccessBorder: '#bbf7d0',
|
||||
colorSuccessBorderHover: '#86efac',
|
||||
colorSuccessHover: '#16a34a',
|
||||
colorSuccessActive: '#15803d',
|
||||
colorSuccessText: '#16a34a',
|
||||
colorSuccessTextHover: '#16a34a',
|
||||
colorSuccessTextActive: '#15803d',
|
||||
colorWarningBg: '#fff7ed',
|
||||
colorWarningBgHover: '#fed7aa',
|
||||
colorWarningBorder: '#fdba74',
|
||||
colorWarningBorderHover: '#fb923c',
|
||||
colorWarningHover: '#ea580c',
|
||||
colorWarningActive: '#c2410c',
|
||||
colorWarningText: '#ea580c',
|
||||
colorWarningTextHover: '#ea580c',
|
||||
colorWarningTextActive: '#c2410c',
|
||||
colorErrorBg: '#fef2f2',
|
||||
colorErrorBgHover: '#fecaca',
|
||||
colorErrorBorder: '#fca5a5',
|
||||
colorErrorBorderHover: '#f87171',
|
||||
colorErrorHover: '#dc2626',
|
||||
colorErrorActive: '#b91c1c',
|
||||
colorErrorText: '#dc2626',
|
||||
colorErrorTextHover: '#dc2626',
|
||||
colorErrorTextActive: '#b91c1c',
|
||||
colorInfoBg: '#f5f5f5',
|
||||
colorInfoBgHover: '#e5e5e5',
|
||||
colorInfoBorder: '#d4d4d4',
|
||||
colorInfoBorderHover: '#a3a3a3',
|
||||
colorInfoHover: '#404040',
|
||||
colorInfoActive: '#171717',
|
||||
colorInfoText: '#262626',
|
||||
colorInfoTextHover: '#404040',
|
||||
colorInfoTextActive: '#171717',
|
||||
colorText: '#262626',
|
||||
colorTextSecondary: '#525252',
|
||||
colorTextTertiary: '#737373',
|
||||
colorTextQuaternary: '#a3a3a3',
|
||||
colorTextDisabled: '#a3a3a3',
|
||||
colorBgContainer: '#ffffff',
|
||||
colorBgElevated: '#ffffff',
|
||||
colorBgLayout: '#fafafa',
|
||||
colorBgSpotlight: 'rgba(38, 38, 38, 0.85)',
|
||||
colorBgMask: 'rgba(38, 38, 38, 0.45)',
|
||||
colorBorder: '#e5e5e5',
|
||||
colorBorderSecondary: '#f5f5f5',
|
||||
borderRadius: 10,
|
||||
borderRadiusXS: 2,
|
||||
borderRadiusSM: 6,
|
||||
borderRadiusLG: 14,
|
||||
padding: 16,
|
||||
paddingSM: 12,
|
||||
paddingLG: 24,
|
||||
margin: 16,
|
||||
marginSM: 12,
|
||||
marginLG: 24,
|
||||
boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)',
|
||||
boxShadowSecondary: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
components: {
|
||||
Button: {
|
||||
primaryShadow: 'none',
|
||||
defaultShadow: 'none',
|
||||
dangerShadow: 'none',
|
||||
defaultBorderColor: '#e4e4e7',
|
||||
defaultColor: '#18181b',
|
||||
defaultBg: '#ffffff',
|
||||
defaultHoverBg: '#f4f4f5',
|
||||
defaultHoverBorderColor: '#d4d4d8',
|
||||
defaultHoverColor: '#18181b',
|
||||
defaultActiveBg: '#e4e4e7',
|
||||
defaultActiveBorderColor: '#d4d4d8',
|
||||
borderRadius: 6,
|
||||
},
|
||||
Input: {
|
||||
activeShadow: 'none',
|
||||
hoverBorderColor: '#a1a1aa',
|
||||
activeBorderColor: '#18181b',
|
||||
borderRadius: 6,
|
||||
},
|
||||
Select: {
|
||||
optionSelectedBg: '#f4f4f5',
|
||||
optionActiveBg: '#fafafa',
|
||||
optionSelectedFontWeight: 500,
|
||||
borderRadius: 6,
|
||||
},
|
||||
Alert: {
|
||||
borderRadiusLG: 8,
|
||||
},
|
||||
Modal: {
|
||||
borderRadiusLG: 12,
|
||||
},
|
||||
Progress: {
|
||||
defaultColor: '#18181b',
|
||||
remainingColor: '#f4f4f5',
|
||||
},
|
||||
Steps: {
|
||||
iconSize: 32,
|
||||
},
|
||||
Switch: {
|
||||
trackHeight: 24,
|
||||
trackMinWidth: 44,
|
||||
innerMinMargin: 4,
|
||||
innerMaxMargin: 24,
|
||||
},
|
||||
Checkbox: {
|
||||
borderRadiusSM: 4,
|
||||
},
|
||||
Slider: {
|
||||
trackBg: '#f4f4f5',
|
||||
trackHoverBg: '#e4e4e7',
|
||||
handleSize: 18,
|
||||
handleSizeHover: 20,
|
||||
railSize: 6,
|
||||
},
|
||||
ColorPicker: {
|
||||
borderRadius: 6,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const shadcnLight: ThemeConfig = {
|
||||
algorithm: defaultAlgorithm,
|
||||
token: shadcn.token,
|
||||
components: shadcn.components,
|
||||
};
|
||||
|
||||
export const shadcnDark: ThemeConfig = {
|
||||
algorithm: darkAlgorithm,
|
||||
token: {
|
||||
...shadcn.token,
|
||||
},
|
||||
components: {
|
||||
...shadcn.components,
|
||||
Message: {
|
||||
contentBg: '#212121',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const shadcnComponentConfig: Partial<ConfigProviderProps> = {
|
||||
button: {
|
||||
styles: (info) => {
|
||||
const { props } = info;
|
||||
if (props.type === 'primary') {
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: '#18181b',
|
||||
color: '#ffffff',
|
||||
border: '1px solid #18181b',
|
||||
fontWeight: 500,
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
},
|
||||
};
|
||||
}
|
||||
if (props.type === 'default') {
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: '#ffffff',
|
||||
color: '#18181b',
|
||||
border: '1px solid #e4e4e7',
|
||||
fontWeight: 500,
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
},
|
||||
};
|
||||
}
|
||||
if (props.danger) {
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: '#dc2626',
|
||||
color: '#ffffff',
|
||||
border: '1px solid #dc2626',
|
||||
fontWeight: 500,
|
||||
},
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
},
|
||||
input: {
|
||||
styles: (info) => {
|
||||
const { props } = info;
|
||||
const baseStyle = {
|
||||
root: {
|
||||
borderColor: '#e4e4e7',
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
},
|
||||
input: {
|
||||
color: '#18181b',
|
||||
},
|
||||
};
|
||||
if (props.status === 'error') {
|
||||
return {
|
||||
...baseStyle,
|
||||
root: {
|
||||
...baseStyle.root,
|
||||
borderColor: '#dc2626',
|
||||
},
|
||||
};
|
||||
}
|
||||
return baseStyle;
|
||||
},
|
||||
},
|
||||
select: {
|
||||
styles: {
|
||||
root: {
|
||||
borderColor: '#e4e4e7',
|
||||
},
|
||||
popup: {
|
||||
root: {
|
||||
borderRadius: 8,
|
||||
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { CSSMotionList } from '@rc-component/motion';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
import { COLOR_IMAGES, getClosetColor } from './colorUtil';
|
||||
@@ -10,7 +10,7 @@ export interface BackgroundImageProps {
|
||||
isLight?: boolean;
|
||||
}
|
||||
|
||||
const useStyle = createStyles(({ cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
image: css`
|
||||
transition: all ${cssVar.motionDurationSlow};
|
||||
position: absolute;
|
||||
@@ -29,7 +29,6 @@ const onHide = () => ({ opacity: 0 });
|
||||
|
||||
const BackgroundImage: React.FC<BackgroundImageProps> = ({ colorPrimary, isLight }) => {
|
||||
const activeColor = useMemo(() => getClosetColor(colorPrimary), [colorPrimary]);
|
||||
const { styles } = useStyle();
|
||||
|
||||
const [keyList, setKeyList] = useState<string[]>([]);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ColorPicker, Flex, Input } from 'antd';
|
||||
import type { ColorPickerProps, GetProp } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { generateColor } from 'antd/es/color-picker/util';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { PRESET_COLORS } from './colorUtil';
|
||||
|
||||
type Color = Extract<GetProp<ColorPickerProps, 'value'>, string | { cleared: any }>;
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
const styles = createStaticStyles(({ cssVar, css }) => ({
|
||||
color: css`
|
||||
width: calc(${cssVar.controlHeightLG} / 2);
|
||||
height: calc(${cssVar.controlHeightLG} / 2);
|
||||
@@ -69,8 +69,6 @@ const DebouncedColorPicker: React.FC<React.PropsWithChildren<ThemeColorPickerPro
|
||||
};
|
||||
|
||||
const ThemeColorPicker: React.FC<ThemeColorPickerProps> = ({ value, onChange, id }) => {
|
||||
const { styles } = useStyle();
|
||||
|
||||
const matchColors = React.useMemo(() => {
|
||||
const valueStr = generateColor(value || '').toRgbString();
|
||||
const colors = PRESET_COLORS.map((color) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { Flex } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
import useLocale from '../../../../hooks/useLocale';
|
||||
@@ -32,7 +32,7 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
const styles = createStaticStyles(({ cssVar, css }) => ({
|
||||
themeCard: css`
|
||||
border-radius: ${cssVar.borderRadius};
|
||||
cursor: pointer;
|
||||
@@ -80,7 +80,6 @@ export interface ThemePickerProps {
|
||||
|
||||
const ThemePicker: React.FC<ThemePickerProps> = (props) => {
|
||||
const { value, id, onChange } = props;
|
||||
const { styles } = useStyle();
|
||||
const [locale] = useLocale(locales);
|
||||
return (
|
||||
<Flex gap="large" wrap>
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
theme,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { generateColor } from 'antd/es/color-picker/util';
|
||||
import { clsx } from 'clsx';
|
||||
import { useLocation } from 'dumi';
|
||||
@@ -92,7 +92,7 @@ const locales = {
|
||||
};
|
||||
|
||||
// ============================= Style =============================
|
||||
const useStyle = createStyles(({ cssVar, css, cx }) => {
|
||||
const styles = createStaticStyles(({ cssVar, css, cx }) => {
|
||||
const { carousel } = getCarouselStyle();
|
||||
const demo = css`
|
||||
overflow: hidden;
|
||||
@@ -345,7 +345,6 @@ function rgbToColorMatrix(color: string) {
|
||||
}
|
||||
|
||||
const Theme: React.FC = () => {
|
||||
const { styles } = useStyle();
|
||||
const [locale, lang] = useLocale(locales);
|
||||
const isZhCN = lang === 'cn';
|
||||
const { search } = useLocation();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { Suspense } from 'react';
|
||||
import { ConfigProvider, theme } from 'antd';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
import useLocale from '../../hooks/useLocale';
|
||||
import { DarkContext } from './../../hooks/useDark';
|
||||
@@ -12,7 +12,7 @@ const ComponentsList = React.lazy(() => import('./components/ComponentsList'));
|
||||
const DesignFramework = React.lazy(() => import('./components/DesignFramework'));
|
||||
const Theme = React.lazy(() => import('./components/Theme'));
|
||||
|
||||
const useStyle = createStyles(() => ({
|
||||
const classNames = createStaticStyles(({ css }) => ({
|
||||
image: css`
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
@@ -38,7 +38,6 @@ const locales = {
|
||||
|
||||
const Homepage: React.FC = () => {
|
||||
const [locale] = useLocale(locales);
|
||||
const { styles } = useStyle();
|
||||
const { token } = theme.useToken();
|
||||
|
||||
const isDark = React.use(DarkContext);
|
||||
@@ -50,13 +49,6 @@ const Homepage: React.FC = () => {
|
||||
</PreviewBanner>
|
||||
|
||||
<div>
|
||||
{/* 定制主题 */}
|
||||
<ConfigProvider theme={{ algorithm: theme.defaultAlgorithm }}>
|
||||
<Suspense fallback={null}>
|
||||
<Theme />
|
||||
</Suspense>
|
||||
</ConfigProvider>
|
||||
|
||||
{/* 组件列表 */}
|
||||
<Group
|
||||
background={token.colorBgElevated}
|
||||
@@ -70,15 +62,22 @@ const Homepage: React.FC = () => {
|
||||
</Suspense>
|
||||
</Group>
|
||||
|
||||
{/* 定制主题 */}
|
||||
<ConfigProvider theme={{ algorithm: theme.defaultAlgorithm }}>
|
||||
<Suspense fallback={null}>
|
||||
<Theme />
|
||||
</Suspense>
|
||||
</ConfigProvider>
|
||||
|
||||
{/* 设计语言 */}
|
||||
<Group
|
||||
title={locale.designTitle}
|
||||
description={locale.designDesc}
|
||||
background={isDark ? '#393F4A' : '#F5F8FF'}
|
||||
background={isDark ? '#393F4A' : token.colorBgContainer}
|
||||
decoration={
|
||||
<img
|
||||
draggable={false}
|
||||
className={styles.image}
|
||||
className={classNames.image}
|
||||
src="https://gw.alipayobjects.com/zos/bmw-prod/ba37a413-28e6-4be4-b1c5-01be1a0ebb1c.svg"
|
||||
alt="bg"
|
||||
/>
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
import React, { Suspense } from 'react';
|
||||
import { App, Button, ConfigProvider, Skeleton } from 'antd';
|
||||
import { App, Button, ConfigProvider, Skeleton, version } from 'antd';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { enUS, zhCN } from 'antd-token-previewer';
|
||||
import type { ThemeConfig } from 'antd/es/config-provider/context';
|
||||
import { Helmet } from 'dumi';
|
||||
|
||||
import useLocale from '../../hooks/useLocale';
|
||||
import useLocalStorage from '../../hooks/useLocalStorage';
|
||||
|
||||
const ThemeEditor = React.lazy(() => import('antd-token-previewer/lib/ThemeEditor'));
|
||||
|
||||
const classNames = createStaticStyles(({ css }) => ({
|
||||
editor: css`
|
||||
svg,
|
||||
img {
|
||||
display: inline;
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
title: '主题编辑器',
|
||||
@@ -34,18 +43,24 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const ANT_THEME_EDITOR_THEME = 'ant-theme-editor-theme';
|
||||
const [antdMajor] = version.split('.');
|
||||
const ANT_DESIGN_V5_THEME_EDITOR_THEME = `ant-design-v${antdMajor}-theme-editor-theme`;
|
||||
|
||||
const CustomTheme: React.FC = () => {
|
||||
const { message } = App.useApp();
|
||||
const [locale, lang] = useLocale(locales);
|
||||
|
||||
const [themeConfig, setThemeConfig] = useLocalStorage<ThemeConfig>(ANT_THEME_EDITOR_THEME, {
|
||||
defaultValue: {},
|
||||
const [theme, setTheme] = React.useState<ThemeConfig>(() => {
|
||||
try {
|
||||
const storedConfig = localStorage.getItem(ANT_DESIGN_V5_THEME_EDITOR_THEME);
|
||||
return storedConfig ? JSON.parse(storedConfig) : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
|
||||
const handleSave = () => {
|
||||
setThemeConfig(themeConfig);
|
||||
localStorage.setItem(ANT_DESIGN_V5_THEME_EDITOR_THEME, JSON.stringify(theme));
|
||||
message.success(locale.saveSuccessfully);
|
||||
};
|
||||
|
||||
@@ -60,9 +75,12 @@ const CustomTheme: React.FC = () => {
|
||||
<ThemeEditor
|
||||
advanced
|
||||
hideAdvancedSwitcher
|
||||
theme={{ name: 'Custom Theme', key: 'test', config: themeConfig }}
|
||||
theme={{ name: 'Custom Theme', key: 'test', config: theme }}
|
||||
style={{ height: 'calc(100vh - 64px)' }}
|
||||
onThemeChange={(newTheme) => setThemeConfig(newTheme.config)}
|
||||
className={classNames.editor}
|
||||
onThemeChange={(newTheme) => {
|
||||
setTheme(newTheme.config);
|
||||
}}
|
||||
locale={lang === 'cn' ? zhCN : enUS}
|
||||
actions={
|
||||
<Button type="primary" onClick={handleSave}>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { SoundOutlined } from '@ant-design/icons';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => {
|
||||
const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
return {
|
||||
playBtn: css`
|
||||
display: inline-flex;
|
||||
@@ -30,7 +30,6 @@ interface AudioProps {
|
||||
}
|
||||
|
||||
const AudioControl: React.FC<React.PropsWithChildren<AudioProps>> = ({ id, children }) => {
|
||||
const { styles } = useStyle();
|
||||
const onClick: React.MouseEventHandler<HTMLAnchorElement> = () => {
|
||||
const audio = document.querySelector<HTMLAudioElement>(`#${id}`);
|
||||
audio?.play();
|
||||
|
||||
@@ -4,11 +4,11 @@ import type { ColorInput } from '@ant-design/fast-color';
|
||||
import { Popover } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const useStyle = createStyles(({ css, cssVar, token }) => ({
|
||||
codeSpan: css`
|
||||
padding: 0.2em 0.4em;
|
||||
font-size: 0.9em;
|
||||
background: ${cssVar.siteMarkdownCodeBg};
|
||||
background: ${token.siteMarkdownCodeBg};
|
||||
border-radius: ${cssVar.borderRadius};
|
||||
font-family: monospace;
|
||||
`,
|
||||
@@ -46,7 +46,7 @@ const ColorChunk: React.FC<React.PropsWithChildren<ColorChunkProps>> = (props) =
|
||||
placement="left"
|
||||
content={<div hidden />}
|
||||
styles={{
|
||||
body: {
|
||||
container: {
|
||||
backgroundColor: dotColor,
|
||||
width: 120,
|
||||
height: 120,
|
||||
|
||||
@@ -61,7 +61,7 @@ const transformComponentName = (componentName: string) => {
|
||||
return componentName;
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ cssVar }) => ({
|
||||
const useStyle = createStyles(({ cssVar, token }) => ({
|
||||
code: css`
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@@ -71,7 +71,7 @@ const useStyle = createStyles(({ cssVar }) => ({
|
||||
border-radius: ${cssVar.borderRadiusSM};
|
||||
padding-inline: ${cssVar.paddingXXS} !important;
|
||||
transition: all ${cssVar.motionDurationSlow} !important;
|
||||
font-family: ${cssVar.codeFamily};
|
||||
font-family: ${token.codeFamily};
|
||||
color: ${cssVar.colorTextSecondary} !important;
|
||||
&:hover {
|
||||
background: ${cssVar.controlItemBgHover};
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { memo, useMemo, useRef, useState } from 'react';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import { Affix, Card, Col, Divider, Flex, Input, Row, Tag, Typography } from 'antd';
|
||||
import { createStyles, useTheme } from 'antd-style';
|
||||
import { createStaticStyles, useTheme } from 'antd-style';
|
||||
import { useIntl, useLocation, useSidebarData } from 'dumi';
|
||||
import debounce from 'lodash/debounce';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
@@ -12,7 +12,7 @@ import SiteContext from '../../slots/SiteContext';
|
||||
import type { Component } from './ProComponentsList';
|
||||
import proComponentsList from './ProComponentsList';
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
const styles = createStaticStyles(({ cssVar, css }) => ({
|
||||
componentsOverviewGroupTitle: css`
|
||||
margin-bottom: ${cssVar.marginLG} !important;
|
||||
`,
|
||||
@@ -78,8 +78,7 @@ const reportSearch = debounce<(value: string) => void>((value) => {
|
||||
const { Title } = Typography;
|
||||
|
||||
const Overview: React.FC = () => {
|
||||
const { styles } = useStyle();
|
||||
const { theme } = React.use(SiteContext);
|
||||
const { isDark } = React.use(SiteContext);
|
||||
|
||||
const data = useSidebarData();
|
||||
const [searchBarAffixed, setSearchBarAffixed] = useState<boolean>(false);
|
||||
@@ -225,11 +224,9 @@ const Overview: React.FC = () => {
|
||||
<img
|
||||
draggable={false}
|
||||
src={
|
||||
theme.includes('dark') && component.coverDark
|
||||
? component.coverDark
|
||||
: component.cover
|
||||
isDark && component.coverDark ? component.coverDark : component.cover
|
||||
}
|
||||
alt={component.title}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { LinkOutlined, QuestionCircleOutlined, RightOutlined } from '@ant-design/icons';
|
||||
import { ConfigProvider, Flex, Popover, Table, Typography } from 'antd';
|
||||
import { createStyles, css, useTheme } from 'antd-style';
|
||||
import { createStaticStyles, css, useTheme } from 'antd-style';
|
||||
import { getDesignToken } from 'antd-token-previewer';
|
||||
import tokenMeta from 'antd/es/version/token-meta.json';
|
||||
import tokenData from 'antd/es/version/token.json';
|
||||
@@ -53,7 +53,7 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ cssVar }) => ({
|
||||
const styles = createStaticStyles(({ cssVar }) => ({
|
||||
tableTitle: css`
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@@ -104,8 +104,6 @@ const SubTokenTable: React.FC<SubTokenTableProps> = (props) => {
|
||||
|
||||
const [open, setOpen] = useState<boolean>(defaultOpen ?? process.env.NODE_ENV !== 'production');
|
||||
|
||||
const { styles } = useStyle();
|
||||
|
||||
if (!tokens.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import { App } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { useIntl } from 'dumi';
|
||||
|
||||
import CopyableIcon from './CopyableIcon';
|
||||
import type { CategoriesKeys } from './fields';
|
||||
import type { ThemeType } from './IconSearch';
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
anticonsList: css`
|
||||
margin: ${cssVar.margin} 0;
|
||||
overflow: hidden;
|
||||
@@ -36,7 +36,6 @@ interface CategoryProps {
|
||||
const Category: React.FC<CategoryProps> = (props) => {
|
||||
const { message } = App.useApp();
|
||||
const { icons, title, newIcons, theme } = props;
|
||||
const { styles } = useStyle();
|
||||
const intl = useIntl();
|
||||
const [justCopied, setJustCopied] = React.useState<string | null>(null);
|
||||
const copyId = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { CSSProperties } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import Icon, * as AntdIcons from '@ant-design/icons';
|
||||
import { Affix, Empty, Input, Segmented } from 'antd';
|
||||
import { createStyles, useTheme } from 'antd-style';
|
||||
import { createStaticStyles, useTheme } from 'antd-style';
|
||||
import type { SegmentedOptions } from 'antd/es/segmented';
|
||||
import { useIntl } from 'dumi';
|
||||
import debounce from 'lodash/debounce';
|
||||
@@ -22,7 +22,7 @@ export enum ThemeType {
|
||||
|
||||
const allIcons: { [key: string]: any } = AntdIcons;
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
iconSearchAffix: css`
|
||||
display: flex;
|
||||
transition: all ${cssVar.motionDurationSlow};
|
||||
@@ -39,7 +39,6 @@ const NEW_ICON_NAMES: ReadonlyArray<string> = [];
|
||||
|
||||
const IconSearch: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const { styles } = useStyle();
|
||||
const [displayState, setDisplayState] = useState<IconSearchState>({
|
||||
searchKey: '',
|
||||
theme: ThemeType.Outlined,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { Suspense } from 'react';
|
||||
import { Skeleton } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
const IconSearch = React.lazy(() => import('./IconSearch'));
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
searchWrapper: css`
|
||||
display: flex;
|
||||
gap: ${cssVar.padding};
|
||||
@@ -34,8 +34,6 @@ const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
}));
|
||||
|
||||
const IconSearchFallback: React.FC = () => {
|
||||
const { styles } = useStyle();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.searchWrapper}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
interface IconProps {
|
||||
@@ -7,7 +7,7 @@ interface IconProps {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const useStyle = createStyles(() => ({
|
||||
const classNames = createStaticStyles(({ css }) => ({
|
||||
iconWrap: css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -19,9 +19,8 @@ const useStyle = createStyles(() => ({
|
||||
|
||||
const BunIcon: React.FC<IconProps> = (props) => {
|
||||
const { className, style } = props;
|
||||
const { styles } = useStyle();
|
||||
return (
|
||||
<span className={clsx(styles.iconWrap, className)} style={style}>
|
||||
<span className={clsx(classNames.iconWrap, className)} style={style}>
|
||||
<svg id="Bun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 70" width="1em" height="1em">
|
||||
<title>Bun Logo</title>
|
||||
<path
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
interface IconProps {
|
||||
@@ -7,7 +7,7 @@ interface IconProps {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const useStyle = createStyles(() => ({
|
||||
const classNames = createStaticStyles(({ css }) => ({
|
||||
iconWrap: css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -19,9 +19,8 @@ const useStyle = createStyles(() => ({
|
||||
|
||||
const NpmIcon: React.FC<IconProps> = (props) => {
|
||||
const { className, style } = props;
|
||||
const { styles } = useStyle();
|
||||
return (
|
||||
<span className={clsx(styles.iconWrap, className)} style={style}>
|
||||
<span className={clsx(classNames.iconWrap, className)} style={style}>
|
||||
<svg
|
||||
fill="#E53E3E"
|
||||
focusable="false"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
interface IconProps {
|
||||
@@ -7,7 +7,7 @@ interface IconProps {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const useStyle = createStyles(() => ({
|
||||
const classNames = createStaticStyles(({ css }) => ({
|
||||
iconWrap: css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -19,9 +19,8 @@ const useStyle = createStyles(() => ({
|
||||
|
||||
const PnpmIcon: React.FC<IconProps> = (props) => {
|
||||
const { className, style } = props;
|
||||
const { styles } = useStyle();
|
||||
return (
|
||||
<span className={clsx(styles.iconWrap, className)} style={style}>
|
||||
<span className={clsx(classNames.iconWrap, className)} style={style}>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="#F69220"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
interface IconProps {
|
||||
@@ -7,7 +7,7 @@ interface IconProps {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const useStyle = createStyles(() => ({
|
||||
const classNames = createStaticStyles(({ css }) => ({
|
||||
iconWrap: css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -19,9 +19,8 @@ const useStyle = createStyles(() => ({
|
||||
|
||||
const YarnIcon: React.FC<IconProps> = (props) => {
|
||||
const { className, style } = props;
|
||||
const { styles } = useStyle();
|
||||
return (
|
||||
<span className={clsx(styles.iconWrap, className)} style={style}>
|
||||
<span className={clsx(classNames.iconWrap, className)} style={style}>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="#2C8EBB"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { Suspense, useRef } from 'react';
|
||||
import { LinkOutlined, ThunderboltOutlined } from '@ant-design/icons';
|
||||
import { BugOutlined, ThunderboltOutlined } from '@ant-design/icons';
|
||||
import stackblitzSdk from '@stackblitz/sdk';
|
||||
import { Flex, Tooltip } from 'antd';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { Button, Dropdown, Flex, Tooltip } from 'antd';
|
||||
import { FormattedMessage, useSiteData } from 'dumi';
|
||||
import LZString from 'lz-string';
|
||||
|
||||
@@ -28,8 +29,6 @@ function compress(string: string): string {
|
||||
}
|
||||
|
||||
interface ActionsProps {
|
||||
showOnlineUrl: boolean;
|
||||
docsOnlineUrl?: string;
|
||||
assetId: string;
|
||||
title?: string;
|
||||
pkgDependencyList: Record<PropertyKey, string>;
|
||||
@@ -39,11 +38,10 @@ interface ActionsProps {
|
||||
onCodeExpand: () => void;
|
||||
entryCode: string;
|
||||
styleCode: string;
|
||||
debugOptions?: MenuProps['items'];
|
||||
}
|
||||
|
||||
const Actions: React.FC<ActionsProps> = ({
|
||||
showOnlineUrl,
|
||||
docsOnlineUrl,
|
||||
assetId,
|
||||
title,
|
||||
jsx,
|
||||
@@ -53,6 +51,7 @@ const Actions: React.FC<ActionsProps> = ({
|
||||
pkgDependencyList,
|
||||
entryCode,
|
||||
styleCode,
|
||||
debugOptions,
|
||||
}) => {
|
||||
const [, lang] = useLocale();
|
||||
const isZhCN = lang === 'cn';
|
||||
@@ -128,8 +127,8 @@ const Actions: React.FC<ActionsProps> = ({
|
||||
editors: '001',
|
||||
css: '',
|
||||
js_external: [
|
||||
'react@19/cjs/react.development.js',
|
||||
'react-dom@19/cjs/react-dom.development.js',
|
||||
'react@18/umd/react.production.min.js',
|
||||
'react-dom@18/umd/react-dom.production.min.js',
|
||||
'dayjs@1/dayjs.min.js',
|
||||
`antd@${pkg.version}/dist/antd-with-locales.min.js`,
|
||||
`@ant-design/icons/dist/index.umd.js`,
|
||||
@@ -213,21 +212,15 @@ createRoot(document.getElementById('container')).render(<Demo />);
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex wrap gap="middle" className="code-box-actions">
|
||||
{/* 在线文档按钮 */}
|
||||
{showOnlineUrl && (
|
||||
<Tooltip title={<FormattedMessage id="app.demo.online" />}>
|
||||
<a
|
||||
className="code-box-code-action"
|
||||
aria-label="open in new tab"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={docsOnlineUrl || ''}
|
||||
>
|
||||
<LinkOutlined className="code-box-online" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Flex wrap gap="middle" className="code-box-actions" align="center">
|
||||
{
|
||||
// 调试选项
|
||||
debugOptions?.length ? (
|
||||
<Dropdown menu={{ items: debugOptions }} arrow={{ pointAtCenter: true }}>
|
||||
<Button icon={<BugOutlined />} color="purple" variant="filled" size="small" />
|
||||
</Dropdown>
|
||||
) : null
|
||||
}
|
||||
{/* CodeSandbox 按钮 */}
|
||||
<form
|
||||
className="code-box-code-action"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { UpOutlined } from '@ant-design/icons';
|
||||
import { Badge, Tooltip } from 'antd';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { Badge, Tag, Tooltip } from 'antd';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
import { FormattedMessage, useLiveDemo } from 'dumi';
|
||||
|
||||
import { FormattedMessage, useLiveDemo, useSiteData } from 'dumi';
|
||||
import { major, minVersion } from 'semver';
|
||||
import type { AntdPreviewerProps } from '.';
|
||||
import useLocation from '../../../hooks/useLocation';
|
||||
import BrowserFrame from '../../common/BrowserFrame';
|
||||
@@ -12,9 +13,11 @@ import ClientOnly from '../../common/ClientOnly';
|
||||
import CodePreview from '../../common/CodePreview';
|
||||
import EditButton from '../../common/EditButton';
|
||||
import SiteContext from '../../slots/SiteContext';
|
||||
import DemoContext from '../../slots/DemoContext';
|
||||
import { isOfficialHost } from '../../utils';
|
||||
import Actions from './Actions';
|
||||
|
||||
const useStyle = createStyles(({ cssVar }) => {
|
||||
const styles = createStaticStyles(({ cssVar, css }) => {
|
||||
return {
|
||||
codeHideBtn: css`
|
||||
position: sticky;
|
||||
@@ -61,13 +64,14 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
|
||||
clientOnly,
|
||||
pkgDependencyList,
|
||||
} = props;
|
||||
const { showDebug } = React.use(DemoContext);
|
||||
const { pkg } = useSiteData();
|
||||
|
||||
const location = useLocation();
|
||||
const { styles } = useStyle();
|
||||
|
||||
const entryName = 'index.tsx';
|
||||
const entryCode = asset.dependencies[entryName].value;
|
||||
|
||||
const previewDemo = useRef<React.ReactNode>(null);
|
||||
const demoContainer = useRef<HTMLElement>(null);
|
||||
const {
|
||||
node: liveDemoNode,
|
||||
@@ -79,18 +83,16 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
|
||||
});
|
||||
const anchorRef = useRef<HTMLAnchorElement>(null);
|
||||
const [codeExpand, setCodeExpand] = useState<boolean>(false);
|
||||
const { theme } = React.use(SiteContext);
|
||||
|
||||
const { isDark } = React.use(SiteContext);
|
||||
const { hash, pathname, search } = location;
|
||||
const docsOnlineUrl = `https://ant.design${pathname ?? ''}${search ?? ''}#${asset.id}`;
|
||||
|
||||
const [showOnlineUrl, setShowOnlineUrl] = useState<boolean>(false);
|
||||
|
||||
/**
|
||||
* Record whether it is deployed on the official domain name.
|
||||
* Note that window.location.hostname is not available on the server side due to hydration issues
|
||||
*/
|
||||
const [deployedOnOfficialHost, setDeployedOnOfficialHost] = useState<boolean>(true);
|
||||
useEffect(() => {
|
||||
const regexp = /preview-(\d+)-ant-design/; // matching PR preview addresses
|
||||
setShowOnlineUrl(
|
||||
process.env.NODE_ENV === 'development' || regexp.test(window.location.hostname),
|
||||
);
|
||||
setDeployedOnOfficialHost(isOfficialHost(window.location.hostname));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -103,11 +105,43 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
|
||||
setCodeExpand(expand);
|
||||
}, [expand]);
|
||||
|
||||
const mergedChildren = !iframe && clientOnly ? <ClientOnly>{children}</ClientOnly> : children;
|
||||
const demoUrlWithTheme = `${demoUrl}${theme.includes('dark') ? '?theme=dark' : ''}`;
|
||||
const generateDocUrl = (domain = 'https://ant.design') =>
|
||||
`${domain}${pathname ?? ''}${search ?? ''}#${asset.id}`;
|
||||
|
||||
if (!previewDemo.current) {
|
||||
previewDemo.current = iframe ? (
|
||||
// Enable "Go Online Docs" only when deployed on non-official domains
|
||||
const enableDocsOnlineUrl = process.env.NODE_ENV === 'development' || !deployedOnOfficialHost;
|
||||
|
||||
// Previous version demos are only available during the maintenance window
|
||||
const [supportsPreviousVersionDemo, previousVersionDomain, previousVersion] = useMemo(() => {
|
||||
const maintenanceDeadline = new Date('2026/12/31');
|
||||
|
||||
if (new Date() > maintenanceDeadline) {
|
||||
return [false, undefined, -1] as const;
|
||||
}
|
||||
|
||||
const currentMajor = major(pkg.version);
|
||||
const previousMajor = Math.min(currentMajor - 1, 5);
|
||||
|
||||
let enabled = true;
|
||||
// If the demo specifies a version, perform an additional check;
|
||||
if (version) {
|
||||
const minVer = minVersion(version);
|
||||
enabled = minVer?.major ? minVer.major < currentMajor : true;
|
||||
}
|
||||
|
||||
return [enabled, `https://${previousMajor}x.ant.design`, previousMajor];
|
||||
}, [version, pkg.version]);
|
||||
|
||||
const mergedChildren = !iframe && clientOnly ? <ClientOnly>{children}</ClientOnly> : children;
|
||||
const demoUrlWithTheme = useMemo(() => {
|
||||
return `${demoUrl}${isDark ? '?theme=dark' : ''}`;
|
||||
}, [demoUrl, isDark]);
|
||||
|
||||
const iframePreview = useMemo(() => {
|
||||
if (!iframe) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<BrowserFrame>
|
||||
<iframe
|
||||
src={demoUrlWithTheme}
|
||||
@@ -116,22 +150,22 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
|
||||
className="iframe-demo"
|
||||
/>
|
||||
</BrowserFrame>
|
||||
) : (
|
||||
mergedChildren
|
||||
);
|
||||
}
|
||||
}, [demoUrlWithTheme, iframe]);
|
||||
|
||||
const previewContent = iframePreview ?? mergedChildren;
|
||||
|
||||
const codeBoxClass = clsx('code-box', {
|
||||
expand: codeExpand,
|
||||
'code-box-debug': originDebug,
|
||||
'code-box-simplify': simplify,
|
||||
'code-box-simplify': simplify && !iframe,
|
||||
});
|
||||
|
||||
const highlightClass = clsx('highlight-wrapper', {
|
||||
'highlight-wrapper-expand': codeExpand,
|
||||
});
|
||||
|
||||
const backgroundGrey = theme.includes('dark') ? '#303030' : '#f0f2f5';
|
||||
const backgroundGrey = isDark ? '#303030' : '#f0f2f5';
|
||||
|
||||
const codeBoxDemoStyle: React.CSSProperties = {
|
||||
padding: iframe || compact ? 0 : undefined,
|
||||
@@ -139,6 +173,47 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
|
||||
backgroundColor: background === 'grey' ? backgroundGrey : undefined,
|
||||
};
|
||||
|
||||
const debugOptions: MenuProps['items'] = [
|
||||
{
|
||||
key: 'online',
|
||||
label: (
|
||||
<a
|
||||
aria-label="Go to online documentation"
|
||||
href={generateDocUrl()}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<FormattedMessage id="app.demo.online" />
|
||||
</a>
|
||||
),
|
||||
icon: (
|
||||
<Tag variant="filled" color="blue">
|
||||
ant.design
|
||||
</Tag>
|
||||
),
|
||||
enabled: enableDocsOnlineUrl,
|
||||
},
|
||||
{
|
||||
key: 'previousVersion',
|
||||
label: (
|
||||
<a
|
||||
aria-label="Go to previous version documentation"
|
||||
href={generateDocUrl(previousVersionDomain)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<FormattedMessage id="app.demo.previousVersion" values={{ version: previousVersion }} />
|
||||
</a>
|
||||
),
|
||||
icon: (
|
||||
<Tag variant="filled" color="purple">
|
||||
v{previousVersion}
|
||||
</Tag>
|
||||
),
|
||||
enabled: supportsPreviousVersionDemo,
|
||||
},
|
||||
].filter(({ enabled }) => showDebug && enabled);
|
||||
|
||||
const codeBox: React.ReactNode = (
|
||||
<section className={codeBoxClass} id={asset.id}>
|
||||
<section
|
||||
@@ -147,7 +222,7 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
|
||||
style={codeBoxDemoStyle}
|
||||
ref={demoContainer}
|
||||
>
|
||||
{liveDemoNode || <React.StrictMode>{previewDemo.current}</React.StrictMode>}
|
||||
{liveDemoNode || <React.StrictMode>{previewContent}</React.StrictMode>}
|
||||
</section>
|
||||
{!simplify && (
|
||||
<section className="code-box-meta markdown">
|
||||
@@ -170,8 +245,7 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
|
||||
/>
|
||||
)}
|
||||
<Actions
|
||||
showOnlineUrl={showOnlineUrl}
|
||||
docsOnlineUrl={docsOnlineUrl}
|
||||
debugOptions={debugOptions}
|
||||
entryCode={entryCode}
|
||||
styleCode={style}
|
||||
pkgDependencyList={pkgDependencyList}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Skeleton } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
skeletonWrapper: css`
|
||||
width: 100% !important;
|
||||
height: 250px;
|
||||
@@ -12,7 +12,6 @@ const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
}));
|
||||
|
||||
const DemoFallback = () => {
|
||||
const { styles } = useStyle();
|
||||
return (
|
||||
<Skeleton.Node
|
||||
active
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { FC } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { CheckOutlined, SketchOutlined } from '@ant-design/icons';
|
||||
import { App } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import copy from '../../../../components/_util/copy';
|
||||
import { nodeToGroup } from 'html2sketch';
|
||||
|
||||
@@ -22,7 +22,7 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
const styles = createStaticStyles(({ cssVar, css }) => ({
|
||||
wrapper: css`
|
||||
position: relative;
|
||||
border: 1px solid ${cssVar.colorBorderSecondary};
|
||||
@@ -72,7 +72,6 @@ const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
}));
|
||||
|
||||
const DesignPreviewer: FC<AntdPreviewerProps> = ({ children, title, description, tip, asset }) => {
|
||||
const { styles } = useStyle();
|
||||
const demoRef = useRef<HTMLDivElement>(null);
|
||||
const [copied, setCopied] = React.useState<boolean>(false);
|
||||
const { message } = App.useApp();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { BugOutlined } from '@ant-design/icons';
|
||||
import { Button, Flex, Popover, theme } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles, cx } from 'antd-style';
|
||||
import dayjs from 'dayjs';
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
@@ -33,7 +33,7 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
const styles = createStaticStyles(({ cssVar, css }) => ({
|
||||
container: css`
|
||||
margin-block: ${cssVar.margin};
|
||||
padding: ${cssVar.padding};
|
||||
@@ -49,8 +49,6 @@ const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
const RefinedChangelog: React.FC<React.PropsWithChildren<RefinedChangelogProps>> = (props) => {
|
||||
const { version, date, children } = props;
|
||||
|
||||
const { styles, cx } = useStyle();
|
||||
|
||||
const memoizedValue = React.useMemo<ContextProps>(() => {
|
||||
const realVersion = version || '0.0.0';
|
||||
const bugVersionInfo = matchDeprecated(realVersion);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Suspense } from 'react';
|
||||
import type { SandpackSetup } from '@codesandbox/sandpack-react';
|
||||
import { Skeleton } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { useSearchParams } from 'dumi';
|
||||
|
||||
import { version } from '../../../../package.json';
|
||||
@@ -17,7 +17,7 @@ const root = createRoot(document.getElementById("root"));
|
||||
root.render(<App />);
|
||||
`;
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
fallback: css`
|
||||
width: 100%;
|
||||
> * {
|
||||
@@ -32,7 +32,6 @@ const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
}));
|
||||
|
||||
const SandpackFallback: React.FC = () => {
|
||||
const { styles } = useStyle();
|
||||
return (
|
||||
<div className={styles.fallback}>
|
||||
<Skeleton.Node active style={{ height: 500, width: '100%' }}>
|
||||
@@ -83,6 +82,7 @@ const Sandpack: React.FC<React.PropsWithChildren<SandpackProps>> = (props) => {
|
||||
<OriginSandpack
|
||||
theme={searchParams.getAll('theme').includes('dark') ? 'dark' : undefined}
|
||||
customSetup={setup}
|
||||
template="vite-react-ts"
|
||||
options={options}
|
||||
files={{
|
||||
'index.tsx': indexContent,
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import React from 'react';
|
||||
import { FastColor } from '@ant-design/fast-color';
|
||||
import { Flex, theme } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import tokenMeta from 'antd/es/version/token-meta.json';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => {
|
||||
const styles = createStaticStyles(({ cssVar, css }) => {
|
||||
const height = cssVar.controlHeightLG;
|
||||
const dotSize = height / 5;
|
||||
|
||||
@@ -63,7 +63,6 @@ interface ColorCircleProps {
|
||||
}
|
||||
|
||||
const ColorCircle: React.FC<ColorCircleProps> = ({ color }) => {
|
||||
const { styles } = useStyle();
|
||||
return (
|
||||
<Flex align="center" gap={4}>
|
||||
<div className={styles.dot} style={{ background: color }} />
|
||||
@@ -79,7 +78,6 @@ export interface TokenCompareProps {
|
||||
const TokenCompare: React.FC<TokenCompareProps> = (props) => {
|
||||
const { tokenNames = '' } = props;
|
||||
const [, lang] = useLocale();
|
||||
const { styles } = useStyle();
|
||||
|
||||
const tokenList = React.useMemo(() => {
|
||||
const list = tokenNames.split('|');
|
||||
|
||||
@@ -39,12 +39,12 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const useStyle = createStyles(({ css, cssVar, token }) => ({
|
||||
codeSpan: css`
|
||||
margin: 0 1px;
|
||||
padding: 0.2em 0.4em;
|
||||
font-size: 0.9em;
|
||||
background: ${cssVar.siteMarkdownCodeBg};
|
||||
background: ${token.siteMarkdownCodeBg};
|
||||
border: 1px solid ${cssVar.colorSplit};
|
||||
border-radius: ${cssVar.borderRadiusSM};
|
||||
font-family: monospace;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { PauseCircleFilled, PlayCircleFilled } from '@ant-design/icons';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
const useStyles = createStyles(({ cx, cssVar }) => {
|
||||
const styles = createStaticStyles(({ css, cx, cssVar }) => {
|
||||
const play = css`
|
||||
position: absolute;
|
||||
inset-inline-end: ${cssVar.paddingLG};
|
||||
@@ -45,7 +45,6 @@ const VideoPlayer: React.FC<React.HtmlHTMLAttributes<HTMLVideoElement>> = ({
|
||||
className,
|
||||
...restProps
|
||||
}) => {
|
||||
const { styles } = useStyles();
|
||||
const videoRef = React.useRef<HTMLVideoElement>(null);
|
||||
const [playing, setPlaying] = React.useState(false);
|
||||
|
||||
|
||||
@@ -1,67 +1,60 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { RightCircleOutlined } from '@ant-design/icons';
|
||||
import type { TreeGraph } from '@antv/g6';
|
||||
import { Flex } from 'antd';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { useRouteMeta } from 'dumi';
|
||||
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
import { renderReactToHTMLString } from '../../../theme/utils/renderReactToHTML';
|
||||
import { useMermaidCode } from './useMermaidCode';
|
||||
|
||||
interface BehaviorMapItem {
|
||||
export interface BehaviorMapItem {
|
||||
id: string;
|
||||
label: string;
|
||||
targetType?: 'mvp' | 'extension';
|
||||
children?: BehaviorMapItem[];
|
||||
link?: string;
|
||||
collapsed?: boolean;
|
||||
type?: 'behavior-start-node' | 'behavior-sub-node';
|
||||
}
|
||||
|
||||
const dataTransform = (rootData: BehaviorMapItem) => {
|
||||
const changeData = (data: BehaviorMapItem, level = 0) => {
|
||||
const clonedData: BehaviorMapItem = { ...data };
|
||||
switch (level) {
|
||||
case 0:
|
||||
clonedData.type = 'behavior-start-node';
|
||||
break;
|
||||
case 1:
|
||||
clonedData.type = 'behavior-sub-node';
|
||||
clonedData.collapsed = true;
|
||||
break;
|
||||
default:
|
||||
clonedData.type = 'behavior-sub-node';
|
||||
break;
|
||||
}
|
||||
if (Array.isArray(data.children)) {
|
||||
clonedData.children = data.children.map((child) => changeData(child, level + 1));
|
||||
}
|
||||
return clonedData;
|
||||
};
|
||||
return changeData(rootData);
|
||||
};
|
||||
export interface BehaviorMapProps {
|
||||
data: BehaviorMapItem;
|
||||
}
|
||||
|
||||
const useStyle = createStyles(({ cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
container: css`
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 600px;
|
||||
height: fit-content;
|
||||
background-color: ${cssVar.colorBgLayout};
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: ${cssVar.borderRadiusLG};
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`,
|
||||
chartContainer: css`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
> svg {
|
||||
margin: auto;
|
||||
}
|
||||
`,
|
||||
title: css`
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
inset-inline-start: 20px;
|
||||
font-size: ${cssVar.fontSizeLG};
|
||||
z-index: 10;
|
||||
`,
|
||||
tips: css`
|
||||
display: flex;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
inset-inline-end: 20px;
|
||||
z-index: 10;
|
||||
border-radius: 4px;
|
||||
font-size: ${cssVar.fontSize};
|
||||
`,
|
||||
mvp: css`
|
||||
margin-inline-end: ${cssVar.marginMD};
|
||||
@@ -72,7 +65,7 @@ const useStyle = createStyles(({ cssVar }) => ({
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-inline-end: ${cssVar.marginXS};
|
||||
background-color: #1677ff;
|
||||
background-color: rgb(22, 119, 255);
|
||||
border-radius: 50%;
|
||||
content: '';
|
||||
}
|
||||
@@ -85,7 +78,7 @@ const useStyle = createStyles(({ cssVar }) => ({
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-inline-end: ${cssVar.marginXS};
|
||||
background-color: #a0a0a0;
|
||||
background-color: rgb(160, 160, 160);
|
||||
border-radius: 50%;
|
||||
content: '';
|
||||
}
|
||||
@@ -105,219 +98,67 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
export interface BehaviorMapProps {
|
||||
data: BehaviorMapItem;
|
||||
}
|
||||
|
||||
const BehaviorMap: React.FC<BehaviorMapProps> = ({ data }) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { styles } = useStyle();
|
||||
const chartRef = useRef<HTMLDivElement>(null);
|
||||
const [locale] = useLocale(locales);
|
||||
|
||||
const meta = useRouteMeta();
|
||||
|
||||
const graphRef = useRef<TreeGraph>(null);
|
||||
const mermaidCode = useMermaidCode(data);
|
||||
|
||||
const cancelledRef = useRef<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
import('@antv/g6').then((G6) => {
|
||||
G6.registerNode('behavior-start-node', {
|
||||
draw: (cfg, group) => {
|
||||
const textWidth = G6.Util.getTextSize(cfg!.label, 16)[0];
|
||||
const size = [textWidth + 20 * 2, 48];
|
||||
const keyShape = group!.addShape('rect', {
|
||||
name: 'start-node',
|
||||
attrs: {
|
||||
width: size[0],
|
||||
height: size[1],
|
||||
y: -size[1] / 2,
|
||||
radius: 8,
|
||||
fill: '#fff',
|
||||
},
|
||||
});
|
||||
group!.addShape('text', {
|
||||
attrs: {
|
||||
text: `${cfg!.label}`,
|
||||
fill: 'rgba(0, 0, 0, 0.88)',
|
||||
fontSize: 16,
|
||||
fontWeight: 500,
|
||||
x: 20,
|
||||
textBaseline: 'middle',
|
||||
},
|
||||
name: 'start-node-text',
|
||||
});
|
||||
return keyShape;
|
||||
},
|
||||
getAnchorPoints() {
|
||||
return [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
];
|
||||
},
|
||||
});
|
||||
cancelledRef.current = false;
|
||||
|
||||
G6.registerNode(
|
||||
'behavior-sub-node',
|
||||
{
|
||||
draw: (cfg, group) => {
|
||||
const textWidth = G6.Util.getTextSize(cfg!.label, 14)[0];
|
||||
const padding = 16;
|
||||
const size = [
|
||||
textWidth + 16 * 2 + (cfg!.targetType ? 12 : 0) + (cfg!.link ? 20 : 0),
|
||||
40,
|
||||
];
|
||||
const keyShape = group!.addShape('rect', {
|
||||
name: 'sub-node',
|
||||
attrs: {
|
||||
width: size[0],
|
||||
height: size[1],
|
||||
y: -size[1] / 2,
|
||||
radius: 8,
|
||||
fill: '#fff',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
});
|
||||
group!.addShape('text', {
|
||||
attrs: {
|
||||
text: `${cfg!.label}`,
|
||||
x: cfg!.targetType ? 12 + 16 : padding,
|
||||
fill: 'rgba(0, 0, 0, 0.88)',
|
||||
fontSize: 14,
|
||||
textBaseline: 'middle',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
name: 'sub-node-text',
|
||||
});
|
||||
if (cfg!.targetType) {
|
||||
group!.addShape('rect', {
|
||||
name: 'sub-node-type',
|
||||
attrs: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
radius: 4,
|
||||
y: -4,
|
||||
x: 12,
|
||||
fill: cfg!.targetType === 'mvp' ? '#1677ff' : '#A0A0A0',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
});
|
||||
}
|
||||
if (cfg!.children) {
|
||||
const { length } = cfg!.children as any;
|
||||
group!.addShape('rect', {
|
||||
name: 'sub-node-children-length',
|
||||
attrs: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
radius: 10,
|
||||
y: -10,
|
||||
x: size[0] - 4,
|
||||
fill: '#404040',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
});
|
||||
group!.addShape('text', {
|
||||
name: 'sub-node-children-length-text',
|
||||
attrs: {
|
||||
text: `${length}`,
|
||||
x: size[0] + 6 - G6.Util.getTextSize(`${length}`, 12)[0] / 2,
|
||||
textBaseline: 'middle',
|
||||
fill: '#fff',
|
||||
fontSize: 12,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
});
|
||||
}
|
||||
if (cfg!.link) {
|
||||
group!.addShape('dom', {
|
||||
attrs: {
|
||||
width: 16,
|
||||
height: 16,
|
||||
x: size[0] - 12 - 16,
|
||||
y: -8,
|
||||
cursor: 'pointer',
|
||||
// DOM's html
|
||||
html: renderReactToHTMLString(
|
||||
<Flex align="center" justify="center">
|
||||
<RightCircleOutlined style={{ color: '#BFBFBF' }} />
|
||||
</Flex>,
|
||||
),
|
||||
},
|
||||
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
|
||||
name: 'sub-node-link',
|
||||
});
|
||||
}
|
||||
return keyShape;
|
||||
},
|
||||
getAnchorPoints() {
|
||||
return [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
];
|
||||
},
|
||||
options: {
|
||||
stateStyles: {
|
||||
hover: {
|
||||
stroke: '#1677ff',
|
||||
'sub-node-link': {
|
||||
html: renderReactToHTMLString(
|
||||
<Flex align="center" justify="center">
|
||||
<RightCircleOutlined style={{ color: '#1677ff' }} />
|
||||
</Flex>,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'rect',
|
||||
);
|
||||
graphRef.current = new G6.TreeGraph({
|
||||
container: ref.current!,
|
||||
width: ref.current!.scrollWidth,
|
||||
height: ref.current!.scrollHeight,
|
||||
renderer: 'svg',
|
||||
modes: {
|
||||
default: ['collapse-expand', 'drag-canvas'],
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'cubic-horizontal',
|
||||
style: { lineWidth: 1, stroke: '#BFBFBF' },
|
||||
},
|
||||
layout: {
|
||||
type: 'mindmap',
|
||||
direction: 'LR',
|
||||
getHeight: () => 48,
|
||||
getWidth: (node: any) => G6.Util.getTextSize(node.label, 16)[0] + 20 * 2,
|
||||
getVGap: () => 10,
|
||||
getHGap: () => 60,
|
||||
getSide: (node: any) => node.data.direction,
|
||||
},
|
||||
});
|
||||
const renderChart = async () => {
|
||||
if (!chartRef.current || !mermaidCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
graphRef.current?.on('node:mouseenter', (e) => {
|
||||
graphRef.current?.setItemState(e.item!, 'hover', true);
|
||||
});
|
||||
graphRef.current?.on('node:mouseleave', (e) => {
|
||||
graphRef.current?.setItemState(e.item!, 'hover', false);
|
||||
});
|
||||
graphRef.current?.on('node:click', (e) => {
|
||||
const { link } = e.item!.getModel();
|
||||
if (link) {
|
||||
window.location.hash = link as string;
|
||||
try {
|
||||
const mermaid = (await import('mermaid')).default;
|
||||
|
||||
if (cancelledRef.current) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
graphRef.current?.data(dataTransform(data));
|
||||
graphRef.current?.render();
|
||||
graphRef.current?.fitCenter();
|
||||
});
|
||||
return () => {
|
||||
graphRef.current?.destroy();
|
||||
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'base',
|
||||
securityLevel: 'strict',
|
||||
flowchart: {
|
||||
htmlLabels: true,
|
||||
curve: 'linear',
|
||||
rankSpacing: 150,
|
||||
nodeSpacing: 10,
|
||||
},
|
||||
});
|
||||
|
||||
const id = `mermaid-${Date.now()}`;
|
||||
|
||||
const { svg } = await mermaid.render(id, mermaidCode);
|
||||
|
||||
if (!cancelledRef.current && chartRef.current) {
|
||||
chartRef.current.innerHTML = svg;
|
||||
}
|
||||
} catch {
|
||||
if (!cancelledRef.current && chartRef.current) {
|
||||
chartRef.current.innerHTML = 'Render Error';
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [data]);
|
||||
|
||||
renderChart();
|
||||
|
||||
return () => {
|
||||
cancelledRef.current = true;
|
||||
};
|
||||
}, [mermaidCode]);
|
||||
|
||||
return (
|
||||
<div ref={ref} className={styles.container}>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.title}>{`${meta.frontmatter.title} ${locale.behaviorMap}`}</div>
|
||||
<div ref={chartRef} className={styles.chartContainer} />
|
||||
<div className={styles.tips}>
|
||||
<div className={styles.mvp}>{locale.MVPPurpose}</div>
|
||||
<div className={styles.extension}>{locale.extensionPurpose}</div>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { Suspense } from 'react';
|
||||
import { Skeleton } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
import type { BehaviorMapProps } from './BehaviorMap';
|
||||
|
||||
const InternalBehaviorMap = React.lazy(() => import('./BehaviorMap'));
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
const styles = createStaticStyles(({ cssVar, css }) => ({
|
||||
fallback: css`
|
||||
width: 100%;
|
||||
> * {
|
||||
@@ -32,7 +32,6 @@ const locales = {
|
||||
};
|
||||
|
||||
const BehaviorMapFallback: React.FC = () => {
|
||||
const { styles } = useStyle();
|
||||
const [locale] = useLocale(locales);
|
||||
return (
|
||||
<div className={styles.fallback}>
|
||||
|
||||
46
.dumi/theme/common/BehaviorMap/useMermaidCode.ts
Normal file
46
.dumi/theme/common/BehaviorMap/useMermaidCode.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import type { BehaviorMapItem } from './BehaviorMap';
|
||||
|
||||
const generateMermaidCode = (root: BehaviorMapItem) => {
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push('graph LR');
|
||||
|
||||
lines.push(`classDef baseNode fill:#fff,stroke:none,stroke-width:0px,rx:5,ry:5,font-size:14px`);
|
||||
|
||||
const traverse = (node: BehaviorMapItem, parentId?: string) => {
|
||||
const safeId = `node_${node.id.replace(/[^a-z0-9]/gi, '_')}`;
|
||||
let labelText = node.label.replace(/"/g, "'");
|
||||
|
||||
if (!parentId) {
|
||||
lines.push(`style ${safeId} font-size:16px`);
|
||||
labelText = `**${labelText}**`;
|
||||
} else if (node.targetType === 'mvp') {
|
||||
const blueDot = `<span style="display:inline-block;width:8px;height:8px;background-color:rgb(22, 119, 255);border-radius:50%;margin-inline-end:8px;vertical-align:middle;"></span>`;
|
||||
labelText = `${blueDot}${labelText}`;
|
||||
} else if (node.targetType === 'extension') {
|
||||
const grayDot = `<span style="display:inline-block;width:8px;height:8px;background-color:rgb(160, 160, 160);border-radius:50%;margin-inline-end:8px;vertical-align:middle;"></span>`;
|
||||
labelText = `${grayDot}${labelText}`;
|
||||
}
|
||||
lines.push(`${safeId}["${labelText}"]:::baseNode`);
|
||||
|
||||
if (node.link) {
|
||||
lines.push(`click ${safeId} "#${node.link}"`);
|
||||
}
|
||||
|
||||
if (parentId) {
|
||||
lines.push(`${parentId} --> ${safeId}`);
|
||||
}
|
||||
|
||||
if (node.children && node.children.length > 0) {
|
||||
node.children.forEach((child) => traverse(child, safeId));
|
||||
}
|
||||
};
|
||||
traverse(root);
|
||||
return lines.join('\n');
|
||||
};
|
||||
|
||||
export const useMermaidCode = (data: BehaviorMapItem) => {
|
||||
return useMemo(() => generateMermaidCode(data), [data]);
|
||||
};
|
||||
@@ -20,7 +20,7 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const BezierVisualizer = (props: BezierVisualizerProps) => {
|
||||
const BezierVisualizer: React.FC<BezierVisualizerProps> = (props) => {
|
||||
const { value } = props;
|
||||
const [locale] = useLocale(locales);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
const styles = createStaticStyles(({ cssVar, css }) => ({
|
||||
browserMockup: css`
|
||||
position: relative;
|
||||
border-top: 2em solid rgba(230, 230, 230, 0.7);
|
||||
@@ -49,7 +49,6 @@ const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
}));
|
||||
|
||||
const BrowserFrame: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const { styles } = useStyle();
|
||||
return <div className={styles.browserMockup}>{children}</div>;
|
||||
};
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@ const Palette: React.FC<PaletteProps> = (props) => {
|
||||
setHexColors(colors);
|
||||
}, []);
|
||||
|
||||
const onCopy = async (colorText: string) => {
|
||||
await copy(hexColors[colorText]);
|
||||
message.success(`@${colorText} copied: ${hexColors[colorText]}`);
|
||||
const onCopy = async (colorText: string, colorKey: string) => {
|
||||
await copy(hexColors[colorKey]);
|
||||
message.success(`@${colorText} copied: ${hexColors[colorKey]}`);
|
||||
};
|
||||
|
||||
const className = direction === 'horizontal' ? 'color-palette-horizontal' : 'color-palette';
|
||||
@@ -67,13 +67,14 @@ const Palette: React.FC<PaletteProps> = (props) => {
|
||||
|
||||
const colors = Array.from<any, React.ReactNode>({ length: count }, (_, i) => {
|
||||
const colorText = `${name}-${i}`;
|
||||
const colorKey = `${name}-${i + 1}`;
|
||||
const defaultBgStyle = dark && name ? presetDarkPalettes[name][i - 1] : '';
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
ref={(node) => {
|
||||
if (node) {
|
||||
colorNodesRef.current[`${name}-${i + 1}`] = node;
|
||||
colorNodesRef.current[colorKey] = node;
|
||||
}
|
||||
}}
|
||||
className={`main-color-item palette-${name}-${i + 1}`}
|
||||
@@ -84,10 +85,10 @@ const Palette: React.FC<PaletteProps> = (props) => {
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
title="click to copy color"
|
||||
onClick={() => onCopy(colorText)}
|
||||
onClick={() => onCopy(colorText, colorKey)}
|
||||
>
|
||||
<span className="main-color-text">{colorText}</span>
|
||||
<span className="main-color-value">{hexColors[colorText]}</span>
|
||||
<span className="main-color-value">{hexColors[colorKey]}</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { ComponentProps, FC } from 'react';
|
||||
import React from 'react';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import SourceCodeEditor from 'dumi/theme-default/slots/SourceCodeEditor';
|
||||
|
||||
import LiveError from '../slots/LiveError';
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => {
|
||||
const styles = createStaticStyles(({ cssVar, css }) => {
|
||||
return {
|
||||
editor: css`
|
||||
// override dumi editor styles
|
||||
@@ -54,7 +54,6 @@ const LiveCode: FC<
|
||||
error: Error | null;
|
||||
} & Pick<ComponentProps<typeof SourceCodeEditor>, 'lang' | 'initialValue' | 'onChange'>
|
||||
> = (props) => {
|
||||
const { styles } = useStyle();
|
||||
return (
|
||||
<div className={styles.editor}>
|
||||
<SourceCodeEditor
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import { StyleProvider } from '@ant-design/cssinjs';
|
||||
import { ConfigProvider, Flex, Skeleton, Spin } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { useLocation } from 'dumi';
|
||||
|
||||
import { Common } from './styles';
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => {
|
||||
const styles = createStaticStyles(({ css, cssVar }) => {
|
||||
return {
|
||||
skeletonWrapper: css`
|
||||
width: 100%;
|
||||
@@ -27,8 +27,6 @@ const useStyle = createStyles(({ css, cssVar }) => {
|
||||
const Loading: React.FC = () => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const { styles } = useStyle();
|
||||
|
||||
let loadingNode: React.ReactNode = null;
|
||||
|
||||
if (
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
const useStyle = createStyles(({ cssVar, cx }) => {
|
||||
const styles = createStaticStyles(({ cssVar, css, cx }) => {
|
||||
const duration = cssVar.motionDurationSlow;
|
||||
|
||||
const marker = css`
|
||||
@@ -55,8 +55,6 @@ export interface MarkerProps {
|
||||
}
|
||||
|
||||
const Marker = React.memo<MarkerProps>((props) => {
|
||||
const { styles } = useStyle();
|
||||
|
||||
const { rect, primary, ...restProps } = props;
|
||||
|
||||
const rectRef = React.useRef(rect);
|
||||
|
||||
@@ -11,6 +11,7 @@ export const locales = {
|
||||
suffix: '后缀元素,包含后缀内容的布局和样式,如清除按钮、箭头图标等',
|
||||
input: '输入框元素,包含搜索输入框的样式、光标控制、字体继承等搜索相关样式,去除了边框样式',
|
||||
content: '多选容器,包含已选项的布局、间距、换行相关样式',
|
||||
clear: '清除按钮元素,包含清除按钮的布局、样式和交互效果',
|
||||
item: '多选项元素,包含边框、背景、内边距、外边距样式',
|
||||
itemContent: '多选项内容区域,包含文字的省略样式',
|
||||
itemRemove: '多选项移除按钮,包含字体相关样式',
|
||||
@@ -29,6 +30,7 @@ export const locales = {
|
||||
'Input element with search input styling, cursor control, font inheritance and other search-related styles. Remove border styles',
|
||||
content:
|
||||
'Multiple selection container with layout, spacing, and wrapping styles for selected items',
|
||||
clear: 'Clear button element with layout, styling and interactive effects for clear button',
|
||||
item: 'Multiple selection item element with border, background, padding, and margin styles',
|
||||
itemContent: 'Multiple selection item content area with text ellipsis styles',
|
||||
itemRemove: 'Multiple selection item remove button with font-related styles',
|
||||
@@ -80,13 +82,13 @@ const Block: React.FC<BlockProps> = ({
|
||||
align="center"
|
||||
>
|
||||
{!singleOnly && (
|
||||
<Segmented
|
||||
<Segmented<'single' | 'multiple'>
|
||||
options={[
|
||||
{ label: 'Single', value: 'single' },
|
||||
{ label: 'Multiple', value: 'multiple' },
|
||||
]}
|
||||
value={mode}
|
||||
onChange={(value) => onModeChange(value as 'single' | 'multiple')}
|
||||
onChange={onModeChange}
|
||||
/>
|
||||
)}
|
||||
<Component
|
||||
@@ -142,6 +144,7 @@ const SelectSemanticTemplate: React.FC<SelectSemanticTemplateProps> = ({
|
||||
{ name: 'prefix', desc: locale.prefix },
|
||||
{ name: 'content', desc: locale.content },
|
||||
{ name: 'placeholder', desc: locale.placeholder },
|
||||
{ name: 'clear', desc: locale.clear },
|
||||
{ name: 'input', desc: locale.input },
|
||||
{ name: 'suffix', desc: locale.suffix },
|
||||
{ name: 'popup.root', desc: locale['popup.root'] },
|
||||
@@ -152,11 +155,12 @@ const SelectSemanticTemplate: React.FC<SelectSemanticTemplateProps> = ({
|
||||
{ name: 'root', desc: locale.root },
|
||||
{ name: 'prefix', desc: locale.prefix },
|
||||
{ name: 'content', desc: locale.content },
|
||||
{ name: 'placeholder', desc: locale.placeholder },
|
||||
{ name: 'clear', desc: locale.clear },
|
||||
{ name: 'item', desc: locale.item },
|
||||
{ name: 'itemContent', desc: locale.itemContent },
|
||||
{ name: 'itemRemove', desc: locale.itemRemove },
|
||||
{ name: 'input', desc: locale.input },
|
||||
{ name: 'placeholder', desc: locale.placeholder },
|
||||
{ name: 'suffix', desc: locale.suffix },
|
||||
{ name: 'popup.root', desc: locale['popup.root'] },
|
||||
{ name: 'popup.list', desc: locale['popup.list'] },
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { InfoCircleOutlined, PushpinOutlined } from '@ant-design/icons';
|
||||
import { get, set } from '@rc-component/util';
|
||||
import { Button, Col, ConfigProvider, Flex, Popover, Row, Tag, theme, Typography } from 'antd';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
import Prism from 'prismjs';
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface SemanticPreviewInjectionProps {
|
||||
classNames?: Record<string, string>;
|
||||
}
|
||||
|
||||
const useStyle = createStyles(({ cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
container: css`
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
@@ -155,8 +155,6 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
|
||||
|
||||
const mergedSemantic = pinSemantic || hoverSemantic;
|
||||
|
||||
const { styles } = useStyle();
|
||||
|
||||
const hoveredSemanticClassNames = React.useMemo(() => {
|
||||
if (!mergedSemantic) {
|
||||
return semanticClassNames;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { StyleProvider } from '@ant-design/cssinjs';
|
||||
import { AntDesignOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import { Bubble, Sender } from '@ant-design/x';
|
||||
import type { SenderRef } from '@ant-design/x/es/sender';
|
||||
import { Drawer, Flex, Typography } from 'antd';
|
||||
import type { GetProp } from 'antd';
|
||||
|
||||
@@ -28,7 +30,8 @@ export interface PromptDrawerProps {
|
||||
const PromptDrawer: React.FC<PromptDrawerProps> = ({ open, onClose, onThemeChange }) => {
|
||||
const [locale] = useLocale(locales);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const senderRef = useRef<any>(null);
|
||||
|
||||
const senderRef = useRef<SenderRef>(null);
|
||||
|
||||
const [submitPrompt, loading, prompt, resText, cancelRequest] = usePromptTheme(onThemeChange);
|
||||
|
||||
@@ -51,17 +54,21 @@ const PromptDrawer: React.FC<PromptDrawerProps> = ({ open, onClose, onThemeChang
|
||||
|
||||
const nextItems: GetProp<typeof Bubble.List, 'items'> = [
|
||||
{
|
||||
key: 1,
|
||||
role: 'user',
|
||||
placement: 'end',
|
||||
content: prompt,
|
||||
avatar: { icon: <UserOutlined /> },
|
||||
avatar: <UserOutlined />,
|
||||
shape: 'corner',
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
role: 'system',
|
||||
placement: 'start',
|
||||
content: resText,
|
||||
avatar: { icon: <AntDesignOutlined /> },
|
||||
avatar: <AntDesignOutlined />,
|
||||
loading: !resText,
|
||||
messageRender: (content: string) => (
|
||||
contentRender: (content: string) => (
|
||||
<Typography>
|
||||
<pre style={{ margin: 0 }}>{content}</pre>
|
||||
</Typography>
|
||||
@@ -71,9 +78,11 @@ const PromptDrawer: React.FC<PromptDrawerProps> = ({ open, onClose, onThemeChang
|
||||
|
||||
if (!loading) {
|
||||
nextItems.push({
|
||||
key: 3,
|
||||
role: 'divider',
|
||||
placement: 'start',
|
||||
content: locale.finishTips,
|
||||
avatar: { icon: <AntDesignOutlined /> },
|
||||
avatar: <AntDesignOutlined />,
|
||||
shape: 'corner',
|
||||
});
|
||||
}
|
||||
@@ -91,16 +100,21 @@ const PromptDrawer: React.FC<PromptDrawerProps> = ({ open, onClose, onThemeChang
|
||||
afterOpenChange={handleAfterOpenChange}
|
||||
>
|
||||
<Flex vertical style={{ height: '100%' }}>
|
||||
<Bubble.List style={{ flex: 1, overflow: 'auto' }} items={items} />
|
||||
<Sender
|
||||
ref={senderRef}
|
||||
style={{ flex: 0 }}
|
||||
value={inputValue}
|
||||
onChange={setInputValue}
|
||||
onSubmit={handleSubmit}
|
||||
loading={loading}
|
||||
onCancel={cancelRequest}
|
||||
/>
|
||||
{/* Workaround for layer incompat between site `StyleProvider layer` and `@ant-design/x`.
|
||||
* See: https://github.com/ant-design/x/issues/1588
|
||||
*/}
|
||||
<StyleProvider layer={false}>
|
||||
<Bubble.List style={{ flex: 1, overflow: 'auto' }} items={items} />
|
||||
<Sender
|
||||
ref={senderRef}
|
||||
style={{ flex: 0 }}
|
||||
value={inputValue}
|
||||
onChange={setInputValue}
|
||||
onSubmit={handleSubmit}
|
||||
loading={loading}
|
||||
onCancel={cancelRequest}
|
||||
/>
|
||||
</StyleProvider>
|
||||
</Flex>
|
||||
</Drawer>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable compat/compat */
|
||||
import { useRef, useState } from 'react';
|
||||
import { XStream } from '@ant-design/x';
|
||||
import { XStream } from '@ant-design/x-sdk';
|
||||
|
||||
import type { SiteContextProps } from '../../../theme/slots/SiteContext';
|
||||
|
||||
|
||||
38
.dumi/theme/common/VersionUpgrade/ChangeLog.tsx
Normal file
38
.dumi/theme/common/VersionUpgrade/ChangeLog.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
import EN from './en-US.md';
|
||||
import CN from './zh-CN.md';
|
||||
|
||||
const changeLog = { cn: CN, en: EN };
|
||||
|
||||
const classNames = createStaticStyles(({ css }) => ({
|
||||
container: css`
|
||||
max-height: max(62vh, 500px);
|
||||
overflow-y: scroll;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #eaeaea transparent;
|
||||
/* 图片铺满 */
|
||||
&& img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
||||
const ChangeLog = () => {
|
||||
const [, lang] = useLocale();
|
||||
|
||||
const validatedLanguage = Object.keys(changeLog).includes(lang) ? lang : 'en';
|
||||
const C = changeLog[validatedLanguage];
|
||||
|
||||
return (
|
||||
<div className={classNames.container}>
|
||||
<C />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChangeLog;
|
||||
12
.dumi/theme/common/VersionUpgrade/en-US.md
Normal file
12
.dumi/theme/common/VersionUpgrade/en-US.md
Normal file
@@ -0,0 +1,12 @@
|
||||
<p align="center">
|
||||
<img src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*_DMIQaxDuXsAAAAAgDAAAAgAegCCAQ/fmt.webp" alt="Ant Design 6.0">
|
||||
</p>
|
||||
|
||||
After extensive refinement, v6 is officially released! This upgrade focuses on deep technical optimizations for better performance and developer experience:
|
||||
|
||||
- **Technical Upgrades**: Minimum React 18 support; defaults to Pure CSS Variables mode, supporting zero-runtime styles and real-time theme switching.
|
||||
- **Semantic Structure**: All components now feature semantic DOM structure, enabling flexible customization via `classNames`.
|
||||
- **New Features**: Added Masonry component; Tooltip panning; InputNumber spinner mode; Resizable Drawer; default blur mask for overlays.
|
||||
- **Smooth Migration**: Direct upgrade from v5 without codemod tools. For v5 documentation, please visit [5x.ant.design](https://5x.ant.design).
|
||||
|
||||
**[Ant Design X 2.0](https://github.com/ant-design/x/issues/1357)** for AI scenarios is also released simultaneously. Explore now!
|
||||
98
.dumi/theme/common/VersionUpgrade/index.tsx
Normal file
98
.dumi/theme/common/VersionUpgrade/index.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import React from 'react';
|
||||
import { Button, Flex, Modal, version } from 'antd';
|
||||
import { useLocation } from 'dumi';
|
||||
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
import * as utils from '../../utils';
|
||||
import ChangeLog from './ChangeLog';
|
||||
|
||||
const [major] = version.split('.');
|
||||
const STORAGE_KEY = `antd${major}-version-upgrade-notify`;
|
||||
|
||||
// 弹窗截止日期
|
||||
const NOTIFICATION_DEADLINE = new Date('2026/02/01').getTime();
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
title: 'Ant Design 6.0 现已发布 🎉',
|
||||
releasePost: '发布公告 🚀',
|
||||
fullChangelog: '完整更新日志 📝',
|
||||
},
|
||||
en: {
|
||||
title: 'Ant Design 6.0 has been released 🎉',
|
||||
releasePost: 'Release Post 🚀',
|
||||
fullChangelog: 'Full Changelog 📝',
|
||||
},
|
||||
};
|
||||
|
||||
const VersionUpgradeModal = () => {
|
||||
const [locale, lang] = useLocale(locales);
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const [open, updateOpen] = React.useState(false);
|
||||
|
||||
const isCN = lang === 'cn' || utils.isZhCN(pathname);
|
||||
|
||||
function handleClose() {
|
||||
localStorage.setItem(STORAGE_KEY, Date.now().toString());
|
||||
updateOpen(false);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
const lastTime = localStorage.getItem(STORAGE_KEY);
|
||||
const now = Date.now();
|
||||
|
||||
if (now > NOTIFICATION_DEADLINE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lastTime) {
|
||||
const timer = setTimeout(() => {
|
||||
updateOpen(true);
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
const fullChangelogUrl = utils.getLocalizedPathname('/changelog', isCN).pathname;
|
||||
|
||||
const releasePostUrl = `https://github.com/ant-design/ant-design/issues/${isCN ? '55805' : '55804'}`;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={locale.title}
|
||||
open={open}
|
||||
width={`min(90vw, 800px)`}
|
||||
centered
|
||||
onCancel={handleClose}
|
||||
styles={{
|
||||
body: {
|
||||
padding: 0,
|
||||
},
|
||||
}}
|
||||
footer={() => (
|
||||
<Flex align="center" gap="middle" justify="flex-end">
|
||||
<Button href={fullChangelogUrl} onClick={handleClose}>
|
||||
{locale.fullChangelog}
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="solid"
|
||||
href={releasePostUrl}
|
||||
target="_blank"
|
||||
onClick={handleClose}
|
||||
>
|
||||
{locale.releasePost}
|
||||
</Button>
|
||||
</Flex>
|
||||
)}
|
||||
>
|
||||
<ChangeLog />
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default VersionUpgradeModal;
|
||||
12
.dumi/theme/common/VersionUpgrade/zh-CN.md
Normal file
12
.dumi/theme/common/VersionUpgrade/zh-CN.md
Normal file
@@ -0,0 +1,12 @@
|
||||
<p align="center">
|
||||
<img src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*_DMIQaxDuXsAAAAAgDAAAAgAegCCAQ/fmt.webp" alt="Ant Design 6.0">
|
||||
</p>
|
||||
|
||||
经过大量打磨,v6 版本现已正式发布!本次升级专注于技术深度优化,带来更佳的性能与开发体验:
|
||||
|
||||
- **技术升级**:最低支持 React 18,移除历史包袱;默认启用纯 CSS 变量模式,支持零运行时样式与实时主题切换。
|
||||
- **语义化结构**:全量组件完成 DOM 语义化改造,配合 `classNames` 属性实现更灵活的样式定制。
|
||||
- **新特性**:新增 Masonry 瀑布流组件;Tooltip 支持平移;InputNumber 新增按钮模式;Drawer 支持拖拽;弹层默认开启模糊背景。
|
||||
- **平滑迁移**:v5 项目可直接升级,无需 codemod 工具。如需查看 v5 文档,请访问 [5x.ant.design](https://5x.ant.design)。
|
||||
|
||||
同时,面向 AI 场景的 **[Ant Design X 2.0](https://github.com/ant-design/x/issues/1358)** 也同步发布,欢迎探索!
|
||||
@@ -26,6 +26,7 @@ const GlobalStyle: React.FC = () => {
|
||||
.markdown img {
|
||||
max-width: calc(100% - 32px);
|
||||
max-height: 100%;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.markdown > a > img,
|
||||
|
||||
@@ -58,6 +58,7 @@ export default () => {
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
scrollbar-width: thin;
|
||||
color: ${token.colorText};
|
||||
font-size: ${token.fontSize}px;
|
||||
font-family: ${token.fontFamily};
|
||||
|
||||
@@ -16,6 +16,7 @@ import SiteContext from '../../slots/SiteContext';
|
||||
import IndexLayout from '../IndexLayout';
|
||||
import ResourceLayout from '../ResourceLayout';
|
||||
import SidebarLayout from '../SidebarLayout';
|
||||
import VersionUpgrade from '../../common/VersionUpgrade';
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
@@ -114,6 +115,7 @@ const DocLayout: React.FC = () => {
|
||||
>
|
||||
<GlobalStyles />
|
||||
{!hideLayout && <Header />}
|
||||
<VersionUpgrade />
|
||||
{content}
|
||||
</ConfigProvider>
|
||||
</>
|
||||
|
||||
@@ -65,17 +65,24 @@ const getSystemTheme = (): 'light' | 'dark' => {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
};
|
||||
|
||||
const isThemeDark = (theme: ThemeName[], systemTheme: 'dark' | 'light') => {
|
||||
return theme.includes('dark') || (theme.includes('auto') && systemTheme === 'dark');
|
||||
};
|
||||
|
||||
const GlobalLayout: React.FC = () => {
|
||||
const outlet = useOutlet();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [{ theme = [], direction, isMobile, bannerVisible = false, dynamicTheme }, setSiteState] =
|
||||
useLayoutState<SiteState>({
|
||||
isMobile: false,
|
||||
direction: 'ltr',
|
||||
theme: [],
|
||||
bannerVisible: false,
|
||||
dynamicTheme: undefined,
|
||||
});
|
||||
const [
|
||||
{ theme = [], direction, isMobile, bannerVisible = false, dynamicTheme, isDark = false },
|
||||
setSiteState,
|
||||
] = useLayoutState<SiteState>({
|
||||
isMobile: false,
|
||||
direction: 'ltr',
|
||||
theme: [],
|
||||
isDark: false,
|
||||
bannerVisible: false,
|
||||
dynamicTheme: undefined,
|
||||
});
|
||||
|
||||
const [storedTheme] = useLocalStorage<ThemeName>(ANT_DESIGN_SITE_THEME, {
|
||||
defaultValue: undefined,
|
||||
@@ -144,7 +151,7 @@ const GlobalLayout: React.FC = () => {
|
||||
updateSiteConfig({ isMobile: window.innerWidth < RESPONSIVE_MOBILE });
|
||||
}, [updateSiteConfig]);
|
||||
|
||||
// 设置 data-prefers-color 属性
|
||||
// 设置 data-prefers-color 属性和 isDark 状态
|
||||
useEffect(() => {
|
||||
const color = theme.find((t) => t === 'light' || t === 'dark');
|
||||
const html = document.querySelector<HTMLHtmlElement>('html');
|
||||
@@ -153,6 +160,8 @@ const GlobalLayout: React.FC = () => {
|
||||
} else if (color) {
|
||||
html?.setAttribute('data-prefers-color', color);
|
||||
}
|
||||
|
||||
setSiteState((prev) => ({ ...prev, isDark: isThemeDark(theme, systemTheme) }));
|
||||
}, [systemTheme, theme]);
|
||||
|
||||
// 监听系统主题变化
|
||||
@@ -180,6 +189,7 @@ const GlobalLayout: React.FC = () => {
|
||||
const urlTheme = searchParams.getAll('theme') as ThemeName[];
|
||||
const finalTheme = getFinalTheme(urlTheme);
|
||||
const _direction = searchParams.get('direction') as DirectionType;
|
||||
const _isDark = isThemeDark(finalTheme, systemTheme);
|
||||
|
||||
const storedBannerVisible = bannerLastTime && dayjs().diff(dayjs(bannerLastTime), 'day') >= 1;
|
||||
|
||||
@@ -189,6 +199,7 @@ const GlobalLayout: React.FC = () => {
|
||||
|
||||
setSiteState({
|
||||
theme: finalTheme,
|
||||
isDark: _isDark,
|
||||
direction: _direction === 'rtl' ? 'rtl' : 'ltr',
|
||||
bannerVisible: hasBannerContent && (bannerLastTime ? !!storedBannerVisible : true),
|
||||
});
|
||||
@@ -213,11 +224,12 @@ const GlobalLayout: React.FC = () => {
|
||||
direction,
|
||||
updateSiteConfig,
|
||||
theme: theme!,
|
||||
isDark: isDark!,
|
||||
isMobile: isMobile!,
|
||||
bannerVisible,
|
||||
dynamicTheme,
|
||||
}),
|
||||
[isMobile, direction, updateSiteConfig, theme, bannerVisible, dynamicTheme],
|
||||
[isMobile, direction, updateSiteConfig, theme, isDark, bannerVisible, dynamicTheme],
|
||||
);
|
||||
|
||||
const [themeConfig, componentsClassNames] = React.useMemo<
|
||||
@@ -252,7 +264,6 @@ const GlobalLayout: React.FC = () => {
|
||||
...dynamicToken,
|
||||
// colorBgContainer: 'rgba(255,0,0,0.1)',
|
||||
},
|
||||
hashed: false,
|
||||
zeroRuntime: process.env.NODE_ENV === 'production',
|
||||
},
|
||||
nextComponentsClassNames,
|
||||
@@ -296,9 +307,7 @@ const GlobalLayout: React.FC = () => {
|
||||
));
|
||||
|
||||
return (
|
||||
<DarkContext
|
||||
value={theme.includes('dark') || (theme.includes('auto') && systemTheme === 'dark')}
|
||||
>
|
||||
<DarkContext value={isDark}>
|
||||
<StyleProvider
|
||||
cache={styleCache}
|
||||
layer
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import React from 'react';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { useSearchParams } from 'dumi';
|
||||
|
||||
import CommonHelmet from '../../common/CommonHelmet';
|
||||
import Content from '../../slots/Content';
|
||||
import Sidebar from '../../slots/Sidebar';
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
main: css`
|
||||
display: flex;
|
||||
`,
|
||||
content: css`
|
||||
padding-top: ${cssVar.marginXL};
|
||||
margin-top: ${cssVar.marginXL};
|
||||
`,
|
||||
}));
|
||||
|
||||
const SidebarLayout: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
const { styles } = useStyle();
|
||||
const [searchParams] = useSearchParams();
|
||||
const hideLayout = searchParams.get('layout') === 'false';
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<CommonHelmet />
|
||||
{!hideLayout && <Sidebar />}
|
||||
<Content className={styles.content}>{children}</Content>
|
||||
<Content>{children}</Content>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
"app.demo.stackblitz": "Open in Stackblitz",
|
||||
"app.demo.codeblock": "Open in Hitu (This feature is only available in the internal network environment)",
|
||||
"app.demo.separate": "Open in a new window",
|
||||
"app.demo.online": "Online Address",
|
||||
"app.demo.online": "Official demo",
|
||||
"app.demo.previousVersion": "Previous version",
|
||||
"app.home.introduce": "A design system for enterprise-level products. Create an efficient and enjoyable work experience.",
|
||||
"app.home.pr-welcome": "💡 It is an alpha version and still in progress. Contribution from community is welcome!",
|
||||
"app.home.recommend": "Recommended",
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
"app.demo.stackblitz": "在 Stackblitz 中打开",
|
||||
"app.demo.codeblock": "在海兔中打开(此功能仅在内网环境可用)",
|
||||
"app.demo.separate": "在新窗口打开",
|
||||
"app.demo.online": "线上地址",
|
||||
"app.demo.online": "官网示例",
|
||||
"app.demo.previousVersion": "历史版本",
|
||||
"app.home.introduce": "企业级产品设计体系,创造高效愉悦的工作体验",
|
||||
"app.home.pr-welcome": "💡 当前为 alpha 版本,仍在开发中。欢迎社区一起共建,让 Ant Design 变得更好!",
|
||||
"app.home.recommend": "精彩推荐",
|
||||
|
||||
@@ -157,21 +157,11 @@ const RoutesPlugin = async (api: IApi) => {
|
||||
api.modifyRoutes((routes) => {
|
||||
// TODO: append extra routes, such as home, changelog, form-v3
|
||||
|
||||
/**
|
||||
* **important!** Make sure that the `id` and `path` are consistent.
|
||||
* see: https://github.com/ant-design/ant-design/issues/55960
|
||||
*/
|
||||
const extraRoutesList: IRoute[] = [
|
||||
{
|
||||
id: 'changelog-cn',
|
||||
path: 'changelog-cn',
|
||||
absPath: '/changelog-cn',
|
||||
parentId: 'DocLayout',
|
||||
file: resolve('../../CHANGELOG.zh-CN.md'),
|
||||
},
|
||||
{
|
||||
id: 'components-changelog-cn',
|
||||
path: 'components/changelog-cn',
|
||||
absPath: '/changelog-cn',
|
||||
parentId: 'DocLayout',
|
||||
file: resolve('../../CHANGELOG.zh-CN.md'),
|
||||
},
|
||||
{
|
||||
id: 'changelog',
|
||||
path: 'changelog',
|
||||
@@ -180,12 +170,26 @@ const RoutesPlugin = async (api: IApi) => {
|
||||
file: resolve('../../CHANGELOG.en-US.md'),
|
||||
},
|
||||
{
|
||||
id: 'components-changelog',
|
||||
id: 'changelog-cn',
|
||||
path: 'changelog-cn',
|
||||
absPath: '/changelog-cn',
|
||||
parentId: 'DocLayout',
|
||||
file: resolve('../../CHANGELOG.zh-CN.md'),
|
||||
},
|
||||
{
|
||||
id: 'components/changelog',
|
||||
path: 'components/changelog',
|
||||
absPath: '/changelog',
|
||||
absPath: '/components/changelog',
|
||||
parentId: 'DocLayout',
|
||||
file: resolve('../../CHANGELOG.en-US.md'),
|
||||
},
|
||||
{
|
||||
id: 'components/changelog-cn',
|
||||
path: 'components/changelog-cn',
|
||||
absPath: '/components/changelog-cn',
|
||||
parentId: 'DocLayout',
|
||||
file: resolve('../../CHANGELOG.zh-CN.md'),
|
||||
},
|
||||
];
|
||||
|
||||
extraRoutesList.forEach((itemRoute) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { RightOutlined, YuqueOutlined, ZhihuOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Divider } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
@@ -10,7 +10,7 @@ import JuejinIcon from '../../../theme/icons/JuejinIcon';
|
||||
const ANTD_IMG_URL =
|
||||
'https://picx.zhimg.com/v2-3b2bca09c2771e7a82a81562e806be4d.jpg?source=d16d100b';
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
const styles = createStaticStyles(({ cssVar, css }) => ({
|
||||
card: css`
|
||||
width: 100%;
|
||||
margin: calc(${cssVar.marginMD} * 2) 0;
|
||||
@@ -113,19 +113,17 @@ interface Props {
|
||||
const ColumnCard: React.FC<Props> = ({ zhihuLink, yuqueLink, juejinLink }) => {
|
||||
const [locale] = useLocale(locales);
|
||||
const {
|
||||
styles: {
|
||||
card,
|
||||
bigTitle,
|
||||
cardBody,
|
||||
leftCard,
|
||||
title,
|
||||
subTitle,
|
||||
logo,
|
||||
arrowIcon,
|
||||
zlBtn,
|
||||
discussLogo,
|
||||
},
|
||||
} = useStyle();
|
||||
card,
|
||||
bigTitle,
|
||||
cardBody,
|
||||
leftCard,
|
||||
title,
|
||||
subTitle,
|
||||
logo,
|
||||
arrowIcon,
|
||||
zlBtn,
|
||||
discussLogo,
|
||||
} = styles;
|
||||
if (!zhihuLink && !yuqueLink && !juejinLink) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { Suspense } from 'react';
|
||||
import ContributorsList from '@qixian.cs/github-contributors-list';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
import { useIntl } from 'dumi';
|
||||
|
||||
import SiteContext from '../SiteContext';
|
||||
import ContributorAvatar from './ContributorAvatar';
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => ({
|
||||
const styles = createStaticStyles(({ cssVar, css }) => ({
|
||||
listMobile: css`
|
||||
margin: 1em 0 !important;
|
||||
`,
|
||||
@@ -38,11 +38,16 @@ interface ContributorsProps {
|
||||
}
|
||||
|
||||
// 这些机器人账号不需要展示
|
||||
const blockList = ['github-actions', 'copilot', 'renovate', 'dependabot'];
|
||||
const blockList = [
|
||||
'github-actions',
|
||||
'copilot',
|
||||
'renovate',
|
||||
'dependabot',
|
||||
'gemini-code-assist[bot]',
|
||||
];
|
||||
|
||||
const Contributors: React.FC<ContributorsProps> = ({ filename }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const { styles } = useStyle();
|
||||
const { isMobile } = React.use(SiteContext);
|
||||
|
||||
if (!filename) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { removeCSS, updateCSS } from '@rc-component/util/lib/Dom/dynamicCSS';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
|
||||
@@ -20,7 +20,7 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ css, cssVar }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
container: css`
|
||||
position: fixed;
|
||||
inset-inline-start: 0;
|
||||
@@ -80,8 +80,6 @@ const InfoNewVersion: React.FC = () => {
|
||||
removeCSS(whereCls);
|
||||
}, []);
|
||||
|
||||
const { styles } = useStyle();
|
||||
|
||||
if (supportWhere) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import Link from '../../common/Link';
|
||||
import * as utils from '../../utils';
|
||||
|
||||
const useStyle = createStyles(({ cssVar, token, css }) => {
|
||||
const { headerHeight } = token;
|
||||
const { colorTextHeading, mobileMaxWidth } = cssVar;
|
||||
const { headerHeight, mobileMaxWidth } = token;
|
||||
const { colorTextHeading } = cssVar;
|
||||
|
||||
return {
|
||||
logo: css`
|
||||
@@ -36,7 +36,7 @@ const useStyle = createStyles(({ cssVar, token, css }) => {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: ${mobileMaxWidth}) {
|
||||
@media only screen and (max-width: ${mobileMaxWidth}px) {
|
||||
padding-inline-start: 0;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { omit } from '@rc-component/util';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
export interface SwitchBtnProps {
|
||||
@@ -18,12 +18,13 @@ export interface SwitchBtnProps {
|
||||
|
||||
const BASE_SIZE = '1.2em';
|
||||
|
||||
const useStyle = createStyles(({ cssVar, css }) => {
|
||||
const styles = createStaticStyles(({ cssVar, css }) => {
|
||||
return {
|
||||
btn: css`
|
||||
width: ${cssVar.controlHeight};
|
||||
.btn-inner {
|
||||
transition: all ${cssVar.motionDurationMid};
|
||||
display: flex;
|
||||
}
|
||||
img {
|
||||
width: ${BASE_SIZE};
|
||||
@@ -64,9 +65,7 @@ const useStyle = createStyles(({ cssVar, css }) => {
|
||||
const SwitchBtn: React.FC<SwitchBtnProps> = (props) => {
|
||||
const { label1, label2, tooltip1, tooltip2, value, pure, onClick, ...rest } = props;
|
||||
|
||||
const {
|
||||
styles: { btn, innerDiv, labelStyle, label1Style, label2Style },
|
||||
} = useStyle();
|
||||
const { btn, innerDiv, labelStyle, label1Style, label2Style } = styles;
|
||||
|
||||
const node = (
|
||||
<Button
|
||||
|
||||
@@ -2,11 +2,14 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { GithubOutlined, MenuOutlined } from '@ant-design/icons';
|
||||
import { Alert, Button, Col, ConfigProvider, Popover, Row, Select, Tooltip } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import type { DefaultOptionType } from 'antd/es/select';
|
||||
import { clsx } from 'clsx';
|
||||
import dayjs from 'dayjs';
|
||||
import { useLocation, useSiteData } from 'dumi';
|
||||
import DumiSearchBar from 'dumi/theme-default/slots/SearchBar';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import versionsFile from '../../../../public/versions.json';
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
import useLocalStorage from '../../../hooks/useLocalStorage';
|
||||
import { getBannerData } from '../../../pages/index/components/util';
|
||||
@@ -14,7 +17,6 @@ import ThemeSwitch from '../../common/ThemeSwitch';
|
||||
import DirectionIcon from '../../icons/DirectionIcon';
|
||||
import { ANT_DESIGN_NOT_SHOW_BANNER } from '../../layouts/GlobalLayout';
|
||||
import * as utils from '../../utils';
|
||||
import { getThemeConfig } from '../../utils';
|
||||
import SiteContext from '../SiteContext';
|
||||
import type { SharedProps } from './interface';
|
||||
import Logo from './Logo';
|
||||
@@ -38,7 +40,7 @@ const useStyle = createStyles(({ cssVar, token, css }) => {
|
||||
box-shadow: ${cssVar.boxShadowTertiary};
|
||||
backdrop-filter: blur(8px);
|
||||
|
||||
@media only screen and (max-width: ${cssVar.mobileMaxWidth}) {
|
||||
@media only screen and (max-width: ${token.mobileMaxWidth}px) {
|
||||
text-align: center;
|
||||
border: none;
|
||||
}
|
||||
@@ -127,12 +129,13 @@ const useStyle = createStyles(({ cssVar, token, css }) => {
|
||||
`,
|
||||
link: css`
|
||||
margin-inline-start: 10px;
|
||||
@media only screen and (max-width: ${cssVar.mobileMaxWidth}) {
|
||||
@media only screen and (max-width: ${token.mobileMaxWidth}px) {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
`,
|
||||
versionSelect: css`
|
||||
min-width: 90px;
|
||||
width: 112px;
|
||||
min-width: 112px; // 这个宽度需要和 Empty 状态的宽度保持一致
|
||||
.rc-virtual-list {
|
||||
.rc-virtual-list-holder {
|
||||
scrollbar-width: thin;
|
||||
@@ -149,19 +152,57 @@ interface HeaderState {
|
||||
searching: boolean;
|
||||
}
|
||||
|
||||
interface VersionItem {
|
||||
version: string;
|
||||
url: string;
|
||||
chineseMirrorUrl?: string;
|
||||
}
|
||||
|
||||
const fetcher = (...args: Parameters<typeof fetch>) => {
|
||||
// eslint-disable-next-line compat/compat
|
||||
return fetch(...args).then((res) => res.json());
|
||||
};
|
||||
|
||||
// ================================= Header =================================
|
||||
const Header: React.FC = () => {
|
||||
const [, lang] = useLocale();
|
||||
|
||||
const { pkg } = useSiteData();
|
||||
|
||||
const themeConfig = getThemeConfig();
|
||||
const isChineseMirror =
|
||||
typeof window !== 'undefined' && typeof window.location !== 'undefined'
|
||||
? window.location.hostname.includes('.antgroup.com')
|
||||
: false;
|
||||
|
||||
const { data: versions = [], isLoading } = useSWR<VersionItem[]>(
|
||||
process.env.NODE_ENV === 'production' && typeof window !== 'undefined'
|
||||
? `${window.location.origin}/versions.json`
|
||||
: null,
|
||||
fetcher,
|
||||
{
|
||||
fallbackData: versionsFile,
|
||||
errorRetryCount: 3,
|
||||
},
|
||||
);
|
||||
|
||||
const versionOptions = useMemo(() => {
|
||||
if (isLoading) {
|
||||
return [];
|
||||
}
|
||||
return versions.map<DefaultOptionType>((item) => {
|
||||
const isMatch = item.version.startsWith(pkg.version[0]);
|
||||
const label = isMatch ? pkg.version : item.version;
|
||||
const value = isChineseMirror && item.chineseMirrorUrl ? item.chineseMirrorUrl : item.url;
|
||||
return { value, label };
|
||||
});
|
||||
}, [versions, isLoading, pkg.version, isChineseMirror]);
|
||||
|
||||
const [headerState, setHeaderState] = useState<HeaderState>({
|
||||
menuVisible: false,
|
||||
windowWidth: 1400,
|
||||
searching: false,
|
||||
});
|
||||
|
||||
const { direction, isMobile, bannerVisible, updateSiteConfig } = React.use(SiteContext);
|
||||
const pingTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const location = useLocation();
|
||||
@@ -258,14 +299,6 @@ const Header: React.FC = () => {
|
||||
);
|
||||
|
||||
const { menuVisible, windowWidth, searching } = headerState;
|
||||
const docVersions: Record<string, string> = {
|
||||
[pkg.version]: pkg.version,
|
||||
...themeConfig?.docVersions,
|
||||
};
|
||||
const versionOptions = Object.keys(docVersions).map((version) => ({
|
||||
value: docVersions[version],
|
||||
label: version,
|
||||
}));
|
||||
|
||||
const isHome = ['', 'index', 'index-cn'].includes(pathname);
|
||||
const isZhCN = lang === 'cn';
|
||||
@@ -308,6 +341,7 @@ const Header: React.FC = () => {
|
||||
key="version"
|
||||
size="small"
|
||||
variant="filled"
|
||||
loading={isLoading}
|
||||
className={styles.versionSelect}
|
||||
defaultValue={pkg.version}
|
||||
onChange={handleVersionChange}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import MobileMenu from '@rc-component/drawer';
|
||||
import { Col, ConfigProvider, Menu } from 'antd';
|
||||
import { createStyles, useTheme } from 'antd-style';
|
||||
@@ -11,7 +11,7 @@ const useStyle = createStyles(({ cssVar, token, css }) => {
|
||||
return {
|
||||
asideContainer: css`
|
||||
min-height: 100%;
|
||||
padding-top: ${cssVar.marginXL};
|
||||
padding-top: 0;
|
||||
padding-bottom: ${cssVar.marginXXL} !important;
|
||||
font-family: Avenir, ${cssVar.fontFamily}, sans-serif;
|
||||
padding-inline: ${cssVar.paddingXXS};
|
||||
@@ -108,13 +108,22 @@ const useStyle = createStyles(({ cssVar, token, css }) => {
|
||||
|
||||
const Sidebar: React.FC = () => {
|
||||
const sidebarData = useSidebarData();
|
||||
const { isMobile, theme } = React.use(SiteContext);
|
||||
const { isMobile, isDark } = React.use(SiteContext);
|
||||
const { styles } = useStyle();
|
||||
|
||||
const [menuItems, selectedKey] = useMenu();
|
||||
const isDark = theme.includes('dark');
|
||||
const { colorBgContainer } = useTheme();
|
||||
|
||||
const defaultOpenKeys = sidebarData?.map<string>(({ title }) => title!).filter(Boolean) || [];
|
||||
const [openKeys, setOpenKeys] = React.useState<string[]>(defaultOpenKeys);
|
||||
|
||||
useEffect(() => {
|
||||
if (openKeys.join(',') === defaultOpenKeys.join(',')) {
|
||||
return;
|
||||
}
|
||||
setOpenKeys(defaultOpenKeys);
|
||||
}, [defaultOpenKeys.join(',')]);
|
||||
|
||||
const menuChild = (
|
||||
<ConfigProvider
|
||||
theme={{ components: { Menu: { itemBg: colorBgContainer, darkItemBg: colorBgContainer } } }}
|
||||
@@ -126,7 +135,8 @@ const Sidebar: React.FC = () => {
|
||||
mode="inline"
|
||||
theme={isDark ? 'dark' : 'light'}
|
||||
selectedKeys={[selectedKey]}
|
||||
defaultOpenKeys={sidebarData?.map<string>(({ title }) => title!).filter(Boolean)}
|
||||
openKeys={openKeys}
|
||||
onOpenChange={setOpenKeys}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
);
|
||||
|
||||
@@ -2,8 +2,8 @@ import * as React from 'react';
|
||||
import type { DirectionType } from 'antd/es/config-provider';
|
||||
|
||||
import type { ConfigComponentProps } from '../../../components/config-provider/context';
|
||||
import type { ThemeName } from '../common/ThemeSwitch';
|
||||
import { getBannerData } from '../../pages/index/components/util';
|
||||
import type { ThemeName } from '../common/ThemeSwitch';
|
||||
|
||||
export type SimpleComponentClassNames = Partial<
|
||||
Record<keyof ConfigComponentProps, Record<string, string>>
|
||||
@@ -14,8 +14,10 @@ export interface SiteContextProps {
|
||||
bannerVisible: boolean;
|
||||
direction: DirectionType;
|
||||
theme: ThemeName[];
|
||||
// 主题存在跟随系统模式,解耦实际生效主题
|
||||
// 应使用 isDark 而非 theme.includes('dark') 等来判断当前主题
|
||||
isDark?: boolean;
|
||||
updateSiteConfig: (props: Partial<SiteContextProps>) => void;
|
||||
|
||||
dynamicTheme?: {
|
||||
algorithm?: 'light' | 'dark';
|
||||
token: Record<string, string | number>;
|
||||
@@ -27,6 +29,7 @@ const SiteContext = React.createContext<SiteContextProps>({
|
||||
bannerVisible: !!getBannerData(),
|
||||
direction: 'ltr',
|
||||
theme: ['light'],
|
||||
isDark: false,
|
||||
updateSiteConfig: () => {},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
const chineseMirror =
|
||||
typeof location !== 'undefined' && location.hostname.includes('.antgroup.com');
|
||||
|
||||
export default {
|
||||
categoryOrder: {
|
||||
'Ant Design': 0,
|
||||
@@ -45,15 +42,4 @@ export default {
|
||||
模板文档: 3,
|
||||
'Template Document': 3,
|
||||
},
|
||||
docVersions: {
|
||||
'5.x': chineseMirror ? 'https://5x-ant-design.antgroup.com' : 'https://5x.ant.design',
|
||||
'4.x': chineseMirror ? 'https://4x-ant-design.antgroup.com' : 'https://4x.ant.design',
|
||||
'3.x': 'https://3x.ant.design',
|
||||
'2.x': 'https://2x.ant.design',
|
||||
'1.x': 'https://1x.ant.design',
|
||||
'0.12.x': 'https://012x.ant.design',
|
||||
'0.11.x': 'https://011x.ant.design',
|
||||
'0.10.x': 'https://010x.ant.design',
|
||||
'0.9.x': 'https://09x.ant.design',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ import flattenDeep from 'lodash/flattenDeep';
|
||||
import semver from 'semver';
|
||||
|
||||
import deprecatedVersions from '../../../BUG_VERSIONS.json';
|
||||
import themeConfig from '../themeConfig';
|
||||
|
||||
interface Meta {
|
||||
skip?: boolean;
|
||||
@@ -206,4 +205,12 @@ export function matchDeprecated(v: string): MatchDeprecatedResult {
|
||||
};
|
||||
}
|
||||
|
||||
export const getThemeConfig = () => themeConfig;
|
||||
/**
|
||||
* Determine if a hostname is an official domain.
|
||||
* antd creates a temporary preview site for each PR for convenient preview and testing.
|
||||
* Usually on platforms like surge.sh or Cloudflare Pages.
|
||||
*/
|
||||
export function isOfficialHost(hostname: string) {
|
||||
const officialHostnames = ['ant.design', 'antgroup.com'];
|
||||
return officialHostnames.some((official) => hostname.includes(official));
|
||||
}
|
||||
|
||||
8
.github/CONTRIBUTING.md
vendored
8
.github/CONTRIBUTING.md
vendored
@@ -4,6 +4,10 @@ Want to contribute to Ant Design? There are a few things you need to know.
|
||||
|
||||
We wrote a **[contribution guide](https://ant.design/docs/react/contributing)** to help you get started.
|
||||
|
||||
## Security
|
||||
|
||||
If you're working with GitHub Actions workflows, please read our **[Workflows Security Guide](.github/WORKFLOWS_SECURITY.md)** to understand security best practices.
|
||||
|
||||
---
|
||||
|
||||
# 参与共建
|
||||
@@ -11,3 +15,7 @@ We wrote a **[contribution guide](https://ant.design/docs/react/contributing)**
|
||||
想要给 Ant Design 贡献自己的一份力量?
|
||||
|
||||
我们写了一份 **[贡献指南](https://ant.design/docs/react/contributing-cn)** 来帮助你开始。
|
||||
|
||||
## 安全
|
||||
|
||||
如果你需要修改 GitHub Actions 工作流,请阅读我们的 **[工作流安全指南](.github/WORKFLOWS_SECURITY.md)** 以了解安全最佳实践。
|
||||
|
||||
130
.github/WORKFLOWS_SECURITY.md
vendored
Normal file
130
.github/WORKFLOWS_SECURITY.md
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
# GitHub Actions Workflows Security
|
||||
|
||||
This document describes the security measures implemented in ant-design's GitHub Actions workflows to protect against common attack vectors, particularly the "PWN Request" vulnerability.
|
||||
|
||||
## Background: PWN Request Vulnerability
|
||||
|
||||
The "PWN Request" (or "Pull Request Target") vulnerability occurs when workflows:
|
||||
|
||||
1. Use `pull_request_target`, `workflow_run`, or `issue_comment` triggers
|
||||
2. Check out code from untrusted sources (fork PRs)
|
||||
3. Execute that code with elevated privileges or access to secrets
|
||||
|
||||
This can allow attackers to:
|
||||
|
||||
- Steal repository secrets
|
||||
- Execute remote code in the CI/CD environment
|
||||
- Modify repository contents
|
||||
- Compromise the supply chain
|
||||
|
||||
**Reference**: See [GitHub Security Lab - Preventing PWN Requests](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/)
|
||||
|
||||
## Security Principles Applied
|
||||
|
||||
### 1. Safe Use of `pull_request_target`
|
||||
|
||||
All workflows using `pull_request_target` follow these rules:
|
||||
|
||||
- ✅ **NEVER** check out PR code (`actions/checkout` with PR ref)
|
||||
- ✅ **NEVER** run `npm install` or similar with PR code
|
||||
- ✅ Only interact with PR metadata (comments, labels, status)
|
||||
- ✅ Use minimal permissions (explicitly defined per job)
|
||||
|
||||
**Safe workflows:**
|
||||
|
||||
- `preview-start.yml` - Only comments on PRs
|
||||
- `pr-open-notify.yml` - Only sends notifications
|
||||
- `pr-open-check.yml` - Only validates PR content
|
||||
- `verify-files-modify.yml` - Only checks file modifications via API
|
||||
- `pr-check-merge.yml` - Only comments on branch merge PRs
|
||||
- `pr-contributor-welcome.yml` - Only comments on merged PRs
|
||||
- `visual-regression-diff-start.yml` - Only comments on PRs
|
||||
|
||||
### 2. Separation of Build and Deploy
|
||||
|
||||
We use the "build in PR, deploy in workflow_run" pattern:
|
||||
|
||||
**Build Phase** (uses `pull_request` trigger):
|
||||
|
||||
- `preview-build.yml` - Builds site from PR code with restricted permissions
|
||||
- `visual-regression-diff-build.yml` - Generates screenshots from PR code
|
||||
- Uses `pull_request` trigger (no secrets, read-only repository access)
|
||||
- Uploads build artifacts (no secrets included)
|
||||
|
||||
**Deploy Phase** (uses `workflow_run` trigger):
|
||||
|
||||
- `preview-deploy.yml` - Downloads artifacts and deploys
|
||||
- `visual-regression-diff-finish.yml` - Downloads artifacts and posts results
|
||||
- Only downloads artifacts, never checks out untrusted code
|
||||
- Has access to secrets for deployment
|
||||
- Validates PR numbers before use
|
||||
|
||||
### 3. Authorization Checks
|
||||
|
||||
Workflows that can modify repository state require authorization:
|
||||
|
||||
- ✅ `rebase.yml` - Restricts `/rebase` command to MEMBER, COLLABORATOR, or OWNER
|
||||
- ✅ `verify-files-modify.yml` - Checks contributor authority for protected paths
|
||||
- ✅ `pr-check-merge.yml` - Only runs for ant-design organization PRs
|
||||
|
||||
### 4. Minimal Permissions
|
||||
|
||||
All workflows follow the principle of least privilege:
|
||||
|
||||
```yaml
|
||||
permissions:
|
||||
contents: read # Default read-only access
|
||||
|
||||
jobs:
|
||||
specific-job:
|
||||
permissions:
|
||||
# Only grant what's needed
|
||||
issues: write
|
||||
pull-requests: write
|
||||
```
|
||||
|
||||
### 5. Pinned Action Versions
|
||||
|
||||
Critical actions are pinned to specific commit SHAs:
|
||||
|
||||
- `actions-cool/verify-files-modify@9f38a3b3d324d4d92c88c8a946001522e17ad554`
|
||||
|
||||
This prevents supply chain attacks via compromised action updates.
|
||||
|
||||
### 6. Input Validation
|
||||
|
||||
All external inputs are validated:
|
||||
|
||||
- PR numbers are validated as numeric before use
|
||||
- File paths are checked before operations
|
||||
- User associations are verified before privileged operations
|
||||
|
||||
## Workflow Security Checklist
|
||||
|
||||
When adding or modifying workflows, ensure:
|
||||
|
||||
- [ ] If using `pull_request_target`, NEVER check out PR code
|
||||
- [ ] If using `pull_request_target`, NEVER run untrusted code
|
||||
- [ ] If using `issue_comment` with code execution, check `author_association`
|
||||
- [ ] If using `workflow_run`, only download artifacts or check out base branch
|
||||
- [ ] Permissions are explicitly set to minimum required
|
||||
- [ ] Secrets are only used in trusted contexts
|
||||
- [ ] All user inputs are validated
|
||||
- [ ] Third-party actions are from trusted sources
|
||||
- [ ] Critical actions are pinned to commit SHAs
|
||||
|
||||
## Incident Response
|
||||
|
||||
If a security vulnerability is discovered:
|
||||
|
||||
1. Immediately disable the affected workflow
|
||||
2. Report to security team via [SECURITY.md](../SECURITY.md)
|
||||
3. Do not disclose publicly until patched
|
||||
4. Review all recent workflow runs for signs of exploitation
|
||||
|
||||
## References
|
||||
|
||||
- [GitHub Security Lab - Preventing PWN Requests](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/)
|
||||
- [GitHub Actions Security Best Practices](https://blog.gitguardian.com/github-actions-security-cheat-sheet/)
|
||||
- [OpenSSF - Mitigating Attack Vectors in GitHub Workflows](https://openssf.org/blog/2024/08/12/mitigating-attack-vectors-in-github-workflows/)
|
||||
- [PostHog - Shai Hulud Attack Post-Mortem](https://posthog.com/blog/nov-24-shai-hulud-attack-post-mortem)
|
||||
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -33,3 +33,7 @@ updates:
|
||||
- github-actions
|
||||
- dependencies
|
||||
- skip-verify-files
|
||||
ignore:
|
||||
# `actions/upload-artifact` modify the upload logic
|
||||
# which can not correct download by `dawidd6/action-download-artifact`
|
||||
- dependency-name: actions/upload-artifact
|
||||
|
||||
6
.github/workflows/discussion-open-check.yml
vendored
6
.github/workflows/discussion-open-check.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
discussion-create:
|
||||
permissions:
|
||||
contents: read # for visiky/dingtalk-release-notify to get latest release
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: send to dingtalk
|
||||
uses: visiky/dingtalk-release-notify@main
|
||||
@@ -21,8 +21,8 @@ jobs:
|
||||
${{ secrets.DINGDING_BOT_COLLABORATOR_TOKEN }}
|
||||
notify_title: '🔥 @${{ github.event.discussion.user.login }} 创建了讨论:${{ github.event.discussion.title }} ${{ github.event.discussion.html_url }}'
|
||||
notify_body: |
|
||||
### 🔥 [@${{ github.event.discussion.user.login }}](https://github.com/${{ github.event.discussion.user.login }}) 开启了一个讨论:
|
||||
[${{ github.event.discussion.title }}](${{ github.event.discussion.html_url }})
|
||||
### 🔥  [@${{ github.event.discussion.user.login }}](https://github.com/${{ github.event.discussion.user.login }}) 开启了一个讨论:
|
||||
[${{ github.event.discussion.title }} · Discussion #${{ github.event.discussion.number }}](${{ github.event.discussion.html_url }})
|
||||
<hr />
|
||||
notify_footer: '> 🫵🏻 欢迎前往 GitHub 进行讨论,社区需要你的帮助!'
|
||||
at_all: false # whether to ding everybody
|
||||
|
||||
2
.github/workflows/issue-check-inactive.yml
vendored
2
.github/workflows/issue-check-inactive.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
permissions:
|
||||
issues: write # for actions-cool/issues-helper to update issues
|
||||
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: check-inactive
|
||||
uses: actions-cool/issues-helper@v3
|
||||
|
||||
2
.github/workflows/issue-close-require.yml
vendored
2
.github/workflows/issue-close-require.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
permissions:
|
||||
issues: write # for actions-cool/issues-helper to update issues
|
||||
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: need reproduce
|
||||
uses: actions-cool/issues-helper@v3
|
||||
|
||||
@@ -9,7 +9,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
reminder_job:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Send reminders for inactive issues
|
||||
uses: actions/github-script@v8
|
||||
|
||||
2
.github/workflows/issue-labeled.yml
vendored
2
.github/workflows/issue-labeled.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
permissions:
|
||||
issues: write # for actions-cool/issues-helper to update issues
|
||||
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: help wanted
|
||||
if: github.event.label.name == 'help wanted'
|
||||
|
||||
6
.github/workflows/issue-open-check.yml
vendored
6
.github/workflows/issue-open-check.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
contents: read # for visiky/dingtalk-release-notify to get latest release
|
||||
issues: write # for actions-cool/issues-helper to update issues
|
||||
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions-cool/check-user-permission@v2
|
||||
id: checkUser
|
||||
@@ -101,8 +101,8 @@ jobs:
|
||||
${{ secrets.DINGDING_BOT_COLLABORATOR_TOKEN }}
|
||||
notify_title: '🔥 @${{ github.event.issue.user.login }} 创建了 issue:${{ github.event.issue.title }} ${{ github.event.issue.html_url }}'
|
||||
notify_body: |
|
||||
### 🔥 [@${{ github.event.issue.user.login }}](https://github.com/${{ github.event.issue.user.login }}) 创建了 issue:
|
||||
[${{ github.event.issue.title }}](${{ github.event.issue.html_url }})
|
||||
### 🔥  [@${{ github.event.issue.user.login }}](https://github.com/${{ github.event.issue.user.login }}) 创建了 issue:
|
||||
[${{ github.event.issue.title }} · Issue #${{ github.event.issue.number }}](${{ github.event.issue.html_url }})
|
||||
<hr />
|
||||
notify_footer: '> 🫵🏻 欢迎前往 GitHub 进行讨论,社区需要你的帮助!'
|
||||
at_all: false # whether to ding everybody
|
||||
|
||||
2
.github/workflows/issue-remove-inactive.yml
vendored
2
.github/workflows/issue-remove-inactive.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
permissions:
|
||||
issues: write # for actions-cool/issues-helper to update issues
|
||||
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: remove inactive
|
||||
if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login
|
||||
|
||||
7
.github/workflows/issue-schedule.yml
vendored
7
.github/workflows/issue-schedule.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
send-message:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Send Unconfirmed Issues to DingTalk
|
||||
uses: actions/github-script@v8
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
const data = await response.json();
|
||||
const issueList = [];
|
||||
for (const item of data.items) {
|
||||
const { created_at, html_url, pull_request, state, title, labels } = item;
|
||||
const { created_at, html_url, pull_request, state, title, labels, number } = item;
|
||||
const createdAt = new Date(created_at);
|
||||
const now = new Date();
|
||||
const diffTime = Math.abs(now - createdAt);
|
||||
@@ -60,6 +60,7 @@ jobs:
|
||||
|
||||
if (!pull_request && !html_url.includes('pull') && state === 'open') {
|
||||
issueList.push({
|
||||
number,
|
||||
html_url,
|
||||
created_at,
|
||||
title
|
||||
@@ -70,7 +71,7 @@ jobs:
|
||||
const actionTitle = process.env.actionTitle + `(${issueList.length})`;
|
||||
|
||||
const markdownList = `## ${actionTitle}\n\n`
|
||||
+ issueList.map(issue => `- [${issue.title}](${issue.html_url}) ${fromNow(issue.created_at)}`).join('\n')
|
||||
+ issueList.map(issue => `- ${fromNow(issue.created_at)} [#${issue.number} ${issue.title}](${issue.html_url})`).join('\n')
|
||||
+ `\n\n > 🫵🏻 快去帮忙处理吧,社区需要你的帮助!`;
|
||||
|
||||
console.log(markdownList);
|
||||
|
||||
6
.github/workflows/mock-project-build.yml
vendored
6
.github/workflows/mock-project-build.yml
vendored
@@ -14,7 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
pr-check-ci:
|
||||
if: github.repository == 'ant-design/ant-design'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
name: Build Project
|
||||
steps:
|
||||
- name: checkout
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
|
||||
- uses: utooland/setup-utoo@v1
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v5
|
||||
with:
|
||||
path: ~tmpProj/yarn.lock
|
||||
key: primes-${{ runner.os }}-${{ github.run_id }}
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Download success lock file as `success.lock`
|
||||
if: ${{ failure() }}
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ~tmpProj/yarn.lock
|
||||
key: primes-${{ runner.os }}-${{ github.run_id }}
|
||||
|
||||
2
.github/workflows/pkg.pr.new.yml
vendored
2
.github/workflows/pkg.pr.new.yml
vendored
@@ -3,7 +3,7 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
||||
4
.github/workflows/pr-auto-merge.yml
vendored
4
.github/workflows/pr-auto-merge.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
contents: write # for actions-cool/check-pr-ci to merge PRs
|
||||
issues: write # for actions-cool/check-pr-ci to update issues
|
||||
pull-requests: write # for actions-cool/check-pr-ci to update PRs
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions-cool/check-pr-ci@v1
|
||||
with:
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
filter-creator-authority: write
|
||||
filter-head-ref: 'master, feature, next, master-merge-feature, feature-merge-master, next-merge-master, next-merge-feature'
|
||||
filter-support-fork: false
|
||||
skip-run-names: 'deploy preview, pr-check-ci, upstream workflow summary, suggest-related-links, download visual-regression report'
|
||||
skip-run-names: 'deploy preview, deploy-to-pages, pr-check-ci, upstream workflow summary, suggest-related-links, download visual-regression report, issue-open-check'
|
||||
conflict-review-body: 😅 This branch has conflicts that must be resolved!
|
||||
success-review: true
|
||||
success-merge: true
|
||||
|
||||
4
.github/workflows/pr-check-merge.yml
vendored
4
.github/workflows/pr-check-merge.yml
vendored
@@ -12,10 +12,10 @@ jobs:
|
||||
permissions:
|
||||
issues: write # for actions-cool/issues-helper to update issues
|
||||
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
if: (github.event.pull_request.head.ref == 'next' || github.event.pull_request.head.ref == 'feature' || github.event.pull_request.head.ref == 'master') && github.event.pull_request.head.user.login == 'ant-design'
|
||||
steps:
|
||||
- uses: actions-cool/issues-helper@v3
|
||||
- uses: actions-cool/issues-helper@e2ff99831a4f13625d35064e2b3dfe65c07a0396
|
||||
with:
|
||||
actions: create-comment
|
||||
issue-number: ${{ github.event.number }}
|
||||
|
||||
4
.github/workflows/pr-contributor-welcome.yml
vendored
4
.github/workflows/pr-contributor-welcome.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
|
||||
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
|
||||
if: github.event.pull_request.merged == true && github.repository == 'ant-design/ant-design'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: get commit count
|
||||
id: get_commit_count
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
echo "COUNT=$DATA_LENGTH" >> $GITHUB_OUTPUT
|
||||
- name: Comment on PR
|
||||
if: steps.get_commit_count.outputs.COUNT < 3
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
|
||||
8
.github/workflows/pr-open-check.yml
vendored
8
.github/workflows/pr-open-check.yml
vendored
@@ -12,9 +12,9 @@ jobs:
|
||||
permissions:
|
||||
issues: write # for actions-cool/pr-welcome to create, update & react on issues
|
||||
pull-requests: write # for actions-cool/pr-welcome to request reviewer
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions-cool/pr-welcome@v1
|
||||
- uses: actions-cool/pr-welcome@4bd317d60ef3b40a3ccda39c22f66c3358010f92
|
||||
with:
|
||||
refuse-issue-label: 🎱 Collaborate PR only
|
||||
need-creator-authority: write
|
||||
@@ -30,10 +30,10 @@ jobs:
|
||||
check-changelog:
|
||||
permissions:
|
||||
pull-requests: write # for actions-cool/pr-check-fill to create or update PR comments
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: check fill
|
||||
uses: actions-cool/pr-check-fill@v1
|
||||
uses: actions-cool/pr-check-fill@35194e32fd717c88c4fde15fbde9005933c2d452
|
||||
with:
|
||||
filter-start: '|'
|
||||
require-include: '🇺🇸 English, 🇨🇳 Chinese, 🇺🇸 英文, 🇨🇳 中文'
|
||||
|
||||
8
.github/workflows/pr-open-notify.yml
vendored
8
.github/workflows/pr-open-notify.yml
vendored
@@ -9,18 +9,18 @@ permissions:
|
||||
|
||||
jobs:
|
||||
send-to-dingtalk:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: send to dingtalk
|
||||
uses: visiky/dingtalk-release-notify@main
|
||||
uses: visiky/dingtalk-release-notify@64fcb0373782b6c2f6d9b9ea3c68af80ca189585
|
||||
with:
|
||||
DING_TALK_TOKEN: |
|
||||
${{ secrets.DINGDING_BOT_TOKEN }}
|
||||
${{ secrets.DINGDING_BOT_COLLABORATOR_TOKEN }}
|
||||
notify_title: '🔥 @${{ github.event.pull_request.user.login }} 创建了 PR:${{ github.event.pull_request.title }} ${{ github.event.pull_request.html_url }}'
|
||||
notify_body: |
|
||||
### 🔥 [@${{ github.event.pull_request.user.login }}](https://github.com/${{ github.event.pull_request.user.login }}) 创建了 PR:
|
||||
[${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})
|
||||
### 🔥  [@${{ github.event.pull_request.user.login }}](https://github.com/${{ github.event.pull_request.user.login }}) 创建了 PR:
|
||||
[${{ github.event.pull_request.title }} · PR #${{ github.event.pull_request.number }}](${{ github.event.pull_request.html_url }})
|
||||
<hr />
|
||||
notify_footer: '> 🫵🏻 快去 Review 吧,社区需要你的帮助!'
|
||||
at_all: false # whether to ding everybody
|
||||
|
||||
8
.github/workflows/preview-deploy.yml
vendored
8
.github/workflows/preview-deploy.yml
vendored
@@ -14,7 +14,7 @@ permissions:
|
||||
jobs:
|
||||
upstream-workflow-summary:
|
||||
name: upstream workflow summary
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
if: github.event.workflow_run.event == 'pull_request'
|
||||
outputs:
|
||||
jobs: ${{ steps.prep-summary.outputs.result }}
|
||||
@@ -57,13 +57,13 @@ jobs:
|
||||
actions: read # for dawidd6/action-download-artifact to query and download artifacts
|
||||
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
|
||||
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
needs: upstream-workflow-summary
|
||||
if: github.event.workflow_run.event == 'pull_request'
|
||||
steps:
|
||||
# We need get PR id first
|
||||
- name: download pr artifact
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
uses: dawidd6/action-download-artifact@v12
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
# Download site artifact
|
||||
- name: download site artifact
|
||||
if: ${{ fromJSON(needs.upstream-workflow-summary.outputs.build-success) }}
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
uses: dawidd6/action-download-artifact@v12
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
4
.github/workflows/preview-start.yml
vendored
4
.github/workflows/preview-start.yml
vendored
@@ -19,10 +19,10 @@ jobs:
|
||||
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
|
||||
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
|
||||
name: start preview info
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: update status comment
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
|
||||
25
.github/workflows/rebase.yml
vendored
25
.github/workflows/rebase.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: Automatic Rebase
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
rebase:
|
||||
permissions:
|
||||
contents: write # for cirrus-actions/rebase to push code to rebase
|
||||
pull-requests: read # for cirrus-actions/rebase to get info about PR
|
||||
name: Rebase
|
||||
if: github.event.issue.pull_request != '' && (contains(github.event.comment.body, '/rebase') || contains(github.event.comment.body, '\rebase'))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Automatic Rebase
|
||||
uses: cirrus-actions/rebase@1.8
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
16
.github/workflows/release-dingtalk.yml
vendored
16
.github/workflows/release-dingtalk.yml
vendored
@@ -18,16 +18,16 @@ jobs:
|
||||
permissions:
|
||||
contents: write # for actions-cool/release-helper to create releases
|
||||
if: github.event.ref_type == 'tag'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Send to Ant Design DingGroup
|
||||
uses: actions-cool/release-helper@v2
|
||||
with:
|
||||
trigger: tag
|
||||
changelogs: 'CHANGELOG.en-US.md, CHANGELOG.zh-CN.md'
|
||||
branch: 'master, 4.x-stable'
|
||||
tag: '5*, 4*'
|
||||
latest: '5*'
|
||||
branch: 'master, 5.x-stable, 4.x-stable'
|
||||
tag: '6*, 5*, 4*'
|
||||
latest: '6*'
|
||||
dingding-token: ${{ secrets.DINGDING_BOT_TOKEN }} ${{ secrets.DINGDING_BOT_COLLABORATOR_TOKEN }} ${{ secrets.DINGDING_BOT_MAINTAINER_TOKEN }}
|
||||
dingding-msg: CHANGELOG.zh-CN.md
|
||||
msg-title: '# Ant Design {{v}} 发布日志'
|
||||
@@ -41,14 +41,14 @@ jobs:
|
||||
with:
|
||||
trigger: tag
|
||||
changelogs: 'CHANGELOG.en-US.md, CHANGELOG.zh-CN.md'
|
||||
branch: 'master, 4.x-stable'
|
||||
tag: '5*, 4*'
|
||||
latest: '5*'
|
||||
branch: 'master, 5.x-stable, 4.x-stable'
|
||||
tag: '6*, 5*, 4*'
|
||||
latest: '6*'
|
||||
dingding-token: ${{ secrets.DINGDING_BOT_BIGFISH_TOKEN }} ${{ secrets.DINGDING_BOT_BIGFISH_2_TOKEN }} ${{ secrets.DINGDING_BOT_YUNFENGDIE_TOKEN }}
|
||||
dingding-msg: CHANGELOG.zh-CN.md
|
||||
dingding-delay-minute: 10
|
||||
release: false
|
||||
conch-tag: 'conch-v5, conch'
|
||||
conch-tag: 'conch-v6, conch-v5, conch'
|
||||
antd-conch-msg: 🐟 当前 Bigfish 内嵌 antd 版本:
|
||||
msg-title: '# Ant Design {{v}} 发布日志'
|
||||
msg-poster: 'https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*zx7LTI_ECSAAAAAAAAAAAABkARQnAQ'
|
||||
|
||||
2
.github/workflows/release-x.yml
vendored
2
.github/workflows/release-x.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
|
||||
jobs:
|
||||
tweet:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
if: ${{ github.event.ref_type == 'tag' && !contains(github.event.ref, 'alpha') }}
|
||||
steps:
|
||||
- name: Tweet
|
||||
|
||||
14
.github/workflows/site-deploy.yml
vendored
14
.github/workflows/site-deploy.yml
vendored
@@ -4,7 +4,7 @@ name: Deploy website
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '5.*'
|
||||
- '6.*'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
@@ -57,13 +57,13 @@ jobs:
|
||||
run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/\./-/g')" >> $GITHUB_OUTPUT
|
||||
|
||||
deploy-to-pages:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
needs: build-site
|
||||
steps:
|
||||
- uses: utooland/setup-utoo@v1
|
||||
|
||||
- name: download site artifact
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: real-site
|
||||
path: _site
|
||||
@@ -86,11 +86,13 @@ jobs:
|
||||
destination-repo: 'git@gitee.com:ant-design/ant-design.git'
|
||||
|
||||
- name: Deploy to Surge (with TAG)
|
||||
if: ${{ needs.build-site.outputs.formatted_version != 'master' }}
|
||||
run: |
|
||||
export DEPLOY_DOMAIN=ant-design-${{ needs.build-site.outputs.formatted_version }}.surge.sh
|
||||
utx surge --project ./_site --domain $DEPLOY_DOMAIN --token ${{ secrets.SURGE_TOKEN }}
|
||||
|
||||
- name: Create Commit Comment
|
||||
if: ${{ needs.build-site.outputs.formatted_version != 'master' }}
|
||||
uses: peter-evans/commit-comment@v4
|
||||
with:
|
||||
body: |
|
||||
@@ -99,13 +101,13 @@ jobs:
|
||||
|
||||
# https://github.com/ant-design/ant-design/pull/49213/files#r1625446496
|
||||
upload-to-release:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
# 仅在 tag 的时候工作,因为我们要将内容发布到以 tag 为版本号的 release 里
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
needs: build-site
|
||||
steps:
|
||||
- name: download site artifact
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: real-site
|
||||
path: _site
|
||||
@@ -117,7 +119,7 @@ jobs:
|
||||
cd ..
|
||||
|
||||
- name: Upload to Release
|
||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
fail_on_unmatched_files: true
|
||||
files: website.tar.gz
|
||||
|
||||
6
.github/workflows/size-limit.yml
vendored
6
.github/workflows/size-limit.yml
vendored
@@ -13,16 +13,16 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- name: size-limit
|
||||
uses: ant-design/size-limit-action@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
package_manager: ut
|
||||
package_manager: bun
|
||||
build_script: dist
|
||||
env:
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
|
||||
2
.github/workflows/sync-gitee.yml
vendored
2
.github/workflows/sync-gitee.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
mirror:
|
||||
permissions:
|
||||
contents: none
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
if: github.repository == 'ant-design/ant-design'
|
||||
steps:
|
||||
- name: mirror
|
||||
|
||||
231
.github/workflows/test-v6.yml
vendored
231
.github/workflows/test-v6.yml
vendored
@@ -1,231 +0,0 @@
|
||||
# Origin Source
|
||||
# https://github.com/ant-design/ant-design/blob/79f566b7f8abb1012ef55b0d2793bfdf5595b85d/.github/workflows/test.yml
|
||||
name: ✅ test v6
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [next]
|
||||
pull_request:
|
||||
branches: [next]
|
||||
|
||||
# Cancel prev CI if new commit come
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- run: ut install
|
||||
- run: ut lint
|
||||
|
||||
################################ Test ################################
|
||||
test-react-legacy:
|
||||
name: test-react-legacy
|
||||
strategy:
|
||||
matrix:
|
||||
react: ['18']
|
||||
shard: [1/2, 2/2]
|
||||
env:
|
||||
REACT: ${{ matrix.react }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- run: ut
|
||||
- name: install react 18
|
||||
if: ${{ matrix.react == '18' }}
|
||||
run: ut ut-install-react-18
|
||||
# dom test
|
||||
- name: dom test
|
||||
run: ut test -- --maxWorkers=2 --shard=${{matrix.shard}}
|
||||
|
||||
test-node:
|
||||
name: test-node
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- run: ut install
|
||||
- run: ut run test:node
|
||||
|
||||
test-react-latest:
|
||||
name: test-react-latest
|
||||
strategy:
|
||||
matrix:
|
||||
module: [dom]
|
||||
shard: [1/2, 2/2]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- run: ut install --ignore-scripts
|
||||
|
||||
# dom test
|
||||
- name: dom test
|
||||
run: ut test -- --maxWorkers=2 --shard=${{matrix.shard}} --coverage
|
||||
|
||||
- name: persist coverages
|
||||
run: |
|
||||
mkdir persist-coverage
|
||||
mv coverage/coverage-final.json persist-coverage/react-test-${{matrix.module}}-${{strategy.job-index}}.json
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
name: upload coverages
|
||||
with:
|
||||
name: coverage-artifacts-${{ matrix.module }}-${{ strategy.job-index }}
|
||||
path: persist-coverage/
|
||||
|
||||
test-react-latest-dist:
|
||||
name: test-react-latest-dist
|
||||
strategy:
|
||||
matrix:
|
||||
module: [dist, dist-min]
|
||||
shard: [1/2, 2/2]
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- run: ut
|
||||
|
||||
- name: restore cache from dist
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: dist
|
||||
key: dist-${{ github.sha }}
|
||||
|
||||
- name: dist-min test
|
||||
if: ${{ matrix.module == 'dist-min' }}
|
||||
run: ut test
|
||||
env:
|
||||
LIB_DIR: dist-min
|
||||
|
||||
- name: dist test
|
||||
if: ${{ matrix.module == 'dist' }}
|
||||
run: ut test
|
||||
env:
|
||||
LIB_DIR: dist
|
||||
|
||||
############################ Test Coverage ###########################
|
||||
upload-test-coverage:
|
||||
name: test-coverage
|
||||
runs-on: ubuntu-latest
|
||||
needs: test-react-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
pattern: coverage-artifacts-*
|
||||
merge-multiple: true
|
||||
path: persist-coverage
|
||||
- name: Merge Code Coverage
|
||||
run: |
|
||||
utx nyc merge persist-coverage/ coverage/coverage-final.json
|
||||
utx nyc report --reporter text -t coverage --report-dir coverage
|
||||
rm -rf persist-coverage
|
||||
- name: Upload coverage to codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
# use own token to upload coverage reports
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
########################### Compile & Test ###########################
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- run: ut
|
||||
|
||||
- name: cache lib
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: lib
|
||||
key: lib-${{ github.sha }}
|
||||
|
||||
- name: cache es
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: es
|
||||
key: es-${{ github.sha }}
|
||||
|
||||
- name: compile
|
||||
run: ut compile
|
||||
|
||||
- name: cache dist
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: dist
|
||||
key: dist-${{ github.sha }}
|
||||
|
||||
- name: dist
|
||||
run: ut dist
|
||||
env:
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
CI: 1
|
||||
|
||||
- name: check build files
|
||||
run: ut test:dekko
|
||||
|
||||
# Artifact build files
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/next'
|
||||
with:
|
||||
name: build artifacts
|
||||
path: |
|
||||
dist
|
||||
locale
|
||||
es
|
||||
lib
|
||||
|
||||
- name: zip builds
|
||||
if: github.repository == 'ant-design/ant-design' && github.event_name == 'push' && github.ref == 'refs/heads/next'
|
||||
env:
|
||||
ALI_OSS_AK_ID: ${{ secrets.ALI_OSS_AK_ID }}
|
||||
ALI_OSS_AK_SECRET: ${{ secrets.ALI_OSS_AK_SECRET }}
|
||||
HEAD_SHA: ${{ github.sha }}
|
||||
run: |
|
||||
zip -r oss-artifacts.zip dist locale es lib
|
||||
echo "🤖 Uploading"
|
||||
node scripts/visual-regression/upload.js ./oss-artifacts.zip --ref=$HEAD_SHA
|
||||
|
||||
test-lib-es:
|
||||
name: test lib/es module
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
module: [lib, es]
|
||||
shard: [1/2, 2/2]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- run: ut
|
||||
|
||||
- name: restore cache from ${{ matrix.module }}
|
||||
# lib only run in master branch not in pull request
|
||||
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ matrix.module }}
|
||||
key: ${{ matrix.module }}-${{ github.sha }}
|
||||
|
||||
- name: compile
|
||||
# lib only run in master branch not in pull request
|
||||
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
|
||||
run: ut compile
|
||||
|
||||
- name: test
|
||||
# lib only run in master branch not in pull request
|
||||
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
|
||||
run: ut test -- --maxWorkers=2 --shard=${{matrix.shard}}
|
||||
env:
|
||||
LIB_DIR: ${{ matrix.module }}
|
||||
34
.github/workflows/test.yml
vendored
34
.github/workflows/test.yml
vendored
@@ -18,7 +18,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
@@ -34,24 +34,18 @@ jobs:
|
||||
shard: [1/2, 2/2]
|
||||
env:
|
||||
REACT: ${{ matrix.react }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- run: ut
|
||||
- name: install react 16
|
||||
if: ${{ matrix.react == '16' }}
|
||||
run: ut bun-install-react-16
|
||||
- name: install react 17
|
||||
if: ${{ matrix.react == '17' }}
|
||||
run: ut bun-install-react-17
|
||||
# dom test
|
||||
- name: dom test
|
||||
run: ut test -- --maxWorkers=2 --shard=${{matrix.shard}} --coverage
|
||||
|
||||
test-node:
|
||||
name: test-node
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
@@ -64,7 +58,7 @@ jobs:
|
||||
matrix:
|
||||
module: [dom]
|
||||
shard: [1/2, 2/2]
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
@@ -91,7 +85,7 @@ jobs:
|
||||
matrix:
|
||||
module: [dist, dist-min]
|
||||
shard: [1/2, 2/2]
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
needs: build
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
@@ -99,7 +93,7 @@ jobs:
|
||||
- run: ut
|
||||
|
||||
- name: restore cache from dist
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: dist
|
||||
key: dist-${{ github.sha }}
|
||||
@@ -119,14 +113,14 @@ jobs:
|
||||
############################ Test Coverage ###########################
|
||||
upload-test-coverage:
|
||||
name: test-coverage
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
needs: test-react-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- run: ut
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: coverage-artifacts-*
|
||||
merge-multiple: true
|
||||
@@ -144,20 +138,20 @@ jobs:
|
||||
|
||||
########################### Compile & Test ###########################
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: utooland/setup-utoo@v1
|
||||
- run: ut
|
||||
|
||||
- name: cache lib
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: lib
|
||||
key: lib-${{ github.sha }}
|
||||
|
||||
- name: cache es
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: es
|
||||
key: es-${{ github.sha }}
|
||||
@@ -166,7 +160,7 @@ jobs:
|
||||
run: ut compile
|
||||
|
||||
- name: cache dist
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: dist
|
||||
key: dist-${{ github.sha }}
|
||||
@@ -205,7 +199,7 @@ jobs:
|
||||
|
||||
test-lib-es:
|
||||
name: test lib/es module
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
strategy:
|
||||
matrix:
|
||||
module: [lib, es]
|
||||
@@ -218,7 +212,7 @@ jobs:
|
||||
- name: restore cache from ${{ matrix.module }}
|
||||
# lib only run in master branch not in pull request
|
||||
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ matrix.module }}
|
||||
key: ${{ matrix.module }}-${{ github.sha }}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user