mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-09 02:49:18 +08:00
chore: sync feature into next
This commit is contained in:
@@ -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<SiteState>({
|
||||
@@ -229,17 +226,7 @@ const GlobalLayout: React.FC = () => {
|
||||
>
|
||||
<SiteContext.Provider value={siteContextValue}>
|
||||
<SiteThemeProvider theme={themeConfig}>
|
||||
<HappyProvider disabled={!theme.includes('happy-work')}>
|
||||
{content}
|
||||
<Monitoring
|
||||
apiKey="GhrCCNrHZHXlf4P6E03ntrFwhRLxJL30" // Safe to expose publically
|
||||
url="https://monitoring.react-scan.com/api/v1/ingest"
|
||||
commit={process.env.COMMIT_HASH}
|
||||
branch={process.env.BRANCH}
|
||||
params={params as Record<string, string>}
|
||||
path={pathname}
|
||||
/>
|
||||
</HappyProvider>
|
||||
<HappyProvider disabled={!theme.includes('happy-work')}>{content}</HappyProvider>
|
||||
</SiteThemeProvider>
|
||||
</SiteContext.Provider>
|
||||
</StyleProvider>
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -143,4 +143,4 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
|
||||
### CSS Var doesn't work inside `<App component={false}>`
|
||||
|
||||
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.
|
||||
|
||||
@@ -144,4 +144,4 @@ export default () => {
|
||||
|
||||
### CSS Var 在 `<App component={false}>` 内不起作用
|
||||
|
||||
请确保 App 的 `component` 是一个有效的 React 组件字符串,以便在启用 CSS 变量时,有一个容器来承载 CSS 类名。
|
||||
请确保 App 的 `component` 是一个有效的 html 标签名,以便在启用 CSS 变量时有一个容器来承载 CSS 类名。如果不设置,则默认为 `div` 标签,如果设置为 `false`,则不会创建额外的 DOM 节点,也不会提供默认样式。
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface CheckboxOptionType<T = any> {
|
||||
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,
|
||||
{
|
||||
|
||||
@@ -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<typeof Checkbox.Group, 'onChange'> = (checkedValues) => {
|
||||
console.log('checked = ', checkedValues);
|
||||
@@ -8,16 +8,16 @@ const onChange: GetProp<typeof Checkbox.Group, 'onChange'> = (checkedValues) =>
|
||||
|
||||
const plainOptions = ['Apple', 'Pear', 'Orange'];
|
||||
|
||||
const options = [
|
||||
{ label: 'Apple', value: 'Apple' },
|
||||
{ label: 'Pear', value: 'Pear' },
|
||||
{ label: 'Orange', value: 'Orange' },
|
||||
const options: CheckboxOptionType<string>[] = [
|
||||
{ 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<string>[] = [
|
||||
{ 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 = () => (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5128,7 +5128,7 @@ Array [
|
||||
type="file"
|
||||
/>
|
||||
<button
|
||||
style="border: 0px; background: none;"
|
||||
style="cursor: inherit; border: 0px; background: none;"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
||||
@@ -2648,7 +2648,7 @@ Array [
|
||||
type="file"
|
||||
/>
|
||||
<button
|
||||
style="border:0;background:none"
|
||||
style="color:inherit;cursor:inherit;border:0;background:none"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
||||
@@ -103,7 +103,10 @@ const FormDisabledDemo: React.FC = () => {
|
||||
</Form.Item>
|
||||
<Form.Item label="Upload" valuePropName="fileList" getValueFromEvent={normFile}>
|
||||
<Upload action="/upload.do" listType="picture-card">
|
||||
<button style={{ border: 0, background: 'none' }} type="button">
|
||||
<button
|
||||
style={{ color: 'inherit', cursor: 'inherit', border: 0, background: 'none' }}
|
||||
type="button"
|
||||
>
|
||||
<PlusOutlined />
|
||||
<div style={{ marginTop: 8 }}>Upload</div>
|
||||
</button>
|
||||
|
||||
@@ -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`] = `
|
||||
<div
|
||||
|
||||
@@ -6,15 +6,15 @@ import type { CheckboxGroupProps } from 'antd/es/checkbox';
|
||||
const plainOptions: CheckboxGroupProps<string>['options'] = ['Apple', 'Pear', 'Orange'];
|
||||
|
||||
const options: CheckboxGroupProps<string>['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<string>['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 = () => {
|
||||
|
||||
@@ -22,6 +22,7 @@ const App: React.FC = () => {
|
||||
options={[
|
||||
{
|
||||
value: 1,
|
||||
className: 'option-1',
|
||||
label: (
|
||||
<Flex gap="small" justify="center" align="center" vertical>
|
||||
<LineChartOutlined style={{ fontSize: 18 }} />
|
||||
@@ -31,6 +32,7 @@ const App: React.FC = () => {
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
className: 'option-2',
|
||||
label: (
|
||||
<Flex gap="small" justify="center" align="center" vertical>
|
||||
<DotChartOutlined style={{ fontSize: 18 }} />
|
||||
@@ -40,6 +42,7 @@ const App: React.FC = () => {
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
className: 'option-3',
|
||||
label: (
|
||||
<Flex gap="small" justify="center" align="center" vertical>
|
||||
<BarChartOutlined style={{ fontSize: 18 }} />
|
||||
@@ -49,6 +52,7 @@ const App: React.FC = () => {
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
className: 'option-4',
|
||||
label: (
|
||||
<Flex gap="small" justify="center" align="center" vertical>
|
||||
<PieChartOutlined style={{ fontSize: 18 }} />
|
||||
|
||||
@@ -98,6 +98,7 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
|
||||
checked={value === option.value}
|
||||
title={option.title}
|
||||
style={option.style}
|
||||
className={option.className} // 👈 5.25.0+
|
||||
id={option.id}
|
||||
required={option.required}
|
||||
>
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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 = () => (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Tabs: {
|
||||
cardBg: '#f6ffed',
|
||||
cardHeight: 60,
|
||||
cardPadding: `20px`,
|
||||
cardPaddingSM: `20px`,
|
||||
cardPaddingLG: `20px`,
|
||||
titleFontSize: 20,
|
||||
titleFontSizeLG: 20,
|
||||
titleFontSizeSM: 20,
|
||||
inkBarColor: '#52C41A',
|
||||
horizontalMargin: `0 0 12px 0`,
|
||||
horizontalItemGutter: 12, // Fixed Value
|
||||
horizontalItemPadding: `20px`,
|
||||
horizontalItemPaddingSM: `20px`,
|
||||
horizontalItemPaddingLG: `20px`,
|
||||
verticalItemPadding: `8px`,
|
||||
verticalItemMargin: `4px 0 0 0`,
|
||||
itemColor: 'rgba(0,0,0,0.85)',
|
||||
itemSelectedColor: '#389e0d',
|
||||
itemHoverColor: '#d9f7be',
|
||||
itemActiveColor: '#b7eb8f',
|
||||
cardGutter: 12,
|
||||
<>
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Tabs: {
|
||||
cardBg: '#f6ffed',
|
||||
cardHeight: 60,
|
||||
cardPadding: `20px`,
|
||||
cardPaddingSM: `20px`,
|
||||
cardPaddingLG: `20px`,
|
||||
titleFontSize: 20,
|
||||
titleFontSizeLG: 20,
|
||||
titleFontSizeSM: 20,
|
||||
inkBarColor: '#52C41A',
|
||||
horizontalMargin: `0 0 12px 0`,
|
||||
horizontalItemGutter: 12, // Fixed Value
|
||||
horizontalItemPadding: `20px`,
|
||||
horizontalItemPaddingSM: `20px`,
|
||||
horizontalItemPaddingLG: `20px`,
|
||||
verticalItemPadding: `8px`,
|
||||
verticalItemMargin: `4px 0 0 0`,
|
||||
itemColor: 'rgba(0,0,0,0.85)',
|
||||
itemSelectedColor: '#389e0d',
|
||||
itemHoverColor: '#d9f7be',
|
||||
itemActiveColor: '#b7eb8f',
|
||||
cardGutter: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
tabBarExtraContent={<Button>Extra Action</Button>}
|
||||
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}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<Tabs
|
||||
tabPosition="left"
|
||||
defaultActiveKey="1"
|
||||
tabBarExtraContent={<Button>Extra Action</Button>}
|
||||
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}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<Tabs
|
||||
size="small"
|
||||
defaultActiveKey="1"
|
||||
tabBarExtraContent={<Button>Extra Action</Button>}
|
||||
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}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<Tabs
|
||||
size="large"
|
||||
defaultActiveKey="1"
|
||||
tabBarExtraContent={<Button>Extra Action</Button>}
|
||||
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}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
centered
|
||||
type="card"
|
||||
items={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}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<Tabs
|
||||
size="small"
|
||||
defaultActiveKey="1"
|
||||
centered
|
||||
type="card"
|
||||
items={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}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<Tabs
|
||||
size="large"
|
||||
defaultActiveKey="1"
|
||||
centered
|
||||
type="card"
|
||||
items={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}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</ConfigProvider>
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
tabBarExtraContent={<Button>Extra Action</Button>}
|
||||
style={{ marginBottom: 32 }}
|
||||
items={tabItems}
|
||||
/>
|
||||
<Tabs
|
||||
tabPosition="left"
|
||||
defaultActiveKey="1"
|
||||
tabBarExtraContent={<Button>Extra Action</Button>}
|
||||
style={{ marginBottom: 32 }}
|
||||
items={tabItems}
|
||||
/>
|
||||
<Tabs
|
||||
size="small"
|
||||
defaultActiveKey="1"
|
||||
tabBarExtraContent={<Button>Extra Action</Button>}
|
||||
style={{ marginBottom: 32 }}
|
||||
items={tabItems}
|
||||
/>
|
||||
<Tabs
|
||||
size="large"
|
||||
defaultActiveKey="1"
|
||||
tabBarExtraContent={<Button>Extra Action</Button>}
|
||||
style={{ marginBottom: 32 }}
|
||||
items={tabItems}
|
||||
/>
|
||||
<Tabs defaultActiveKey="1" centered type="card" items={tabItems} />
|
||||
<Tabs size="small" defaultActiveKey="1" centered type="card" items={tabItems} />
|
||||
<Tabs size="large" defaultActiveKey="1" centered type="card" items={tabItems} />
|
||||
</div>
|
||||
</ConfigProvider>
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Tabs: {
|
||||
cardHeight: 180,
|
||||
cardPadding: '0px 0px 0px 0px',
|
||||
cardPaddingSM: '0px 0px 0px 0px',
|
||||
verticalItemPadding: '0px 0px',
|
||||
borderRadiusLG: 0,
|
||||
borderRadius: 0,
|
||||
horizontalItemPadding: '0px 0px 0px 0px',
|
||||
horizontalMargin: '0 0 0 0',
|
||||
inkBarColor: '#ffa940',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tabs size="small" type="editable-card" items={tabItems} />
|
||||
</ConfigProvider>
|
||||
<Flex align="flex-end">
|
||||
<Tabs size="large" type="card" {...sharedTabsProps} />
|
||||
<Tabs size="middle" type="card" {...sharedTabsProps} />
|
||||
<Tabs size="small" type="editable-card" {...sharedTabsProps} />
|
||||
<Tabs size="small" type="card" {...sharedTabsProps} />
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -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<TabsToken> = (token: TabsToken): CSSObject =>
|
||||
componentCls,
|
||||
cardPaddingSM,
|
||||
cardPaddingLG,
|
||||
cardHeightSM,
|
||||
cardHeightLG,
|
||||
horizontalItemPaddingSM,
|
||||
horizontalItemPaddingLG,
|
||||
} = token;
|
||||
return {
|
||||
// >>>>> shared
|
||||
[componentCls]: {
|
||||
'&-small': {
|
||||
[`> ${componentCls}-nav`]: {
|
||||
@@ -612,17 +625,24 @@ const genSizeStyle: GenerateStyle<TabsToken> = (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<TabsToken> = (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<TabsToken> = (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<TabsToken> = (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,
|
||||
|
||||
@@ -853,7 +853,7 @@ exports[`renders components/upload/demo/debug-disabled.tsx extend context correc
|
||||
type="file"
|
||||
/>
|
||||
<button
|
||||
style="border: 0px; background: none;"
|
||||
style="cursor: inherit; border: 0px; background: none;"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
@@ -1130,7 +1130,7 @@ exports[`renders components/upload/demo/debug-disabled.tsx extend context correc
|
||||
type="file"
|
||||
/>
|
||||
<button
|
||||
style="border: 0px; background: none;"
|
||||
style="cursor: inherit; border: 0px; background: none;"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
||||
@@ -805,7 +805,7 @@ exports[`renders components/upload/demo/debug-disabled.tsx correctly 1`] = `
|
||||
type="file"
|
||||
/>
|
||||
<button
|
||||
style="border:0;background:none"
|
||||
style="color:inherit;cursor:inherit;border:0;background:none"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
@@ -1062,7 +1062,7 @@ exports[`renders components/upload/demo/debug-disabled.tsx correctly 1`] = `
|
||||
type="file"
|
||||
/>
|
||||
<button
|
||||
style="border:0;background:none"
|
||||
style="color:inherit;cursor:inherit;border:0;background:none"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
||||
@@ -34,7 +34,10 @@ const fileList: UploadFile[] = [
|
||||
|
||||
const App: React.FC = () => {
|
||||
const uploadButton = (
|
||||
<button style={{ border: 0, background: 'none' }} type="button">
|
||||
<button
|
||||
style={{ color: 'inherit', cursor: 'inherit', border: 0, background: 'none' }}
|
||||
type="button"
|
||||
>
|
||||
<PlusOutlined />
|
||||
<div style={{ marginTop: 8 }}>Upload</div>
|
||||
</button>
|
||||
|
||||
@@ -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<OSSDataType>();
|
||||
|
||||
// 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<Readonly<AliyunOSSUploadProps>> = ({ value, onChange }) => {
|
||||
const { message } = App.useApp();
|
||||
|
||||
const [OSSData, setOSSData] = useState<OSSDataType>();
|
||||
|
||||
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 = () => (
|
||||
<Form labelCol={{ span: 4 }}>
|
||||
<Form.Item label="Photos" name="photos">
|
||||
<AliyunOSSUpload />
|
||||
@@ -106,4 +112,4 @@ const App: React.FC = () => (
|
||||
</Form>
|
||||
);
|
||||
|
||||
export default App;
|
||||
export default Demo;
|
||||
|
||||
@@ -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(<Watermark className="watermark" content="MutationObserver" />);
|
||||
const target = container.querySelector<HTMLDivElement>('.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', () => {
|
||||
|
||||
@@ -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<WatermarkProps> = (props) => {
|
||||
|
||||
const getClips = useClips();
|
||||
|
||||
type ClipParams = Parameters<typeof getClips>;
|
||||
const getClipsCache = useSingletonCache<ClipParams, ReturnType<typeof getClips>>();
|
||||
|
||||
const [watermarkInfo, setWatermarkInfo] = React.useState<[base64: string, contentWidth: number]>(
|
||||
null!,
|
||||
);
|
||||
@@ -181,24 +185,19 @@ const Watermark: React.FC<WatermarkProps> = (props) => {
|
||||
const drawCanvas = (
|
||||
drawContent?: NonNullable<WatermarkProps['content']> | 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]);
|
||||
};
|
||||
|
||||
|
||||
25
components/watermark/useSingletonCache.ts
Normal file
25
components/watermark/useSingletonCache.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import * as React from 'react';
|
||||
import isEqual from 'rc-util/lib/isEqual';
|
||||
|
||||
export type GetCache<T, R> = (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<T extends any[], R>(): GetCache<T, R> {
|
||||
const cacheRef = React.useRef<[any[] | null, R | null]>([null, null]);
|
||||
|
||||
const getCache: GetCache<T, R> = (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;
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user