chore: auto merge branches (#53361)

chore: merge feature into next
This commit is contained in:
github-actions[bot]
2025-03-31 18:20:53 +00:00
committed by GitHub
55 changed files with 440 additions and 426 deletions

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks-extra/no-direct-set-state-in-use-effect */
import React from 'react';
import { Col, ConfigProvider, Flex, Row, Tag, theme, Typography } from 'antd';
import { Col, ConfigProvider, Flex, Popover, Row, Tag, theme, Typography } from 'antd';
import { createStyles, css } from 'antd-style';
import classnames from 'classnames';
@@ -68,6 +69,7 @@ const useStyle = createStyles(({ token }, markPos: [number, number, number, numb
}));
export interface SemanticPreviewProps {
componentName?: string;
semantics: { name: string; desc: string; version?: string }[];
children: React.ReactElement<any>;
height?: number;
@@ -75,7 +77,7 @@ export interface SemanticPreviewProps {
}
const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
const { semantics = [], children, height, padding } = props;
const { semantics = [], children, height, padding, componentName = 'Component' } = props;
const { token } = theme.useToken();
// ======================= Semantic =======================
@@ -162,24 +164,45 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
<Col span={8}>
<ul className={classnames(styles.listWrap)}>
{semantics.map<React.ReactNode>((semantic) => (
<li
<Popover
key={semantic.name}
className={classnames(styles.listItem)}
onMouseEnter={() => setHoverSemantic(semantic.name)}
onMouseLeave={() => setHoverSemantic(null)}
content={
<Typography style={{ fontSize: 12, minWidth: 300 }}>
<pre dir="ltr">
<code dir="ltr">
{`<${componentName}
classNames={{
${semantic.name}: 'my-${componentName.toLowerCase()}',
}}
styles={{
${semantic.name}: { color: 'red' },
}}
>
...
</${componentName}>`}
</code>
</pre>
</Typography>
}
>
<Flex vertical gap="small">
<Flex gap="small" align="center">
<Typography.Title level={5} style={{ margin: 0 }}>
{semantic.name}
</Typography.Title>
{semantic.version && <Tag color="blue">{semantic.version}</Tag>}
<li
className={classnames(styles.listItem)}
onMouseEnter={() => setHoverSemantic(semantic.name)}
onMouseLeave={() => setHoverSemantic(null)}
>
<Flex vertical gap="small">
<Flex gap="small" align="center">
<Typography.Title level={5} style={{ margin: 0 }}>
{semantic.name}
</Typography.Title>
{semantic.version && <Tag color="blue">{semantic.version}</Tag>}
</Flex>
<Typography.Paragraph style={{ margin: 0, fontSize: token.fontSizeSM }}>
{semantic.desc}
</Typography.Paragraph>
</Flex>
<Typography.Paragraph style={{ margin: 0, fontSize: token.fontSizeSM }}>
{semantic.desc}
</Typography.Paragraph>
</Flex>
</li>
</li>
</Popover>
))}
</ul>
</Col>

View File

@@ -1,7 +1,3 @@
import React from 'react';
export const DarkContext = React.createContext(false);
export default function useDark() {
return React.useContext(DarkContext);
}

View File

@@ -1,4 +1,4 @@
import React, { useContext } from 'react';
import React from 'react';
import { Badge, Carousel, Flex, Skeleton, Typography } from 'antd';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
@@ -110,7 +110,7 @@ const RecommendItem: React.FC<RecommendItemProps> = ({ extra, index, icons, clas
};
export const BannerRecommendsFallback: React.FC = () => {
const { isMobile } = useContext(SiteContext);
const { isMobile } = React.use(SiteContext);
const { styles } = useStyle();
const list = Array.from({ length: 3 });
@@ -137,7 +137,7 @@ export const BannerRecommendsFallback: React.FC = () => {
const BannerRecommends: React.FC = () => {
const { styles } = useStyle();
const [, lang] = useLocale();
const { isMobile } = React.useContext(SiteContext);
const { isMobile } = React.use(SiteContext);
const data = useSiteData();
const extras = data?.extras?.[lang];
const icons = data?.icons || [];

View File

@@ -1,4 +1,4 @@
import React, { useContext } from 'react';
import React from 'react';
import { CustomerServiceOutlined, QuestionCircleOutlined, SyncOutlined } from '@ant-design/icons';
import {
Alert,
@@ -16,9 +16,9 @@ import { createStyles, css } from 'antd-style';
import classNames from 'classnames';
import dayjs from 'dayjs';
import useDark from '../../../hooks/useDark';
import useLocale from '../../../hooks/useLocale';
import SiteContext from '../../../theme/slots/SiteContext';
import { DarkContext } from './../../../hooks/useDark';
import { getCarouselStyle } from './util';
const { _InternalPanelDoNotUseOrYouWillBeFired: ModalDoNotUseOrYouWillBeFired } = Modal;
@@ -61,66 +61,62 @@ const locales = {
},
};
const useStyle = () => {
const isRootDark = useDark();
const useStyle = createStyles(({ token }) => {
const { carousel } = getCarouselStyle();
const isDark = React.use(DarkContext);
return {
card: css`
border-radius: ${token.borderRadius}px;
border: 1px solid ${isDark ? token.colorBorder : 'transparent'};
background-color: ${isDark ? token.colorBgContainer : '#f5f8ff'};
padding: ${token.paddingXL}px;
flex: none;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
align-items: stretch;
return createStyles(({ token }) => {
const { carousel } = getCarouselStyle();
return {
card: css`
border-radius: ${token.borderRadius}px;
border: 1px solid ${isRootDark ? token.colorBorder : 'transparent'};
background: ${isRootDark ? token.colorBgContainer : '#f5f8ff'};
padding: ${token.paddingXL}px;
> * {
flex: none;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
align-items: stretch;
> * {
flex: none;
}
`,
cardCircle: css`
position: absolute;
width: 120px;
height: 120px;
background: #1677ff;
border-radius: 50%;
filter: blur(40px);
opacity: 0.1;
`,
mobileCard: css`
height: 395px;
`,
nodeWrap: css`
margin-top: ${token.paddingLG}px;
flex: auto;
display: flex;
align-items: center;
justify-content: center;
`,
carousel,
componentsList: css`
width: 100%;
overflow: hidden;
`,
mobileComponentsList: css`
margin: 0 ${token.margin}px;
`,
};
})();
};
}
`,
cardCircle: css`
position: absolute;
width: 120px;
height: 120px;
background: #1677ff;
border-radius: 50%;
filter: blur(40px);
opacity: 0.1;
`,
mobileCard: css`
height: 395px;
`,
nodeWrap: css`
margin-top: ${token.paddingLG}px;
flex: auto;
display: flex;
align-items: center;
justify-content: center;
`,
carousel,
componentsList: css`
width: 100%;
overflow: hidden;
`,
mobileComponentsList: css`
margin: 0 ${token.margin}px;
`,
};
});
const ComponentItem: React.FC<ComponentItemProps> = ({ title, node, type, index }) => {
const tagColor = type === 'new' ? 'processing' : 'warning';
const [locale] = useLocale(locales);
const tagText = type === 'new' ? locale.new : locale.update;
const { styles } = useStyle();
const { isMobile } = useContext(SiteContext);
const { isMobile } = React.use(SiteContext);
return (
<div className={classNames(styles.card, isMobile && styles.mobileCard)}>
{/* Decorator */}
@@ -151,7 +147,7 @@ interface ComponentItemProps {
const ComponentsList: React.FC = () => {
const { styles } = useStyle();
const [locale] = useLocale(locales);
const { isMobile } = useContext(SiteContext);
const { isMobile } = React.use(SiteContext);
const COMPONENTS = React.useMemo<Omit<ComponentItemProps, 'index'>[]>(
() => [
{

View File

@@ -1,13 +1,13 @@
import React, { useContext } from 'react';
import React from 'react';
import { Col, Row, Typography } from 'antd';
import { createStyles, useTheme } from 'antd-style';
import { useLocation } from 'dumi';
import useDark from '../../../hooks/useDark';
import useLocale from '../../../hooks/useLocale';
import Link from '../../../theme/common/Link';
import SiteContext from '../../../theme/slots/SiteContext';
import * as utils from '../../../theme/utils';
import { DarkContext } from './../../../hooks/useDark';
const SECONDARY_LIST = [
{
@@ -63,14 +63,13 @@ const locales = {
},
};
const useStyle = () => {
const isRootDark = useDark();
return createStyles(({ token, css }) => ({
const useStyle = createStyles(({ token, css }) => {
const isDark = React.use(DarkContext);
return {
card: css`
padding: ${token.paddingSM}px;
border-radius: ${token.borderRadius * 2}px;
background: ${isRootDark ? 'rgba(0, 0, 0, 0.45)' : token.colorBgElevated};
background: ${isDark ? 'rgba(0, 0, 0, 0.45)' : token.colorBgElevated};
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.03),
0 1px 6px -1px rgba(0, 0, 0, 0.02),
@@ -87,15 +86,15 @@ const useStyle = () => {
display: block;
border-radius: ${token.borderRadius * 2}px;
padding: ${token.paddingMD}px ${token.paddingLG}px;
background: ${isRootDark ? 'rgba(0, 0, 0, 0.25)' : 'rgba(0, 0, 0, 0.02)'};
border: 1px solid ${isRootDark ? 'rgba(255, 255, 255, 0.45)' : 'rgba(0, 0, 0, 0.06)'};
background: ${isDark ? 'rgba(0, 0, 0, 0.25)' : 'rgba(0, 0, 0, 0.02)'};
border: 1px solid ${isDark ? 'rgba(255, 255, 255, 0.45)' : 'rgba(0, 0, 0, 0.06)'};
img {
height: 48px;
}
`,
}))();
};
};
});
const DesignFramework: React.FC = () => {
const [locale] = useLocale(locales);
@@ -103,7 +102,7 @@ const DesignFramework: React.FC = () => {
const { styles } = useStyle();
const { pathname, search } = useLocation();
const isZhCN = utils.isZhCN(pathname);
const { isMobile } = useContext(SiteContext);
const { isMobile } = React.use(SiteContext);
const colSpan = isMobile ? 24 : 8;
const MAINLY_LIST = [

View File

@@ -1,5 +1,4 @@
import * as React from 'react';
import { useContext } from 'react';
import { Typography } from 'antd';
import { createStyles, useTheme } from 'antd-style';
import classNames from 'classnames';
@@ -40,7 +39,7 @@ 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 } = useContext(SiteContext);
const { isMobile } = React.use(SiteContext);
const childNode = (
<>
<div style={{ textAlign: 'center' }}>

View File

@@ -26,101 +26,98 @@ const locales = {
},
};
const useStyle = () => {
const useStyle = createStyles(({ token, css, cx }) => {
const textShadow = `0 0 4px ${token.colorBgContainer}`;
const { isMobile, theme } = use(SiteContext);
const isDark = theme.includes('dark');
return createStyles(({ token, css, cx }) => {
const textShadow = `0 0 4px ${token.colorBgContainer}`;
const mask = cx(css`
position: absolute;
inset: 0;
backdrop-filter: blur(2px);
opacity: 1;
background-color: ${isDark ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.2)'};
transition: all 1s ease;
pointer-events: none;
`);
const mask = cx(css`
position: absolute;
inset: 0;
backdrop-filter: blur(2px);
opacity: 1;
background-color: ${isDark ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.2)'};
transition: all 1s ease;
pointer-events: none;
`);
const block = cx(css`
position: absolute;
inset-inline-end: -60px;
top: -24px;
transition: all 1s cubic-bezier(0.03, 0.98, 0.52, 0.99);
`);
const block = cx(css`
position: absolute;
inset-inline-end: -60px;
top: -24px;
transition: all 1s cubic-bezier(.03,.98,.52,.99);
`);
return {
holder: css`
height: 640px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
perspective: 800px;
/* fix safari bug by removing blur style */
transform: translateZ(1000px);
row-gap: ${token.marginXL}px;
return {
holder: css`
height: 640px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
perspective: 800px;
/* fix safari bug by removing blur style */
transform: translateZ(1000px);
row-gap: ${token.marginXL}px;
&:hover {
&:hover {
.${mask} {
opacity: 0;
}
.${block} {
transform: scale(0.96);
transform: scale(0.96);
}
}
`,
}
`,
mask,
mask,
typography: css`
text-align: center;
position: relative;
z-index: 1;
padding-inline: ${token.paddingXL}px;
text-shadow: ${Array.from({ length: 5 }, () => textShadow).join(', ')};
h1 {
font-family: AliPuHui, ${token.fontFamily} !important;
font-weight: 900 !important;
font-size: ${token.fontSizeHeading2 * 2}px !important;
line-height: ${token.lineHeightHeading2} !important;
}
typography: css`
text-align: center;
position: relative;
z-index: 1;
padding-inline: ${token.paddingXL}px;
text-shadow: ${Array.from({ length: 5 }, () => textShadow).join(', ')};
h1 {
font-family: AliPuHui, ${token.fontFamily} !important;
font-weight: 900 !important;
font-size: ${token.fontSizeHeading2 * 2}px !important;
line-height: ${token.lineHeightHeading2} !important;
}
p {
font-size: ${token.fontSizeLG}px !important;
font-weight: normal !important;
margin-bottom: 0;
}
`,
block,
child: css`
position: relative;
width: 100%;
max-width: 1200px;
margin: 0 auto;
z-index: 1;
`,
btnWrap: css`
margin-bottom: ${token.marginXL}px;
`,
bgImg: css`
position: absolute;
width: 240px;
`,
bgImgTop: css`
top: 0;
inset-inline-start: ${isMobile ? '-120px' : 0};
`,
bgImgBottom: css`
bottom: 120px;
inset-inline-end: ${isMobile ? 0 : '40%'};
`,
};
})();
};
p {
font-size: ${token.fontSizeLG}px !important;
font-weight: normal !important;
margin-bottom: 0;
}
`,
block,
child: css`
position: relative;
width: 100%;
max-width: 1200px;
margin: 0 auto;
z-index: 1;
`,
btnWrap: css`
margin-bottom: ${token.marginXL}px;
`,
bgImg: css`
position: absolute;
width: 240px;
`,
bgImgTop: css`
top: 0;
inset-inline-start: ${isMobile ? '-120px' : 0};
`,
bgImgBottom: css`
bottom: 120px;
inset-inline-end: ${isMobile ? 0 : '40%'};
`,
};
});
const PreviewBanner: React.FC<Readonly<React.PropsWithChildren>> = (props) => {
const { children } = props;

View File

@@ -4,8 +4,6 @@ export interface SiteContextProps {
isMobile: boolean;
}
const SiteContext = React.createContext<SiteContextProps>({
isMobile: false,
});
const SiteContext = React.createContext<SiteContextProps>({ isMobile: false });
export default SiteContext;

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks-extra/no-direct-set-state-in-use-effect */
import * as React from 'react';
import { defaultAlgorithm, defaultTheme } from '@ant-design/compatible';
import { FastColor } from '@ant-design/fast-color';
@@ -25,13 +26,13 @@ import { generateColor } from 'antd/es/color-picker/util';
import classNames from 'classnames';
import { useLocation } from 'dumi';
import useDark from '../../../../hooks/useDark';
import useLocale from '../../../../hooks/useLocale';
import LinkButton from '../../../../theme/common/LinkButton';
import SiteContext from '../../../../theme/slots/SiteContext';
import { getLocalizedPathname } from '../../../../theme/utils';
import Group from '../Group';
import { getCarouselStyle } from '../util';
import { DarkContext } from './../../../../hooks/useDark';
import BackgroundImage from './BackgroundImage';
import ColorPicker from './ColorPicker';
import { DEFAULT_COLOR, getAvatarURL, getClosetColor, PINK_COLOR } from './colorUtil';
@@ -360,7 +361,7 @@ const Theme: React.FC = () => {
const { compact, themeType, colorPrimary, ...themeToken } = themeData;
const isLight = themeType !== 'dark';
const [form] = Form.useForm();
const { isMobile } = React.useContext(SiteContext);
const { isMobile } = React.use(SiteContext);
const colorPrimaryValue = React.useMemo(
() => (typeof colorPrimary === 'string' ? colorPrimary : colorPrimary.toHexString()),
[colorPrimary],
@@ -393,11 +394,11 @@ const Theme: React.FC = () => {
form.setFieldsValue(mergedData);
}, [themeType]);
const isRootDark = useDark();
const isDark = React.use(DarkContext);
React.useEffect(() => {
onThemeChange({}, { ...themeData, themeType: isRootDark ? 'dark' : 'default' });
}, [isRootDark]);
onThemeChange({}, { ...themeData, themeType: isDark ? 'dark' : 'default' });
}, [isDark]);
// ================================ Tokens ================================
const closestColor = getClosetColor(colorPrimaryValue);

View File

@@ -2,8 +2,8 @@ import React, { Suspense } from 'react';
import { ConfigProvider, theme } from 'antd';
import { createStyles, css } from 'antd-style';
import useDark from '../../hooks/useDark';
import useLocale from '../../hooks/useLocale';
import { DarkContext } from './../../hooks/useDark';
import BannerRecommends from './components/BannerRecommends';
import Group from './components/Group';
import PreviewBanner from './components/PreviewBanner';
@@ -41,7 +41,7 @@ const Homepage: React.FC = () => {
const { styles } = useStyle();
const { token } = theme.useToken();
const isRootDark = useDark();
const isDark = React.use(DarkContext);
return (
<section>
@@ -78,7 +78,7 @@ const Homepage: React.FC = () => {
<Group
title={locale.designTitle}
description={locale.designDesc}
background={isRootDark ? '#393F4A' : '#F5F8FF'}
background={isDark ? '#393F4A' : '#F5F8FF'}
decoration={
<img
draggable={false}

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks-extra/no-direct-set-state-in-use-effect */
import React, { Suspense, useEffect } from 'react';
import { Button, App, Skeleton } from 'antd';
import { App, Button, Skeleton } from 'antd';
import { enUS, zhCN } from 'antd-token-previewer';
import type { ThemeConfig } from 'antd/es/config-provider/context';
import { Helmet } from 'dumi';

View File

@@ -1,4 +1,4 @@
import React, { useContext } from 'react';
import React from 'react';
import { theme as antdTheme, ConfigProvider } from 'antd';
import type { ThemeConfig } from 'antd';
import type { ThemeProviderProps } from 'antd-style';
@@ -31,10 +31,10 @@ const headerHeight = 64;
const bannerHeight = 38;
const SiteThemeProvider: React.FC<ThemeProviderProps<any>> = ({ children, theme, ...rest }) => {
const { getPrefixCls, iconPrefixCls } = useContext(ConfigProvider.ConfigContext);
const { getPrefixCls, iconPrefixCls } = React.use(ConfigProvider.ConfigContext);
const rootPrefixCls = getPrefixCls();
const { token } = antdTheme.useToken();
const { bannerVisible } = useContext(SiteContext);
const { bannerVisible } = React.use(SiteContext);
React.useEffect(() => {
// 需要注意与 components/config-provider/demo/holderRender.tsx 配置冲突
ConfigProvider.config({ theme: theme as ThemeConfig });

View File

@@ -1,4 +1,4 @@
import React, { memo, useContext, useMemo, useRef, useState } from 'react';
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';
@@ -83,7 +83,7 @@ const { Title } = Typography;
const Overview: React.FC = () => {
const { styles } = useStyle();
const { theme } = useContext(SiteContext);
const { theme } = React.use(SiteContext);
const data = useSidebarData();
const [searchBarAffixed, setSearchBarAffixed] = useState<boolean>(false);

View File

@@ -1,4 +1,4 @@
import React, { Suspense, useContext } from 'react';
import React, { Suspense } from 'react';
import { BugOutlined, CodeOutlined } from '@ant-design/icons';
import { css, Global } from '@emotion/react';
import { Button, Tooltip } from 'antd';
@@ -9,7 +9,7 @@ import DemoContext from '../../slots/DemoContext';
import DemoFallback from '../Previewer/DemoFallback';
const DemoWrapper: typeof DumiDemoGrid = ({ items }) => {
const { showDebug, setShowDebug } = useContext(DemoContext);
const { showDebug, setShowDebug } = React.use(DemoContext);
const [expandAll, setExpandAll] = useLayoutState(false);

View File

@@ -1,4 +1,5 @@
import React, { useContext, useEffect, useRef, useState } from 'react';
/* eslint-disable react-hooks-extra/no-direct-set-state-in-use-effect */
import React, { useEffect, useRef, useState } from 'react';
import { LinkOutlined, ThunderboltOutlined, UpOutlined } from '@ant-design/icons';
import type { Project } from '@stackblitz/sdk';
import stackblitzSdk from '@stackblitz/sdk';
@@ -17,7 +18,6 @@ import CodePenIcon from '../../icons/CodePenIcon';
import CodeSandboxIcon from '../../icons/CodeSandboxIcon';
import ExternalLinkIcon from '../../icons/ExternalLinkIcon';
import DemoContext from '../../slots/DemoContext';
import type { SiteContextProps } from '../../slots/SiteContext';
import SiteContext from '../../slots/SiteContext';
import CodeBlockButton from './CodeBlockButton';
import type { AntdPreviewerProps } from './Previewer';
@@ -86,7 +86,7 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
clientOnly,
pkgDependencyList,
} = props;
const { codeType } = useContext(DemoContext);
const { codeType } = React.use(DemoContext);
const { pkg } = useSiteData();
const location = useLocation();
@@ -110,7 +110,7 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
const codeSandboxIconRef = useRef<HTMLFormElement>(null);
const codepenIconRef = useRef<HTMLFormElement>(null);
const [codeExpand, setCodeExpand] = useState<boolean>(false);
const { theme } = useContext<SiteContextProps>(SiteContext);
const { theme } = React.use(SiteContext);
const { hash, pathname, search } = location;
const docsOnlineUrl = `https://ant.design${pathname}${search}#${asset.id}`;

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks-extra/no-direct-set-state-in-use-effect */
import type { ComponentProps } from 'react';
import React, { useContext, useEffect, useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import { Button, Tabs, Typography } from 'antd';
import { createStyles } from 'antd-style';
import toReactElement from 'jsonml-to-react-element';
@@ -108,7 +109,7 @@ const CodePreview: React.FC<CodePreviewProps> = ({
initialCodes.style = '';
}
const [highlightedCodes, setHighlightedCodes] = React.useState(initialCodes);
const { codeType, setCodeType } = useContext(DemoContext);
const { codeType, setCodeType } = React.use(DemoContext);
const sourceCodes = {
// omit trailing line break
tsx: sourceCode?.trim(),

View File

@@ -1,5 +1,5 @@
import type { ReactElement } from 'react';
import React, { useContext, useMemo } from 'react';
import React, { useMemo } from 'react';
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
import type { GetProp, MenuProps } from 'antd';
import { createStyles } from 'antd-style';
@@ -7,7 +7,6 @@ import classNames from 'classnames';
import useMenu from '../../hooks/useMenu';
import SiteContext from '../slots/SiteContext';
import type { SiteContextProps } from '../slots/SiteContext';
type MenuItemType = Extract<GetProp<MenuProps, 'items'>[number], { type?: 'item' }>;
@@ -115,7 +114,7 @@ const PrevAndNext: React.FC<{ rtl?: boolean }> = ({ rtl }) => {
const [menuItems, selectedKey] = useMenu({ before, after });
const { isMobile } = useContext<SiteContextProps>(SiteContext);
const { isMobile } = React.use(SiteContext);
const [prev, next] = useMemo(() => {
const flatMenu = flattenMenu(menuItems);

View File

@@ -1,17 +1,19 @@
import React, { use } from 'react';
import { BgColorsOutlined, SmileOutlined, SunOutlined, LinkOutlined } from '@ant-design/icons';
import { Dropdown, Button, Badge } from 'antd';
import { BgColorsOutlined, LinkOutlined, SmileOutlined, SunOutlined } from '@ant-design/icons';
import { Badge, Button, Dropdown } from 'antd';
import type { MenuProps } from 'antd';
import { CompactTheme, DarkTheme } from 'antd-token-previewer/es/icons';
import { FormattedMessage, useLocation } from 'dumi';
import useThemeAnimation from '../../../hooks/useThemeAnimation';
import type { SiteContextProps } from '../../slots/SiteContext';
import SiteContext from '../../slots/SiteContext';
import useThemeAnimation from '../../../hooks/useThemeAnimation';
import { getLocalizedPathname, isZhCN } from '../../utils';
import Link from '../Link';
import ThemeIcon from './ThemeIcon';
export type ThemeName = 'light' | 'dark' | 'compact' | 'motion-off' | 'happy-work';
export interface ThemeSwitchProps {
value?: ThemeName[];
}

View File

@@ -3,7 +3,7 @@ import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import React, { useContext, useEffect, useLayoutEffect, useRef } from 'react';
import React, { useEffect, useLayoutEffect, useRef } from 'react';
import ConfigProvider from 'antd/es/config-provider';
import zhCN from 'antd/es/locale/zh_CN';
import { Helmet, useOutlet, useSiteData } from 'dumi';
@@ -38,7 +38,7 @@ const DocLayout: React.FC = () => {
const { pathname, search, hash } = location;
const [locale, lang] = useLocale(locales);
const timerRef = useRef<ReturnType<typeof setTimeout>>(null!);
const { direction } = useContext(SiteContext);
const { direction } = React.use(SiteContext);
const { loading } = useSiteData();
useLayoutEffect(() => {

View File

@@ -200,20 +200,20 @@ const GlobalLayout: React.FC = () => {
));
return (
<DarkContext.Provider value={theme.includes('dark')}>
<DarkContext value={theme.includes('dark')}>
<StyleProvider
cache={styleCache}
linters={[legacyNotSelectorLinter, parentSelectorLinter, NaNLinter]}
>
<SiteContext.Provider value={siteContextValue}>
<SiteContext value={siteContextValue}>
<SiteThemeProvider theme={themeConfig}>
<HappyProvider disabled={!theme.includes('happy-work')}>
<App>{outlet}</App>
</HappyProvider>
</SiteThemeProvider>
</SiteContext.Provider>
</SiteContext>
</StyleProvider>
</DarkContext.Provider>
</DarkContext>
);
};

View File

@@ -4,10 +4,10 @@ import { ConfigProvider, Layout, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { FormattedMessage, useRouteMeta } from 'dumi';
import useDark from '../../../hooks/useDark';
import CommonHelmet from '../../common/CommonHelmet';
import EditButton from '../../common/EditButton';
import Footer from '../../slots/Footer';
import { DarkContext } from './../../../hooks/useDark';
import AffixTabs from './AffixTabs';
export type ResourceLayoutProps = PropsWithChildren<NonNullable<any>>;
@@ -16,85 +16,77 @@ const resourcePadding = 40;
const articleMaxWidth = 1208;
const resourcePaddingXS = 24;
const useStyle = () => {
const isRootDark = useDark();
return createStyles((config) => {
const { token, css } = config;
const { antCls } = token;
return {
resourcePage: css`
footer {
margin-top: 176px;
.rc-footer-container {
max-width: ${articleMaxWidth}px;
margin: 0 auto;
padding-inline-end: 0;
padding-inline-start: 0;
}
}
`,
resourceContent: css`
padding: 0 ${resourcePadding}px;
max-width: ${articleMaxWidth}px;
margin: 0 auto;
box-sizing: content-box;
min-height: 100vh;
@media only screen and (max-width: 767.99px) {
& {
article {
padding: 0 ${resourcePaddingXS}px;
}
${antCls}-col {
padding-top: ${token.padding}px !important;
padding-bottom: ${token.padding}px !important;
}
}
}
`,
banner: css`
padding: 0 ${resourcePadding}px;
overflow: hidden;
${
isRootDark
? ``
: `background: url('https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*y_r7RogIG1wAAAAAAAAAAABkARQnAQ');`
}
background-size: cover;
h1 {
box-sizing: content-box;
const useStyle = createStyles(({ token, css }) => {
const isDark = React.use(DarkContext);
return {
resourcePage: css`
footer {
margin-top: 176px;
.rc-footer-container {
max-width: ${articleMaxWidth}px;
margin: 56px auto 16px;
margin: 0 auto;
padding-inline-end: 0;
padding-inline-start: 0;
}
}
`,
resourceContent: css`
padding: 0 ${resourcePadding}px;
max-width: ${articleMaxWidth}px;
margin: 0 auto;
box-sizing: content-box;
min-height: 100vh;
section {
max-width: ${articleMaxWidth}px;
margin: 0 auto 56px;
font-weight: 200;
font-size: ${token.fontSizeLG}px;
line-height: 24px;
}
@media only screen and (max-width: 767.99px) {
& {
margin: 0 -${resourcePaddingXS}px;
@media only screen and (max-width: 767.99px) {
& {
article {
padding: 0 ${resourcePaddingXS}px;
}
${token.antCls}-col {
padding-top: ${token.padding}px !important;
padding-bottom: ${token.padding}px !important;
}
}
`,
};
})();
};
}
`,
banner: css`
padding: 0 ${resourcePadding}px;
overflow: hidden;
${
isDark
? ``
: `background: url('https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*y_r7RogIG1wAAAAAAAAAAABkARQnAQ');`
}
background-size: cover;
h1 {
box-sizing: content-box;
max-width: ${articleMaxWidth}px;
margin: 56px auto 16px;
}
section {
max-width: ${articleMaxWidth}px;
margin: 0 auto 56px;
font-weight: 200;
font-size: ${token.fontSizeLG}px;
line-height: 24px;
}
@media only screen and (max-width: 767.99px) {
& {
margin: 0 -${resourcePaddingXS}px;
padding: 0 ${resourcePaddingXS}px;
}
}
`,
};
});
const ResourceLayout: React.FC<ResourceLayoutProps> = ({ children }) => {
const { styles } = useStyle();
const meta = useRouteMeta();
const isRootDark = useDark();
const isDark = React.use(DarkContext);
const node = (
<Layout>
<CommonHelmet />
@@ -116,7 +108,7 @@ const ResourceLayout: React.FC<ResourceLayoutProps> = ({ children }) => {
</Layout>
);
if (!isRootDark) {
if (!isDark) {
return <ConfigProvider theme={{ token: { colorBgLayout: '#fff' } }}>{node}</ConfigProvider>;
}

View File

@@ -1,4 +1,4 @@
import React, { useContext } from 'react';
import React from 'react';
import ContributorsList from '@qixian.cs/github-contributors-list';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
@@ -40,7 +40,7 @@ interface ContributorsProps {
const Contributors: React.FC<ContributorsProps> = ({ filename }) => {
const { formatMessage } = useIntl();
const { styles } = useStyle();
const { isMobile } = useContext(SiteContext);
const { isMobile } = React.use(SiteContext);
if (!filename) {
return null;

View File

@@ -1,5 +1,5 @@
import React, { useContext, useLayoutEffect, useMemo, useState } from 'react';
import { Col, Flex, Space, Typography, Skeleton } from 'antd';
import React, { useLayoutEffect, useMemo, useState } from 'react';
import { Col, Flex, Skeleton, Space, Typography } from 'antd';
import classNames from 'classnames';
import { FormattedMessage, useRouteMeta } from 'dumi';
@@ -9,8 +9,8 @@ import ComponentMeta from '../../builtins/ComponentMeta';
import type { DemoContextProps } from '../DemoContext';
import DemoContext from '../DemoContext';
import SiteContext from '../SiteContext';
import InViewSuspense from './InViewSuspense';
import { useStyle } from './DocAnchor';
import InViewSuspense from './InViewSuspense';
const Contributors = React.lazy(() => import('./Contributors'));
const ColumnCard = React.lazy(() => import('./ColumnCard'));
@@ -28,7 +28,7 @@ const AvatarPlaceholder: React.FC<{ num?: number }> = ({ num = 6 }) =>
const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
const meta = useRouteMeta();
const { pathname, hash } = useLocation();
const { direction } = useContext(SiteContext);
const { direction } = React.use(SiteContext);
const { styles } = useStyle();
const [showDebug, setShowDebug] = useLayoutState(false);
@@ -52,7 +52,7 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
const isRTL = direction === 'rtl';
return (
<DemoContext.Provider value={contextValue}>
<DemoContext value={contextValue}>
<Col xxl={20} xl={19} lg={18} md={18} sm={24} xs={24}>
<InViewSuspense fallback={null}>
<DocAnchor showDebug={showDebug} debugDemos={debugDemos} />
@@ -110,7 +110,7 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
</InViewSuspense>
<Footer />
</Col>
</DemoContext.Provider>
</DemoContext>
);
};

View File

@@ -1,4 +1,5 @@
import React, { useContext } from 'react';
import React from 'react';
import { FastColor } from '@ant-design/fast-color';
import {
AntDesignOutlined,
BgColorsOutlined,
@@ -13,7 +14,6 @@ import {
UsergroupAddOutlined,
ZhihuOutlined,
} from '@ant-design/icons';
import { FastColor } from '@ant-design/fast-color';
import { createStyles } from 'antd-style';
import getAlphaColor from 'antd/es/theme/util/getAlphaColor';
import { FormattedMessage, Link } from 'dumi';
@@ -34,59 +34,52 @@ const locales = {
},
};
const useStyle = () => {
const { isMobile } = useContext(SiteContext);
return createStyles(({ token, css }) => {
const background = new FastColor(getAlphaColor('#f0f3fa', '#fff'))
.onBackground(token.colorBgContainer)
.toHexString();
const useStyle = createStyles(({ token, css }) => {
const { isMobile } = React.use(SiteContext);
const background = new FastColor(getAlphaColor('#f0f3fa', '#fff'))
.onBackground(token.colorBgContainer)
.toHexString();
return {
holder: css`
background: ${background};
`,
return {
holder: css`
background: ${background};
`,
footer: css`
background: ${background};
color: ${token.colorTextSecondary};
box-shadow: inset 0 106px 36px -116px rgba(0, 0, 0, 0.14);
footer: css`
background: ${background};
color: ${token.colorTextSecondary};
* {
box-sizing: border-box;
}
h2,
a {
color: ${token.colorText};
}
.rc-footer-column {
margin-bottom: ${isMobile ? 60 : 0}px;
:last-child {
margin-bottom: ${isMobile ? 20 : 0}px;
}
}
.rc-footer-item-icon {
top: -1.5px;
}
.rc-footer-container {
max-width: 1208px;
margin-inline: auto;
padding-inline: ${token.marginXXL}px;
}
.rc-footer-bottom {
box-shadow: inset 0 106px 36px -116px rgba(0, 0, 0, 0.14);
* {
box-sizing: border-box;
.rc-footer-bottom-container {
font-size: ${token.fontSize}px;
}
h2,
a {
color: ${token.colorText};
}
.rc-footer-column {
margin-bottom: ${isMobile ? 60 : 0}px;
:last-child {
margin-bottom: ${isMobile ? 20 : 0}px;
}
}
.rc-footer-item-icon {
top: -1.5px;
}
.rc-footer-container {
max-width: 1208px;
margin-inline: auto;
padding-inline: ${token.marginXXL}px;
}
.rc-footer-bottom {
box-shadow: inset 0 106px 36px -116px rgba(0, 0, 0, 0.14);
.rc-footer-bottom-container {
font-size: ${token.fontSize}px;
}
}
`,
};
})();
};
}
`,
};
});
const Footer: React.FC = () => {
const location = useLocation();

View File

@@ -1,6 +1,7 @@
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
/* eslint-disable react-hooks-extra/no-direct-set-state-in-use-effect */
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { GithubOutlined, MenuOutlined } from '@ant-design/icons';
import { Alert, Col, ConfigProvider, Popover, Row, Select, Button, Tooltip } from 'antd';
import { Alert, Button, Col, ConfigProvider, Popover, Row, Select, Tooltip } from 'antd';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import dayjs from 'dayjs';
@@ -8,17 +9,16 @@ import { useLocation, useSiteData } from 'dumi';
import DumiSearchBar from 'dumi/theme-default/slots/SearchBar';
import useLocale from '../../../hooks/useLocale';
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 type { SiteContextProps } from '../SiteContext';
import SiteContext from '../SiteContext';
import type { SharedProps } from './interface';
import Logo from './Logo';
import Navigation from './Navigation';
import SwitchBtn from './SwitchBtn';
import ThemeSwitch from '../../common/ThemeSwitch';
const RESPONSIVE_XS = 1120;
const RESPONSIVE_SM = 1200;
@@ -169,8 +169,7 @@ const Header: React.FC = () => {
windowWidth: 1400,
searching: false,
});
const { direction, isMobile, bannerVisible, updateSiteConfig } =
useContext<SiteContextProps>(SiteContext);
const { direction, isMobile, bannerVisible, updateSiteConfig } = React.use(SiteContext);
const pingTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const location = useLocation();
const { pathname, search } = location;
@@ -339,7 +338,7 @@ const Header: React.FC = () => {
target="_blank"
rel="noreferrer"
>
<Tooltip title="GitHub">
<Tooltip title="GitHub" destroyTooltipOnHide>
<Button type="text" icon={<GithubOutlined />} style={{ fontSize: 16 }} />
</Tooltip>
</a>,

View File

@@ -1,4 +1,4 @@
import React, { useContext } from 'react';
import React from 'react';
import { Col, ConfigProvider, Menu } from 'antd';
import { createStyles, useTheme } from 'antd-style';
import { useSidebarData } from 'dumi';
@@ -109,7 +109,7 @@ const useStyle = createStyles(({ token, css }) => {
const Sidebar: React.FC = () => {
const sidebarData = useSidebarData();
const { isMobile, theme } = useContext(SiteContext);
const { isMobile, theme } = React.use(SiteContext);
const { styles } = useStyle();
const [menuItems, selectedKey] = useMenu();

View File

@@ -1,6 +1,7 @@
import { waitFakeTimer } from '../../../tests/utils';
import { isStyleSupport } from '../styleChecker';
import throttleByAnimationFrame from '../throttleByAnimationFrame';
import toList from '../toList';
describe('Test utils function', () => {
describe('throttle', () => {
@@ -56,4 +57,12 @@ describe('Test utils function', () => {
spy.mockRestore();
});
});
describe('toList', () => {
it('toList should work', () => {
expect(toList(123)).toEqual([123]);
expect(toList([123])).toEqual([123]);
expect(toList(null, true)).toEqual([]);
expect(toList(undefined, true)).toEqual([]);
});
});
});

View File

@@ -1,5 +1,8 @@
export default function toList<T>(candidate: T | T[], skipEmpty = false): T[] {
if (skipEmpty && (candidate === undefined || candidate === null)) return [];
const toList = <T>(candidate: T | T[], skipEmpty = false): T[] => {
if (skipEmpty && (candidate === undefined || candidate === null)) {
return [];
}
return Array.isArray(candidate) ? candidate : [candidate];
}
};
export default toList;

View File

@@ -19,6 +19,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Badge"
semantics={[
{ name: 'root', desc: locale.root, version: '5.7.0' },
{ name: 'indicator', desc: locale.indicator, version: '5.7.0' },

View File

@@ -22,6 +22,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Button"
semantics={[
{ name: 'root', desc: locale.root, version: '6.0.0' },
{ name: 'icon', desc: locale.icon, version: '5.5.0' },

View File

@@ -58,6 +58,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Card"
semantics={[
{ name: 'root', desc: locale.root, version: '6.0.0' },
{ name: 'header', desc: locale.header, version: '5.14.0' },

View File

@@ -43,6 +43,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Collapse"
semantics={[
{ name: 'root', desc: locale.root, version: '6.0.0' },
{ name: 'header', desc: locale.header, version: '5.21.0' },

View File

@@ -58,6 +58,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Descriptions"
semantics={[
{ name: 'root', desc: locale.root, version: '5.23.0' },
{ name: 'header', desc: locale.header, version: '5.23.0' },

View File

@@ -31,6 +31,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Drawer"
semantics={[
{ name: 'root', desc: locale.root, version: '6.0.0' },
{ name: 'mask', desc: locale.mask, version: '5.13.0' },

View File

@@ -23,6 +23,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Empty"
semantics={[
{ name: 'root', desc: locale.root, version: '5.23.0' },
{ name: 'image', desc: locale.image, version: '5.23.0' },

View File

@@ -24,6 +24,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Input"
semantics={[
{ name: 'input', desc: locale.input, version: '5.4.0' },
{ name: 'prefix', desc: locale.prefix, version: '5.4.0' },

View File

@@ -19,6 +19,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="TextArea"
semantics={[
{ name: 'textarea', desc: locale.textarea, version: '5.4.0' },
{ name: 'count', desc: locale.count, version: '5.4.0' },

View File

@@ -77,6 +77,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="List"
height={300}
semantics={[
{ name: 'extra', desc: locale.extra, version: '5.18.0' },

View File

@@ -44,7 +44,7 @@ Common props ref[Common props](/docs/react/common-props)
| loadMore | Shows a load more content | ReactNode | - | |
| locale | The i18n text including empty text | object | {emptyText: `No Data`} | |
| pagination | Pagination [config](/components/pagination/), hide it by setting it to false | boolean \| object | false | |
| renderItem | Customize list item when using `dataSource` | (item) => ReactNode | - | |
| renderItem | Customize list item when using `dataSource` | (item: T, index: number) => ReactNode | - | |
| rowKey | Item's unique value, could be an Item's key which holds a unique value of type `React.Key` or function that receives Item and returns a `React.Key` | `keyof` T \| (item: T) => `React.Key` | `"key"` | |
| size | Size of list | `default` \| `large` \| `small` | `default` | |
| split | Toggles rendering of the split under the list item | boolean | true | |

View File

@@ -47,7 +47,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*tBzwQ7raKX8AAA
| loadMore | 加载更多 | ReactNode | - | |
| locale | 默认文案设置,目前包括空数据文案 | object | {emptyText: `暂无数据`} | |
| pagination | 对应的 `pagination` 配置,设置 false 不显示 | boolean \| object | false | |
| renderItem | 当使用 dataSource 时,可以用 `renderItem` 自定义渲染列表项 | (item) => ReactNode | - | |
| renderItem | 当使用 dataSource 时,可以用 `renderItem` 自定义渲染列表项 | (item: T, index: number) => ReactNode | - | |
| rowKey | 当 `renderItem` 自定义渲染列表项有效时,自定义每一行的 `key` 的获取方式 | `keyof` T \| (item: T) => `React.Key` | `"key"` | |
| size | list 的尺寸 | `default` \| `large` \| `small` | `default` | |
| split | 是否展示分割线 | boolean | true | |

View File

@@ -61,6 +61,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Modal"
semantics={[
{ name: 'root', desc: locale.root, version: '6.0.0' },
{ name: 'mask', desc: locale.mask, version: '5.13.0' },

View File

@@ -36,6 +36,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Popconfirm"
semantics={[
{ name: 'root', desc: locale.root, version: '5.23.0' },
{ name: 'body', desc: locale.body, version: '5.23.0' },

View File

@@ -36,6 +36,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Popover"
semantics={[
{ name: 'root', desc: locale.root, version: '5.23.0' },
{ name: 'body', desc: locale.body, version: '5.23.0' },

View File

@@ -25,6 +25,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Slider"
semantics={[
{ name: 'root', desc: locale.root, version: '5.23.0' },
{ name: 'track', desc: locale.track, version: '5.10.0' },

View File

@@ -19,6 +19,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Space"
semantics={[
{ name: 'root', desc: locale.root, version: '6.0.0' },
{ name: 'item', desc: locale.item, version: '5.6.0' },

View File

@@ -313,35 +313,25 @@ describe('Theme', () => {
});
it('component token should support algorithm', () => {
const Demo = ({ algorithm }: { algorithm?: boolean | typeof theme.darkAlgorithm }) => (
<ConfigProvider
theme={{
components: {
Input: {
colorPrimary: '#00B96B',
algorithm,
},
},
}}
>
<Input />
</ConfigProvider>
);
const Demo: React.FC<{ algorithm?: boolean | typeof theme.darkAlgorithm }> = (props) => {
const { algorithm } = props;
return (
<ConfigProvider theme={{ components: { Input: { colorPrimary: '#00B96B', algorithm } } }}>
<Input />
</ConfigProvider>
);
};
const { container, rerender } = render(<Demo />);
expect(container.querySelector('input')).toHaveStyle({
'--ant-input-hover-border-color': '#4096ff',
});
const inputElement = container.querySelector<HTMLInputElement>('input');
expect(inputElement).toHaveStyle({ '--ant-input-hover-border-color': '#4096ff' });
rerender(<Demo algorithm />);
expect(container.querySelector('input')).toHaveStyle({
'--ant-input-hover-border-color': '#20c77c',
});
expect(inputElement).toHaveStyle({ '--ant-input-hover-border-color': '#20c77c' });
rerender(<Demo algorithm={theme.darkAlgorithm} />);
expect(container.querySelector('input')).toHaveStyle({
'--ant-input-hover-border-color': '#1fb572',
});
expect(inputElement).toHaveStyle({ '--ant-input-hover-border-color': '#1fb572' });
});
it('get cssVar from useToken', () => {

View File

@@ -36,6 +36,7 @@ const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Tooltip"
semantics={[
{ name: 'root', desc: locale.root, version: '5.23.0' },
{ name: 'body', desc: locale.body, version: '5.23.0' },

View File

@@ -84,7 +84,7 @@ export interface TransferListProps<RecordType> extends TransferLocale {
export interface TransferCustomListBodyProps<T> extends TransferListBodyProps<T> {}
const useShowSearchOption = (showSearch: boolean | TransferSearchOption) => {
const getShowSearchOption = (showSearch: boolean | TransferSearchOption) => {
if (showSearch && typeof showSearch === 'object') {
return {
...showSearch,
@@ -133,7 +133,7 @@ const TransferList = <RecordType extends KeyWiseTransferItem>(
filterOption,
render = defaultRender,
} = props;
const searchOptions = useShowSearchOption(showSearch);
const searchOptions = getShowSearchOption(showSearch);
const [filterValue, setFilterValue] = useState<string>(searchOptions.defaultValue);
const listBodyRef = useRef<ListBodyRef<RecordType>>({});

View File

@@ -196,8 +196,8 @@ const Watermark: React.FC<WatermarkProps> = (props) => {
gapY,
] as const;
const result = getClipsCache(params, () => getClips(...params));
const [nextClips, clipWidth] = result;
const [nextClips, clipWidth] = getClipsCache(params, () => getClips(...params));
setWatermarkInfo([nextClips, clipWidth]);
};

View File

@@ -1,9 +1,11 @@
import React from 'react';
import type { WatermarkProps } from '.';
import toList from '../_util/toList';
export const FontGap = 3;
function prepareCanvas(
const prepareCanvas = (
width: number,
height: number,
ratio = 1,
@@ -12,26 +14,31 @@ function prepareCanvas(
canvas: HTMLCanvasElement,
realWidth: number,
realHeight: number,
] {
] => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')!;
const realWidth = width * ratio;
const realHeight = height * ratio;
canvas.setAttribute('width', `${realWidth}px`);
canvas.setAttribute('height', `${realHeight}px`);
ctx.save();
return [ctx, canvas, realWidth, realHeight];
}
};
// Get boundary of rotated text
const getRotatePos = (x: number, y: number, angle: number) => {
const targetX = x * Math.cos(angle) - y * Math.sin(angle);
const targetY = x * Math.sin(angle) + y * Math.cos(angle);
return [targetX, targetY] as const;
};
/**
* Get the clips of text content.
* This is a lazy hook function since SSR no need this
*/
export default function useClips() {
const useClips = () => {
// Get single clips
function getClips(
const getClips = (
content: NonNullable<WatermarkProps['content']> | HTMLImageElement,
rotate: number,
ratio: number,
@@ -40,7 +47,7 @@ export default function useClips() {
font: Required<NonNullable<WatermarkProps['font']>>,
gapX: number,
gapY: number,
): [dataURL: string, finalWidth: number, finalHeight: number] {
): [dataURL: string, finalWidth: number, finalHeight: number] => {
// ================= Text / Image =================
const [ctx, canvas, contentWidth, contentHeight] = prepareCanvas(width, height, ratio);
@@ -74,13 +81,6 @@ export default function useClips() {
rCtx.drawImage(canvas, -contentWidth / 2, -contentHeight / 2);
}
// Get boundary of rotated text
function getRotatePos(x: number, y: number) {
const targetX = x * Math.cos(angle) - y * Math.sin(angle);
const targetY = x * Math.sin(angle) + y * Math.cos(angle);
return [targetX, targetY];
}
let left = 0;
let right = 0;
let top = 0;
@@ -95,7 +95,7 @@ export default function useClips() {
[0 - halfWidth, 0 + halfHeight],
];
points.forEach(([x, y]) => {
const [targetX, targetY] = getRotatePos(x, y);
const [targetX, targetY] = getRotatePos(x, y, angle);
left = Math.min(left, targetX);
right = Math.max(right, targetX);
top = Math.min(top, targetY);
@@ -115,7 +115,7 @@ export default function useClips() {
const [fCtx, fCanvas] = prepareCanvas(filledWidth, filledHeight);
function drawImg(targetX = 0, targetY = 0) {
const drawImg = (targetX = 0, targetY = 0) => {
fCtx.drawImage(
rCanvas,
cutLeft,
@@ -127,13 +127,15 @@ export default function useClips() {
cutWidth,
cutHeight,
);
}
};
drawImg();
drawImg(cutWidth + realGapX, -cutHeight / 2 - realGapY / 2);
drawImg(cutWidth + realGapX, +cutHeight / 2 + realGapY / 2);
return [fCanvas.toDataURL(), filledWidth / ratio, filledHeight / ratio];
}
};
return getClips;
}
return React.useCallback(getClips, []);
};
export default useClips;

View File

@@ -12,7 +12,7 @@ export default function useSingletonCache<T extends any[], R>(): GetCache<T, R>
const getCache: GetCache<T, R> = (cacheKeys, callback) => {
const filteredKeys = cacheKeys.map((item) =>
item instanceof HTMLElement || isNaN(item) ? '' : item,
item instanceof HTMLElement || Number.isNaN(item) ? '' : item,
);
if (!isEqual(cacheRef.current[0], filteredKeys)) {

View File

@@ -66,20 +66,18 @@ import React from 'react';
import { ConfigProvider } from 'antd';
import { createStyles } from 'antd-style';
const useButtonStyle = () => {
const { getPrefixCls } = React.useContext(ConfigProvider.ConfigContext);
const useButtonStyle = createStyles(({ css }) => {
const { getPrefixCls } = React.use(ConfigProvider.ConfigContext);
const btnPrefixCls = getPrefixCls('btn');
// Customize styles
return createStyles(({ css }) => ({
return {
btn: css`
background: red;
.${btnPrefixCls}-icon {
color: green;
}
`,
}))();
};
};
});
const GeekProvider: React.FC<Readonly<React.PropsWithChildren>> = (props) => {
const { styles } = useButtonStyle();

View File

@@ -66,20 +66,18 @@ import React from 'react';
import { ConfigProvider } from 'antd';
import { createStyles } from 'antd-style';
const useButtonStyle = () => {
const { getPrefixCls } = React.useContext(ConfigProvider.ConfigContext);
const useButtonStyle = createStyles(({ css }) => {
const { getPrefixCls } = React.use(ConfigProvider.ConfigContext);
const btnPrefixCls = getPrefixCls('btn');
// Customize styles
return createStyles(({ css }) => ({
return {
btn: css`
background: red;
.${btnPrefixCls}-icon {
color: green;
}
`,
}))();
};
};
});
const GeekProvider: React.FC<Readonly<React.PropsWithChildren>> = (props) => {
const { styles } = useButtonStyle();

View File

@@ -64,6 +64,7 @@ export default antfu(
/* turn off React 19 only rules */
'react/no-forward-ref': 'off',
'react/no-context-provider': 'off',
'react/no-use-context': 'off',
},
},
compat.configs['flat/recommended'],
@@ -125,6 +126,7 @@ export default antfu(
'react/no-array-index-key': 'off',
'react-dom/no-missing-iframe-sandbox': 'off',
'no-restricted-globals': 'off',
'react/no-use-context': 'warn',
},
settings: {
polyfills: ['Promise', 'URL', 'URLSearchParams'],

View File

@@ -240,7 +240,7 @@
"dumi": "~2.4.17",
"dumi-plugin-color-chunk": "^2.1.0",
"env-paths": "^3.0.0",
"eslint": "^9.15.0",
"eslint": "^9.23.0",
"eslint-plugin-compat": "^6.0.1",
"eslint-plugin-jest": "^28.9.0",
"eslint-plugin-jsx-a11y": "^6.10.0",
@@ -289,10 +289,10 @@
"rc-footer": "^0.6.8",
"rc-tween-one": "^3.0.6",
"rc-virtual-list": "^3.17.0",
"react": "^19.0.0",
"react": "^19.1.0",
"react-copy-to-clipboard": "^5.1.0",
"react-countup": "^6.5.3",
"react-dom": "^19.0.0",
"react-dom": "^19.1.0",
"react-draggable": "^4.4.6",
"react-fast-marquee": "^1.6.5",
"react-highlight-words": "^0.21.0",
@@ -350,17 +350,17 @@
"tnpm": {
"mode": "npm"
},
"pnpm":{
"pnpm": {
"overrides": {
"nwsapi":"2.2.19"
"nwsapi": "2.2.20"
}
},
"overrides": {
"@ant-design/cssinjs": "^2.0.0-alpha.5",
"@ant-design/cssinjs-utils": "^2.0.0-alpha.1",
"nwsapi": "2.2.19"
"nwsapi": "2.2.20"
},
"resolutions": {
"nwsapi": "2.2.19"
"nwsapi": "2.2.20"
}
}