diff --git a/.dumi/theme/layouts/GlobalLayout.tsx b/.dumi/theme/layouts/GlobalLayout.tsx index 6f5b615aae..7c48b0b9df 100644 --- a/.dumi/theme/layouts/GlobalLayout.tsx +++ b/.dumi/theme/layouts/GlobalLayout.tsx @@ -1,5 +1,5 @@ +import { scan } from 'react-scan'; // import this BEFORE react import React, { Suspense, useCallback, useEffect } from 'react'; -import { Monitoring } from 'react-scan/monitoring'; import { createCache, extractStyle, @@ -13,13 +13,7 @@ import { getSandpackCssText } from '@codesandbox/sandpack-react'; import { theme as antdTheme, App } from 'antd'; import type { MappingAlgorithm } from 'antd'; import type { DirectionType, ThemeConfig } from 'antd/es/config-provider'; -import { - createSearchParams, - useOutlet, - useParams, - useSearchParams, - useServerInsertedHTML, -} from 'dumi'; +import { createSearchParams, useOutlet, useSearchParams, useServerInsertedHTML } from 'dumi'; import { DarkContext } from '../../hooks/useDark'; import useLayoutState from '../../hooks/useLayoutState'; @@ -52,6 +46,10 @@ if (typeof window !== 'undefined') { location.hash = `#${hashId.replace(/^components-/, '')}`; } } + scan({ + enabled: process.env.NODE_ENV !== 'production', + log: true, // logs render info to console (default: false) + }); } const getAlgorithm = (themes: ThemeName[] = []) => @@ -70,7 +68,6 @@ const getAlgorithm = (themes: ThemeName[] = []) => const GlobalLayout: React.FC = () => { const outlet = useOutlet(); const { pathname } = useLocation(); - const params = useParams(); const [searchParams, setSearchParams] = useSearchParams(); const [{ theme = [], direction, isMobile, bannerVisible = false }, setSiteState] = useLayoutState({ @@ -229,17 +226,7 @@ const GlobalLayout: React.FC = () => { > - - {content} - } - path={pathname} - /> - + {content} diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 62f11660d0..9cd3e2e0fd 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -15,6 +15,29 @@ tag: vVERSION --- +## 5.24.2 + +`2025-02-24` + +- Input + - 🐞 Fix Input with component token `inputFontSize` breaks the height of `controlHeight`. [#52865](https://github.com/ant-design/ant-design/pull/52865) [@zombieJ](https://github.com/zombieJ) + - 🐞 Fix Input.Search has a border that is not aligned with the bottom of the search button when configure `variable` as `underlined`. [#52861](https://github.com/ant-design/ant-design/pull/52861) [@ustcfury](https://github.com/ustcfury) + - 🛠 Improve Input.OTP logic for create default state. [#52878](https://github.com/ant-design/ant-design/pull/52878) [@Dandelion-F](https://github.com/Dandelion-F) + - 🛠 Improve Input.OTP implementation for render separator. [#52841](https://github.com/ant-design/ant-design/pull/52841) [@li-jia-nan](https://github.com/li-jia-nan) +- Watermark + - 🐞 Fix Watermark may cause page unresponsive when re-rendering. [#52897](https://github.com/ant-design/ant-design/pull/52897) [@765477020](https://github.com/765477020) + - 🆕 Improve Watermark rendering logic to avoid disable it via developer tools and `hidden` attribute. [#52891](https://github.com/ant-design/ant-design/pull/52891) [@arronlai](https://github.com/arronlai) +- 🐞 Fix DatePicker.RangePicker arrow position not correctly when sometime reopened. [#52854](https://github.com/ant-design/ant-design/pull/52854) [@zombieJ](https://github.com/zombieJ) +- 🐞 Fix Layout.Sider content overflow issue when `collapsedWidth={0}`. [#52862](https://github.com/ant-design/ant-design/pull/52862) [@afc163](https://github.com/afc163) +- 🛠 Refactor Grid internal useBreakpoint logic to be same as other component, this will not affect usage. [#52870](https://github.com/ant-design/ant-design/pull/52870) [@zombieJ](https://github.com/zombieJ) +- 💄 Fix Button styles for hyperlink mode. [#52888](https://github.com/ant-design/ant-design/pull/52888) [@DDDDD12138](https://github.com/DDDDD12138) +- 💄 Fix Table sortable column headers could not wrap automatically. [#52899](https://github.com/ant-design/ant-design/pull/52899) [@765477020](https://github.com/765477020) +- ⚡️ Improve Menu re-rendering performance when pass function to `expandIcon` property. [#52863](https://github.com/ant-design/ant-design/pull/52863) [@wanpan11](https://github.com/wanpan11) +- ⚡️ Improve Carousel indicator animation performance. [#52881](https://github.com/ant-design/ant-design/pull/52881) [@li-jia-nan](https://github.com/li-jia-nan) +- RTL + - 💄 Fix DatePicker wrong icon direction for RTL mode. [#52896](https://github.com/ant-design/ant-design/pull/52896) [@li-jia-nan](https://github.com/li-jia-nan) + - 💄 Fix Dropdown wrong arrow direction of multi-level menu for RTL mode. [#52885](https://github.com/ant-design/ant-design/pull/52885) [@yellowryan](https://github.com/yellowryan) + ## 5.24.1 `2025-02-17` diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index eae1865b80..112bb00bc8 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -15,6 +15,29 @@ tag: vVERSION --- +## 5.24.2 + +`2025-02-24` + +- Input + - 🐞 修复 Input 配置 `inputFontSize` component token 时,`controlHeight` 会不生效的问题。[#52865](https://github.com/ant-design/ant-design/pull/52865) [@zombieJ](https://github.com/zombieJ) + - 🐞 修复 Input.Search 在设置 `variant` 为 `underlined` 时下边框与搜索按钮底部没对齐的问题。[#52861](https://github.com/ant-design/ant-design/pull/52861) [@ustcfury](https://github.com/ustcfury) + - 🛠 优化 Input.OTP 的默认状态创建逻辑。[#52878](https://github.com/ant-design/ant-design/pull/52878) [@Dandelion-F](https://github.com/Dandelion-F) + - 🛠 优化 Input.OTP 的分隔符渲染实现。[#52841](https://github.com/ant-design/ant-design/pull/52841) [@li-jia-nan](https://github.com/li-jia-nan) +- Watermark + - 🐞 修复 Watermark 重新渲染时可能导致页面卡死的问题。[#52897](https://github.com/ant-design/ant-design/pull/52897) [@765477020](https://github.com/765477020) + - 🆕 调整 Watermark 渲染逻辑,防止通过开发者工具添加 `hidden` 属性来去掉水印。[#52891](https://github.com/ant-design/ant-design/pull/52891) [@arronlai](https://github.com/arronlai) +- 🐞 修复 DatePicker.RangePicker 在弹层重新打开的时候,有可能出现箭头位置不正确的问题。[#52854](https://github.com/ant-design/ant-design/pull/52854) [@zombieJ](https://github.com/zombieJ) +- 🐞 修复 Layout.Sider 当 `collapsedWidth={0}` 时的内容溢出的问题。[#52862](https://github.com/ant-design/ant-design/pull/52862) [@afc163](https://github.com/afc163) +- 🛠 重构 Grid 内部响应式逻辑以复用其他组件类似的逻辑,该更新不会于使用上有所变化。[#52870](https://github.com/ant-design/ant-design/pull/52870) [@zombieJ](https://github.com/zombieJ) +- 💄 修复 Button 超链接模式的样式。[#52888](https://github.com/ant-design/ant-design/pull/52888) [@DDDDD12138](https://github.com/DDDDD12138) +- 💄 修复 Table 可排序列头不自动换行的问题。[#52899](https://github.com/ant-design/ant-design/pull/52899) [@765477020](https://github.com/765477020) +- ⚡️ 优化 Menu 在 `expandIcon` 属性传入函数时重新渲染的性能。[#52863](https://github.com/ant-design/ant-design/pull/52863) [@wanpan11](https://github.com/wanpan11) +- ⚡️ 优化 Carousel 指示器的动画性能。[#52881](https://github.com/ant-design/ant-design/pull/52881) [@li-jia-nan](https://github.com/li-jia-nan) +- RTL + - 💄 修复 DatePicker 在 RTL 模式下图标方向错误的问题。[#52896](https://github.com/ant-design/ant-design/pull/52896) [@li-jia-nan](https://github.com/li-jia-nan) + - 💄 修复 Dropdown 在 RTL 模式下多级菜单箭头方向错误的问题。[#52885](https://github.com/ant-design/ant-design/pull/52885) [@yellowryan](https://github.com/yellowryan) + ## 5.24.1 `2025-02-17` diff --git a/components/app/index.en-US.md b/components/app/index.en-US.md index 75057b39a0..0920be4441 100644 --- a/components/app/index.en-US.md +++ b/components/app/index.en-US.md @@ -143,4 +143,4 @@ Common props ref:[Common props](/docs/react/common-props) ### CSS Var doesn't work inside `` -Make sure the App `component` is a legit React component string, so when you're turning on CSS variables, there's a container to hold the CSS class name. +Make sure the App `component` is a valid html tag, so when you're turning on CSS variables, there's a container to hold the CSS class name. If not set, it defaults to the `div` tag. If set to `false`, no additional DOM nodes will be created, and no default styles will be provided. diff --git a/components/app/index.zh-CN.md b/components/app/index.zh-CN.md index fa00e8ceb2..476efe74fa 100644 --- a/components/app/index.zh-CN.md +++ b/components/app/index.zh-CN.md @@ -144,4 +144,4 @@ export default () => { ### CSS Var 在 `` 内不起作用 -请确保 App 的 `component` 是一个有效的 React 组件字符串,以便在启用 CSS 变量时,有一个容器来承载 CSS 类名。 +请确保 App 的 `component` 是一个有效的 html 标签名,以便在启用 CSS 变量时有一个容器来承载 CSS 类名。如果不设置,则默认为 `div` 标签,如果设置为 `false`,则不会创建额外的 DOM 节点,也不会提供默认样式。 diff --git a/components/checkbox/Group.tsx b/components/checkbox/Group.tsx index 03da0f90ed..59e4c92998 100644 --- a/components/checkbox/Group.tsx +++ b/components/checkbox/Group.tsx @@ -14,6 +14,7 @@ export interface CheckboxOptionType { label: React.ReactNode; value: T; style?: React.CSSProperties; + className?: string; // 👈 5.25.0+ disabled?: boolean; title?: string; id?: string; @@ -125,7 +126,7 @@ const CheckboxGroup = React.forwardRef( value={option.value} checked={value.includes(option.value)} onChange={option.onChange} - className={`${groupPrefixCls}-item`} + className={classNames(`${groupPrefixCls}-item`, option.className)} style={option.style} title={option.title} id={option.id} @@ -145,6 +146,7 @@ const CheckboxGroup = React.forwardRef( registerValue, cancelValue, }; + const classString = classNames( groupPrefixCls, { diff --git a/components/checkbox/demo/group.tsx b/components/checkbox/demo/group.tsx index fbd36094e9..a8816e3cc0 100644 --- a/components/checkbox/demo/group.tsx +++ b/components/checkbox/demo/group.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Checkbox } from 'antd'; -import type { GetProp } from 'antd'; +import type { CheckboxOptionType, GetProp } from 'antd'; const onChange: GetProp = (checkedValues) => { console.log('checked = ', checkedValues); @@ -8,16 +8,16 @@ const onChange: GetProp = (checkedValues) => const plainOptions = ['Apple', 'Pear', 'Orange']; -const options = [ - { label: 'Apple', value: 'Apple' }, - { label: 'Pear', value: 'Pear' }, - { label: 'Orange', value: 'Orange' }, +const options: CheckboxOptionType[] = [ + { label: 'Apple', value: 'Apple', className: 'label-1' }, + { label: 'Pear', value: 'Pear', className: 'label-2' }, + { label: 'Orange', value: 'Orange', className: 'label-3' }, ]; -const optionsWithDisabled = [ - { label: 'Apple', value: 'Apple' }, - { label: 'Pear', value: 'Pear' }, - { label: 'Orange', value: 'Orange', disabled: false }, +const optionsWithDisabled: CheckboxOptionType[] = [ + { label: 'Apple', value: 'Apple', className: 'label-1' }, + { label: 'Pear', value: 'Pear', className: 'label-2' }, + { label: 'Orange', value: 'Orange', className: 'label-3', disabled: false }, ]; const App: React.FC = () => ( diff --git a/components/checkbox/index.en-US.md b/components/checkbox/index.en-US.md index 136d6f171e..eafabbc12b 100644 --- a/components/checkbox/index.en-US.md +++ b/components/checkbox/index.en-US.md @@ -44,7 +44,7 @@ Common props ref:[Common props](/docs/react/common-props) | onBlur | Called when leaving the component | function() | - | | | onFocus | Called when entering the component | function() | - | | -#### Checkbox Group +#### Checkbox.Group | Property | Description | Type | Default | Version | | --- | --- | --- | --- | --- | @@ -53,6 +53,9 @@ Common props ref:[Common props](/docs/react/common-props) | name | The `name` property of all `input[type="checkbox"]` children | string | - | | | options | Specifies options | string\[] \| number\[] \| Option\[] | \[] | | | value | Used for setting the currently selected value | (string \| number \| boolean)\[] | \[] | | +| title | title of the option | `string` | - | | +| className | className of the option | `string` | - | 5.25.0 | +| style | styles of the option | `React.CSSProperties` | - | | | onChange | The callback function that is triggered when the state changes | (checkedValue: T[]) => void | - | | ##### Option diff --git a/components/checkbox/index.zh-CN.md b/components/checkbox/index.zh-CN.md index 603518ff63..d07b8dd340 100644 --- a/components/checkbox/index.zh-CN.md +++ b/components/checkbox/index.zh-CN.md @@ -45,7 +45,7 @@ demo: | onBlur | 失去焦点时的回调 | function() | - | | | onFocus | 获得焦点时的回调 | function() | - | | -#### Checkbox Group +#### Checkbox.Group | 参数 | 说明 | 类型 | 默认值 | 版本 | | --- | --- | --- | --- | --- | @@ -54,6 +54,9 @@ demo: | name | CheckboxGroup 下所有 `input[type="checkbox"]` 的 `name` 属性 | string | - | | | options | 指定可选项 | string\[] \| number\[] \| Option\[] | \[] | | | value | 指定选中的选项 | (string \| number \| boolean)\[] | \[] | | +| title | 选项的 title | `string` | - | | +| className | 选项的类名 | `string` | - | 5.25.0 | +| style | 选项的样式 | `React.CSSProperties` | - | | | onChange | 变化时的回调函数 | (checkedValue: T[]) => void | - | | ##### Option diff --git a/components/form/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/form/__tests__/__snapshots__/demo-extend.test.ts.snap index c00b3ac5a9..0c38fbc26d 100644 --- a/components/form/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/form/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -5128,7 +5128,7 @@ Array [ type="file" /> diff --git a/components/radio/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/radio/__tests__/__snapshots__/demo-extend.test.ts.snap index 89788a8137..b9d902b261 100644 --- a/components/radio/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/radio/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +z// Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders components/radio/demo/badge.tsx extend context correctly 1`] = `
['options'] = ['Apple', 'Pear', 'Orange']; const options: CheckboxGroupProps['options'] = [ - { label: 'Apple', value: 'Apple' }, - { label: 'Pear', value: 'Pear' }, - { label: 'Orange', value: 'Orange', title: 'Orange' }, + { label: 'Apple', value: 'Apple', className: 'label-1' }, + { label: 'Pear', value: 'Pear', className: 'label-2' }, + { label: 'Orange', value: 'Orange', title: 'Orange', className: 'label-3' }, ]; const optionsWithDisabled: CheckboxGroupProps['options'] = [ - { label: 'Apple', value: 'Apple' }, - { label: 'Pear', value: 'Pear' }, - { label: 'Orange', value: 'Orange', disabled: true }, + { label: 'Apple', value: 'Apple', className: 'label-1' }, + { label: 'Pear', value: 'Pear', className: 'label-2' }, + { label: 'Orange', value: 'Orange', className: 'label-3', disabled: true }, ]; const App: React.FC = () => { diff --git a/components/radio/demo/radiogroup.tsx b/components/radio/demo/radiogroup.tsx index ef6df7e624..612d46d9b3 100644 --- a/components/radio/demo/radiogroup.tsx +++ b/components/radio/demo/radiogroup.tsx @@ -22,6 +22,7 @@ const App: React.FC = () => { options={[ { value: 1, + className: 'option-1', label: ( @@ -31,6 +32,7 @@ const App: React.FC = () => { }, { value: 2, + className: 'option-2', label: ( @@ -40,6 +42,7 @@ const App: React.FC = () => { }, { value: 3, + className: 'option-3', label: ( @@ -49,6 +52,7 @@ const App: React.FC = () => { }, { value: 4, + className: 'option-4', label: ( diff --git a/components/radio/group.tsx b/components/radio/group.tsx index abde4cc373..c17f0917d5 100644 --- a/components/radio/group.tsx +++ b/components/radio/group.tsx @@ -98,6 +98,7 @@ const RadioGroup = React.forwardRef((props, ref checked={value === option.value} title={option.title} style={option.style} + className={option.className} // 👈 5.25.0+ id={option.id} required={option.required} > diff --git a/components/radio/index.en-US.md b/components/radio/index.en-US.md index dd3fae2a53..f61064481c 100644 --- a/components/radio/index.en-US.md +++ b/components/radio/index.en-US.md @@ -70,7 +70,7 @@ Common props ref:[Common props](/docs/react/common-props) | disabled | Disable radio | boolean | false | | value | According to value for comparison, to determine whether the selected | any | - | -### RadioGroup +### Radio.Group Radio group can wrap a group of `Radio`。 @@ -94,6 +94,7 @@ Radio group can wrap a group of `Radio`。 | label | The text used to display as the Radio option | `string` | - | 4.4.0 | | value | The value associated with the Radio option | `string` \| `number` \| `boolean` | - | 4.4.0 | | style | The style to apply to the Radio option | `React.CSSProperties` | - | 4.4.0 | +| className | className of the Radio option | `string` | - | 5.25.0 | | disabled | Specifies whether the Radio option is disabled | `boolean` | `false` | 4.4.0 | | title | Adds the Title attribute value | `string` | - | 4.4.0 | | id | Adds the Radio Id attribute value | `string` | - | 4.4.0 | diff --git a/components/radio/index.zh-CN.md b/components/radio/index.zh-CN.md index 8084ad6ae3..7db2bf2da6 100644 --- a/components/radio/index.zh-CN.md +++ b/components/radio/index.zh-CN.md @@ -92,11 +92,12 @@ return ( ### CheckboxOptionType -| Property | Description | Type | Default | Version | +| 属性 | 说明 | 类型 | 默认值 | 版本 | | --- | --- | --- | --- | --- | | label | 用于作为 Radio 选项展示的文本 | `string` | - | 4.4.0 | | value | 关联 Radio 选项的值 | `string` \| `number` \| `boolean` | - | 4.4.0 | | style | 应用到 Radio 选项的 style | `React.CSSProperties` | - | 4.4.0 | +| className | Radio 选项的类名 | `string` | - | 5.25.0 | | disabled | 指定 Radio 选项是否要禁用 | `boolean` | `false` | 4.4.0 | | title | 添加 Title 属性值 | `string` | - | 4.4.0 | | id | 添加 Radio Id 属性值 | `string` | - | 4.4.0 | diff --git a/components/tabs/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/tabs/__tests__/__snapshots__/demo-extend.test.ts.snap index 07b4b2c06b..cc8b08dc47 100644 --- a/components/tabs/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/tabs/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -776,7 +776,1020 @@ exports[`renders components/tabs/demo/centered.tsx extend context correctly 1`] exports[`renders components/tabs/demo/centered.tsx extend context correctly 2`] = `[]`; exports[`renders components/tabs/demo/component-token.tsx extend context correctly 1`] = ` -
+Array [ +
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
    + +
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
    + +
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
    + +
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
    + +
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
    + +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
    + +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
    + +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
,
Tab 1
+
Tab 2
+
+
@@ -1485,6 +2582,34 @@ exports[`renders components/tabs/demo/component-token.tsx extend context correct style="display: none;" />
+
-
+
,
-
-
-
-
+ class="ant-tabs-ink-bar ant-tabs-ink-bar-animated" + />
-
-
-
- +
+
    + -
    - -
    -
    - -
    -
-
-
- Content of Tab Pane 1 +
+
+ +
+
+ +
+
+
+
+
+ +
+
    + +
+
+
+
+
-
-
+
+
+
+
+
+ + +
+
+ + +
+ +
+
+
+
+ +
+
    + + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
    + +
+
+
+
+
+
+
+
+
, +] `; exports[`renders components/tabs/demo/component-token.tsx extend context correctly 2`] = `[]`; diff --git a/components/tabs/__tests__/__snapshots__/demo.test.ts.snap b/components/tabs/__tests__/__snapshots__/demo.test.ts.snap index 3c4e266e5b..cb04340e74 100644 --- a/components/tabs/__tests__/__snapshots__/demo.test.ts.snap +++ b/components/tabs/__tests__/__snapshots__/demo.test.ts.snap @@ -512,17 +512,17 @@ exports[`renders components/tabs/demo/card-top.tsx correctly 1`] = ` tabindex="0" >

- Content of Tab Pane + Content of Tab Pane 1

- Content of Tab Pane + Content of Tab Pane 1

- Content of Tab Pane + Content of Tab Pane 1

@@ -647,7 +647,845 @@ exports[`renders components/tabs/demo/centered.tsx correctly 1`] = ` `; exports[`renders components/tabs/demo/component-token.tsx correctly 1`] = ` -
+Array [ +
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ Content of Tab Pane 1 +
+
+
+
+
,
Tab 1
+
Tab 2
+
+
@@ -1233,6 +2155,34 @@ exports[`renders components/tabs/demo/component-token.tsx correctly 1`] = ` +
-
+
,
-
-
-
-
+ class="ant-tabs-ink-bar ant-tabs-ink-bar-animated" + />
-
+
+
+
- +
+
- Content of Tab Pane 1 +
+
+ +
+
+ +
+
+
+
+
+
@@ -1373,110 +2395,328 @@ exports[`renders components/tabs/demo/component-token.tsx correctly 1`] = ` role="tablist" >
- -
-
- -
-
- -
-
-
- -
- Content of Tab Pane 1 +
+
+ + +
+
+ + +
+ +
+
+
+
+ + +
+
+
+
+
-
-
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
, +] `; exports[`renders components/tabs/demo/custom-add-trigger.tsx correctly 1`] = ` diff --git a/components/tabs/demo/component-token.tsx b/components/tabs/demo/component-token.tsx index 76d3434a6e..3fa212014e 100644 --- a/components/tabs/demo/component-token.tsx +++ b/components/tabs/demo/component-token.tsx @@ -1,138 +1,117 @@ import React from 'react'; -import { Button, ConfigProvider, Tabs } from 'antd'; +import { Button, ConfigProvider, Flex, Tabs } from 'antd'; + +const tabItems = Array.from({ length: 3 }).map((_, i) => { + const id = String(i + 1); + return { + disabled: i === 2, + label: `Tab ${id}`, + key: id, + children: `Content of Tab Pane ${id}`, + }; +}); + +const sharedTabsProps = { + items: Array.from({ length: 2 }).map((_, i) => { + const id = String(i + 1); + return { + label: `Tab ${id}`, + key: id, + }; + }), + tabBarStyle: { background: 'red' }, +}; const App: React.FC = () => ( - + -
- Extra Action} - style={{ marginBottom: 32 }} - items={Array.from({ length: 3 }).map((_, i) => { - const id = String(i + 1); - return { - label: `Tab ${id}`, - key: id, - children: `Content of tab ${id}`, - }; - })} - /> - Extra Action} - style={{ marginBottom: 32 }} - items={Array.from({ length: 3 }).map((_, i) => { - const id = String(i + 1); - return { - label: `Tab ${id}`, - key: id, - children: `Content of tab ${id}`, - }; - })} - /> - Extra Action} - style={{ marginBottom: 32 }} - items={Array.from({ length: 3 }).map((_, i) => { - const id = String(i + 1); - return { - label: `Tab ${id}`, - key: id, - children: `Content of tab ${id}`, - }; - })} - /> - Extra Action} - style={{ marginBottom: 32 }} - items={Array.from({ length: 3 }).map((_, i) => { - const id = String(i + 1); - return { - label: `Tab ${id}`, - key: id, - children: `Content of tab ${id}`, - }; - })} - /> - { - const id = String(i + 1); - return { - disabled: i === 2, - label: `Tab ${id}`, - key: id, - children: `Content of Tab Pane ${id}`, - }; - })} - /> - { - const id = String(i + 1); - return { - disabled: i === 2, - label: `Tab ${id}`, - key: id, - children: `Content of Tab Pane ${id}`, - }; - })} - /> - { - const id = String(i + 1); - return { - disabled: i === 2, - label: `Tab ${id}`, - key: id, - children: `Content of Tab Pane ${id}`, - }; - })} - /> -
-
+ }} + > +
+ Extra Action} + style={{ marginBottom: 32 }} + items={tabItems} + /> + Extra Action} + style={{ marginBottom: 32 }} + items={tabItems} + /> + Extra Action} + style={{ marginBottom: 32 }} + items={tabItems} + /> + Extra Action} + style={{ marginBottom: 32 }} + items={tabItems} + /> + + + +
+
+ + + + + + + + + + ); export default App; diff --git a/components/tabs/style/index.ts b/components/tabs/style/index.ts index 0f120de1fa..7478d62613 100644 --- a/components/tabs/style/index.ts +++ b/components/tabs/style/index.ts @@ -21,7 +21,17 @@ export interface ComponentToken { * @desc 卡片标签页高度 * @descEN Height of card tab */ - cardHeight: number | string; + cardHeight: number; + /** + * @desc 小尺寸卡片标签页高度 + * @descEN Height of small card tab + */ + cardHeightSM: number; + /** + * @desc 大尺寸卡片标签页高度 + * @descEN Height of large card tab + */ + cardHeightLG: number; /** * @desc 卡片标签页内间距 * @descEN Padding of card tab @@ -593,10 +603,13 @@ const genSizeStyle: GenerateStyle = (token: TabsToken): CSSObject => componentCls, cardPaddingSM, cardPaddingLG, + cardHeightSM, + cardHeightLG, horizontalItemPaddingSM, horizontalItemPaddingLG, } = token; return { + // >>>>> shared [componentCls]: { '&-small': { [`> ${componentCls}-nav`]: { @@ -612,17 +625,24 @@ const genSizeStyle: GenerateStyle = (token: TabsToken): CSSObject => [`${componentCls}-tab`]: { padding: horizontalItemPaddingLG, fontSize: token.titleFontSizeLG, + lineHeight: token.lineHeightLG, }, }, }, }, + // >>>>> card [`${componentCls}-card`]: { + // Small [`&${componentCls}-small`]: { [`> ${componentCls}-nav`]: { [`${componentCls}-tab`]: { padding: cardPaddingSM, }, + [`${componentCls}-nav-add`]: { + minWidth: cardHeightSM, + minHeight: cardHeightSM, + }, }, [`&${componentCls}-bottom`]: { [`> ${componentCls}-nav ${componentCls}-tab`]: { @@ -652,11 +672,16 @@ const genSizeStyle: GenerateStyle = (token: TabsToken): CSSObject => }, }, + // Large [`&${componentCls}-large`]: { [`> ${componentCls}-nav`]: { [`${componentCls}-tab`]: { padding: cardPaddingLG, }, + [`${componentCls}-nav-add`]: { + minWidth: cardHeightLG, + minHeight: cardHeightLG, + }, }, }, }, @@ -952,11 +977,11 @@ const genTabsStyle: GenerateStyle = (token: TabsToken): CSSObject => [`${componentCls}-nav-add`]: { minWidth: cardHeight, + minHeight: cardHeight, marginLeft: { _skip_check_: true, value: cardGutter, }, - padding: unit(token.paddingXS), background: 'transparent', border: `${unit(token.lineWidth)} ${token.lineType} ${colorBorderSecondary}`, borderRadius: `${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)} 0 0`, @@ -1024,18 +1049,31 @@ const genTabsStyle: GenerateStyle = (token: TabsToken): CSSObject => }; export const prepareComponentToken: GetDefaultToken<'Tabs'> = (token) => { - const cardHeight = token.controlHeightLG; + const { cardHeight, cardHeightSM, cardHeightLG, controlHeight, controlHeightLG } = token; + + const mergedCardHeight = cardHeight || controlHeightLG; + const mergedCardHeightSM = cardHeightSM || controlHeight; + // `controlHeight` missing XL variable, so we directly write it here: + const mergedCardHeightLG = cardHeightLG || controlHeightLG + 8; return { zIndexPopup: token.zIndexPopupBase + 50, cardBg: token.colorFillAlter, - cardHeight, + // We can not pass this as valid value, + // Since `cardHeight` will lock nav add button height. + cardHeight: mergedCardHeight, + cardHeightSM: mergedCardHeightSM, + cardHeightLG: mergedCardHeightLG, // Initialize with empty string, because cardPadding will be calculated with cardHeight by default. cardPadding: `${ - (cardHeight - Math.round(token.fontSize * token.lineHeight)) / 2 - token.lineWidth + (mergedCardHeight - token.fontHeight) / 2 - token.lineWidth + }px ${token.padding}px`, + cardPaddingSM: `${ + (mergedCardHeightSM - token.fontHeight) / 2 - token.lineWidth + }px ${token.paddingXS}px`, + cardPaddingLG: `${ + (mergedCardHeightLG - token.fontHeightLG) / 2 - token.lineWidth }px ${token.padding}px`, - cardPaddingSM: `${token.paddingXXS * 1.5}px ${token.padding}px`, - cardPaddingLG: `${token.paddingXS}px ${token.padding}px ${token.paddingXXS * 1.5}px`, titleFontSize: token.fontSize, titleFontSizeLG: token.fontSizeLG, titleFontSizeSM: token.fontSize, diff --git a/components/upload/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/upload/__tests__/__snapshots__/demo-extend.test.ts.snap index 31c79d7cd0..c8b8a0aac8 100644 --- a/components/upload/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/upload/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -853,7 +853,7 @@ exports[`renders components/upload/demo/debug-disabled.tsx extend context correc type="file" /> diff --git a/components/upload/demo/upload-with-aliyun-oss.tsx b/components/upload/demo/upload-with-aliyun-oss.tsx index 67f7006598..0e3cd078cc 100644 --- a/components/upload/demo/upload-with-aliyun-oss.tsx +++ b/components/upload/demo/upload-with-aliyun-oss.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import { UploadOutlined } from '@ant-design/icons'; import type { UploadFile, UploadProps } from 'antd'; -import { Button, Form, message, Upload } from 'antd'; +import { App, Button, Form, Upload } from 'antd'; interface OSSDataType { dir: string; @@ -17,26 +17,33 @@ interface AliyunOSSUploadProps { onChange?: (fileList: UploadFile[]) => void; } -const AliyunOSSUpload = ({ value, onChange }: AliyunOSSUploadProps) => { - const [OSSData, setOSSData] = useState(); - - // Mock get OSS api - // https://help.aliyun.com/document_detail/31988.html - const mockGetOSSData = () => ({ +// Mock get OSS api +// https://help.aliyun.com/document_detail/31988.html +const mockOSSData = () => { + const mockData = { dir: 'user-dir/', expire: '1577811661', host: 'https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload', accessId: 'c2hhb2RhaG9uZw==', policy: 'eGl4aWhhaGFrdWt1ZGFkYQ==', signature: 'ZGFob25nc2hhbw==', - }); + }; + return Promise.resolve(mockData); +}; + +const AliyunOSSUpload: React.FC> = ({ value, onChange }) => { + const { message } = App.useApp(); + + const [OSSData, setOSSData] = useState(); const init = async () => { try { - const result = await mockGetOSSData(); + const result = await mockOSSData(); setOSSData(result); - } catch (error) { - message.error(error as string); + } catch (err) { + if (err instanceof Error) { + message.error(err.message); + } } }; @@ -51,10 +58,7 @@ const AliyunOSSUpload = ({ value, onChange }: AliyunOSSUploadProps) => { const onRemove = (file: UploadFile) => { const files = (value || []).filter((v) => v.url !== file.url); - - if (onChange) { - onChange(files); - } + onChange?.(files); }; const getExtraData: UploadProps['data'] = (file) => ({ @@ -65,7 +69,9 @@ const AliyunOSSUpload = ({ value, onChange }: AliyunOSSUploadProps) => { }); const beforeUpload: UploadProps['beforeUpload'] = async (file) => { - if (!OSSData) return false; + if (!OSSData) { + return false; + } const expire = Number(OSSData.expire) * 1000; @@ -98,7 +104,7 @@ const AliyunOSSUpload = ({ value, onChange }: AliyunOSSUploadProps) => { ); }; -const App: React.FC = () => ( +const Demo: React.FC = () => (
@@ -106,4 +112,4 @@ const App: React.FC = () => ( ); -export default App; +export default Demo; diff --git a/components/watermark/__tests__/index.test.tsx b/components/watermark/__tests__/index.test.tsx index 2d37957fa9..b56b329788 100644 --- a/components/watermark/__tests__/index.test.tsx +++ b/components/watermark/__tests__/index.test.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; import Watermark from '..'; import mountTest from '../../../tests/shared/mountTest'; @@ -89,12 +90,25 @@ describe('Watermark', () => { }); it('MutationObserver should work properly', async () => { + let counter = 0; + const spyCanvas = spyElementPrototypes(HTMLCanvasElement, { + toDataURL(originDescriptor: any) { + counter += 1; + return originDescriptor.value.call(this); + }, + }); const { container } = render(); const target = container.querySelector('.watermark div'); await waitFakeTimer(); + expect(counter).toBe(1); + target?.remove(); await waitFakeTimer(); + expect(counter).toBe(1); + expect(container).toMatchSnapshot(); + + spyCanvas.mockRestore(); }); describe('Observe the modification of style', () => { diff --git a/components/watermark/index.tsx b/components/watermark/index.tsx index a7c0974aaa..df1f1bb879 100644 --- a/components/watermark/index.tsx +++ b/components/watermark/index.tsx @@ -10,6 +10,7 @@ import WatermarkContext from './context'; import type { WatermarkContextProps } from './context'; import useClips, { FontGap } from './useClips'; import useRafDebounce from './useRafDebounce'; +import useSingletonCache from './useSingletonCache'; import useWatermark from './useWatermark'; import { getPixelRatio, reRendering } from './utils'; @@ -165,6 +166,9 @@ const Watermark: React.FC = (props) => { const getClips = useClips(); + type ClipParams = Parameters; + const getClipsCache = useSingletonCache>(); + const [watermarkInfo, setWatermarkInfo] = React.useState<[base64: string, contentWidth: number]>( null!, ); @@ -181,24 +185,19 @@ const Watermark: React.FC = (props) => { const drawCanvas = ( drawContent?: NonNullable | HTMLImageElement, ) => { - const [nextClips, clipWidth] = getClips( + const params: ClipParams = [ drawContent || '', rotate, ratio, markWidth, markHeight, - { - color, - fontSize, - fontStyle, - fontWeight, - fontFamily, - textAlign, - }, + { color, fontSize, fontStyle, fontWeight, fontFamily, textAlign }, gapX, gapY, - ); + ] as const; + const result = getClipsCache(params, () => getClips(...params)); + const [nextClips, clipWidth] = result; setWatermarkInfo([nextClips, clipWidth]); }; diff --git a/components/watermark/useSingletonCache.ts b/components/watermark/useSingletonCache.ts new file mode 100644 index 0000000000..c89e5e58dc --- /dev/null +++ b/components/watermark/useSingletonCache.ts @@ -0,0 +1,25 @@ +import * as React from 'react'; +import isEqual from 'rc-util/lib/isEqual'; + +export type GetCache = (cacheKeys: T, callback: () => R) => R; + +/** + * Singleton cache will only take latest `cacheParams` as key + * and return the result for callback matching. + */ +export default function useSingletonCache(): GetCache { + const cacheRef = React.useRef<[any[] | null, R | null]>([null, null]); + + const getCache: GetCache = (cacheKeys, callback) => { + const filteredKeys = cacheKeys.map((item) => + item instanceof HTMLElement || isNaN(item) ? '' : item, + ); + + if (!isEqual(cacheRef.current[0], filteredKeys)) { + cacheRef.current = [filteredKeys, callback()]; + } + return cacheRef.current[1]!; + }; + + return getCache; +} diff --git a/package.json b/package.json index 5e9cd6659b..bd4057c986 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "antd", - "version": "5.24.1", + "version": "5.24.2", "description": "An enterprise-class UI design language and React components implementation", "license": "MIT", "funding": { @@ -279,7 +279,7 @@ "ora": "^8.1.0", "p-all": "^5.0.0", "package-manager-detector": "^0.2.8", - "pixelmatch": "^6.0.0", + "pixelmatch": "^7.1.0", "pngjs": "^7.0.0", "prettier": "^3.4.1", "pretty-format": "^29.7.0", @@ -301,7 +301,7 @@ "react-intersection-observer": "^9.13.1", "react-resizable": "^3.0.5", "react-router-dom": "^7.0.1", - "react-scan": "^0.1.0", + "react-scan": "^0.1.3", "react-sticky-box": "^2.0.5", "regenerator-runtime": "^0.14.1", "rehype-stringify": "^10.0.1",