Browse Source

Merge branch master into master-merge-feature

pull/41518/head
栗嘉男 2 years ago
parent
commit
de2dde6613
  1. 4
      .dumi/hooks/useLocale.ts
  2. 66
      .dumi/theme/builtins/ComponentTokenTable/index.tsx
  3. 8
      .dumi/theme/builtins/Previewer/CodePreviewer.tsx
  4. 1
      .dumi/theme/common/styles/Demo.tsx
  5. 2
      .dumi/theme/slots/Header/Navigation.tsx
  6. 6
      .dumi/theme/slots/Header/index.tsx
  7. 1
      .github/workflows/chatgpt-cr.yml
  8. 22
      .github/workflows/issues-similarity-analysis.yml
  9. 25
      CHANGELOG.en-US.md
  10. 25
      CHANGELOG.zh-CN.md
  11. 4
      components/anchor/index.en-US.md
  12. 4
      components/anchor/index.zh-CN.md
  13. 4
      components/badge/style/index.ts
  14. 11
      components/breadcrumb/BreadcrumbItem.tsx
  15. 19
      components/breadcrumb/__tests__/Breadcrumb.test.tsx
  16. 6
      components/breadcrumb/__tests__/__snapshots__/demo-extend.test.ts.snap
  17. 4
      components/breadcrumb/index.en-US.md
  18. 4
      components/breadcrumb/index.zh-CN.md
  19. 4
      components/carousel/index.en-US.md
  20. 4
      components/carousel/index.zh-CN.md
  21. 21
      components/config-provider/__tests__/nonce.test.tsx
  22. 2
      components/config-provider/index.tsx
  23. 12
      components/config-provider/style/index.ts
  24. 1
      components/date-picker/locale/fa_IR.ts
  25. 4
      components/divider/index.en-US.md
  26. 4
      components/divider/index.zh-CN.md
  27. 4
      components/dropdown/index.en-US.md
  28. 4
      components/dropdown/index.zh-CN.md
  29. 26
      components/dropdown/style/button.ts
  30. 12
      components/dropdown/style/index.ts
  31. 43
      components/form/FormItem/ItemHolder.tsx
  32. 75
      components/form/__tests__/index.test.tsx
  33. 5
      components/form/style/index.ts
  34. 3
      components/grid/col.tsx
  35. 4
      components/input/index.en-US.md
  36. 4
      components/input/index.zh-CN.md
  37. 4
      components/layout/index.en-US.md
  38. 4
      components/layout/index.zh-CN.md
  39. 11
      components/locale/sv_SE.ts
  40. 3
      components/menu/__tests__/__snapshots__/demo-extend.test.ts.snap
  41. 1
      components/menu/demo/submenu-theme.tsx
  42. 4
      components/menu/index.en-US.md
  43. 4
      components/menu/index.zh-CN.md
  44. 13
      components/menu/style/index.tsx
  45. 2
      components/menu/style/theme.tsx
  46. 17
      components/modal/__tests__/hook.test.tsx
  47. 9
      components/modal/useModal/HookModal.tsx
  48. 38
      components/pagination/Pagination.tsx
  49. 4
      components/pagination/index.en-US.md
  50. 4
      components/pagination/index.zh-CN.md
  51. 28
      components/pagination/style/index.tsx
  52. 4
      components/select/index.en-US.md
  53. 4
      components/select/index.zh-CN.md
  54. 4
      components/steps/index.en-US.md
  55. 4
      components/steps/index.zh-CN.md
  56. 1
      components/style/index.ts
  57. 14
      components/table/InternalTable.tsx
  58. 6
      components/table/Table.tsx
  59. 43
      components/table/__tests__/Table.filter.test.tsx
  60. 5
      components/table/hooks/useFilter/index.tsx
  61. 2
      components/table/hooks/usePagination.ts
  62. 12
      components/tabs/style/index.ts
  63. 4
      components/tag/style/index.ts
  64. 10
      components/theme/interface/maps/colors.ts
  65. 2
      components/theme/interface/maps/style.ts
  66. 5
      components/theme/interface/seeds.ts
  67. 2
      components/theme/internal.ts
  68. 75
      components/theme/util/genComponentStyleHook.ts
  69. 8
      components/theme/util/genPresetColor.tsx
  70. 4
      components/tooltip/style/index.ts
  71. 7
      components/tree-select/index.tsx
  72. 4
      components/typography/index.en-US.md
  73. 4
      components/typography/index.zh-CN.md
  74. 6
      package.json
  75. 2
      typings/custom-typings.d.ts

4
.dumi/hooks/useLocale.ts

@ -9,6 +9,6 @@ export default function useLocale<Key extends string>(
localeMap?: LocaleMap<Key>, localeMap?: LocaleMap<Key>,
): [Record<Key, string>, 'cn' | 'en'] { ): [Record<Key, string>, 'cn' | 'en'] {
const { id } = useDumiLocale(); const { id } = useDumiLocale();
const localeType = id === 'zh-CN' ? 'cn' : ('en' as const); const localeType = id === 'zh-CN' ? 'cn' : 'en';
return [localeMap?.[localeType]!, localeType]; return [localeMap?.[localeType], localeType];
} }

66
.dumi/theme/builtins/ComponentTokenTable/index.tsx

@ -40,20 +40,40 @@ function SubTokenTable({ defaultOpen, tokens, title }: SubTokenTableProps) {
return null; return null;
} }
const data = tokens.map((name) => { const data = tokens
const meta = tokenMeta[name]; .sort((token1, token2) => {
const hasColor1 = token1.toLowerCase().includes('color');
const hasColor2 = token2.toLowerCase().includes('color');
return { if (hasColor1 && !hasColor2) {
name, return -1;
desc: lang === 'cn' ? meta.desc : meta.descEn, }
type: meta.type,
value: (defaultToken as any)[name], if (!hasColor1 && hasColor2) {
}; return 1;
}); }
return token1 < token2 ? -1 : 1;
})
.map((name) => {
const meta = tokenMeta[name];
if (!meta) {
return null;
}
return {
name,
desc: lang === 'cn' ? meta.desc : meta.descEn,
type: meta.type,
value: (defaultToken as any)[name],
};
})
.filter((info) => info);
return ( return (
// Reuse `.markdown` style // Reuse `.markdown` style
<details className="markdown" open={defaultOpen}> <details className="markdown" open={defaultOpen || process.env.NODE_ENV !== 'production'}>
<summary> <summary>
<h3 style={{ display: 'inline' }}>{title}</h3> <h3 style={{ display: 'inline' }}>{title}</h3>
</summary> </summary>
@ -82,12 +102,32 @@ export interface ComponentTokenTableProps {
} }
function ComponentTokenTable({ component }: ComponentTokenTableProps) { function ComponentTokenTable({ component }: ComponentTokenTableProps) {
const { global: globalTokens = [], component: componentTokens = [] } = tokenData[component] || {}; const [mergedGlobalTokens] = React.useMemo(() => {
const globalTokenSet = new Set<string>();
let componentTokens: Record<string, string> = {};
component.split(',').forEach((comp) => {
const { global: globalTokens = [], component: singleComponentTokens = [] } =
tokenData[comp] || {};
globalTokens.forEach((token: string) => {
globalTokenSet.add(token);
});
componentTokens = {
...componentTokens,
...singleComponentTokens,
};
});
return [Array.from(globalTokenSet), componentTokens];
}, [component]);
return ( return (
<> <>
<SubTokenTable title="Component Token" tokens={componentTokens} defaultOpen /> {/* Component Token 先不展示 */}
<SubTokenTable title="Global Token" tokens={globalTokens} /> {/* <SubTokenTable title="Component Token" tokens={mergedComponentTokens} defaultOpen /> */}
<SubTokenTable title="Global Token" tokens={mergedGlobalTokens} />
</> </>
); );
} }

8
.dumi/theme/builtins/Previewer/CodePreviewer.tsx

@ -102,7 +102,7 @@ const CodePreviewer: React.FC<IPreviewerProps> = (props) => {
style, style,
compact, compact,
background, background,
filePath, filename,
version, version,
clientOnly, clientOnly,
} = props; } = props;
@ -333,10 +333,10 @@ createRoot(document.getElementById('container')).render(<Demo />);
...dependencies, ...dependencies,
react: '^18.0.0', react: '^18.0.0',
'react-dom': '^18.0.0', 'react-dom': '^18.0.0',
'react-scripts': '^4.0.0', 'react-scripts': '^5.0.0',
}, },
devDependencies: { devDependencies: {
typescript: '^4.0.5', typescript: '^5.0.2',
}, },
scripts: { scripts: {
start: 'react-scripts start', start: 'react-scripts start',
@ -399,7 +399,7 @@ createRoot(document.getElementById('container')).render(<Demo />);
{localizedTitle} {localizedTitle}
</a> </a>
</Tooltip> </Tooltip>
<EditButton title={<FormattedMessage id="app.content.edit-demo" />} filename={filePath} /> <EditButton title={<FormattedMessage id="app.content.edit-demo" />} filename={filename} />
</div> </div>
<div className="code-box-description">{introChildren}</div> <div className="code-box-description">{introChildren}</div>
<Space wrap size="middle" className="code-box-actions"> <Space wrap size="middle" className="code-box-actions">

1
.dumi/theme/common/styles/Demo.tsx

@ -38,7 +38,6 @@ const GlobalDemoStyles: React.FC = () => {
} }
.code-box-demo { .code-box-demo {
overflow: auto;
background-color: ${token.colorBgContainer}; background-color: ${token.colorBgContainer};
border-radius: ${token.borderRadius}px ${token.borderRadius}px 0 0; border-radius: ${token.borderRadius}px ${token.borderRadius}px 0 0;
} }

2
.dumi/theme/slots/Header/Navigation.tsx

@ -45,7 +45,7 @@ const useStyle = () => {
border-bottom: none; border-bottom: none;
& > ${antCls}-menu-item, & > ${antCls}-menu-submenu { & > ${antCls}-menu-item, & > ${antCls}-menu-submenu {
min-width: (40px + 12px * 2); min-width: ${40 + 12 * 2}px;
height: ${headerHeight}px; height: ${headerHeight}px;
padding-right: 12px; padding-right: 12px;
padding-left: 12px; padding-left: 12px;

6
.dumi/theme/slots/Header/index.tsx

@ -209,7 +209,11 @@ const Header: React.FC = () => {
.replace(/\/$/, ''); .replace(/\/$/, '');
return; return;
} }
window.location.href = currentUrl.replace(window.location.origin, url);
// Mirror url must have `/`, we add this for compatible
const urlObj = new URL(currentUrl.replace(window.location.origin, url));
urlObj.pathname = `${urlObj.pathname.replace(/\/$/, '')}/`;
window.location.href = urlObj.href;
}, []); }, []);
const onLangChange = useCallback(() => { const onLangChange = useCallback(() => {

1
.github/workflows/chatgpt-cr.yml

@ -11,6 +11,7 @@ on:
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event.pull_request.head.ref != 'feature' && github.event.pull_request.head.ref != 'master' && github.event.pull_request.head.ref != 'next' && github.event.pull_request.head.ref != 'master-merge-feature' && github.event.pull_request.head.ref != 'feature-merge-master' && github.event.pull_request.head.ref != 'next-merge-master' && github.event.pull_request.head.ref != 'next-merge-feature'
steps: steps:
- uses: anc95/ChatGPT-CodeReview@main - uses: anc95/ChatGPT-CodeReview@main
env: env:

22
.github/workflows/issues-similarity-analysis.yml

@ -1,22 +0,0 @@
name: Issues Similarity Analysis
on:
issues:
types: [opened, edited]
permissions:
contents: read
jobs:
similarity-analysis:
permissions:
issues: write # for actions-cool/issues-similarity-analysis to create issue comments
runs-on: ubuntu-latest
steps:
- name: analysis
uses: actions-cool/issues-similarity-analysis@v1
with:
filter-threshold: 0.5
title-excludes: ''
comment-title: '### You may look for issues:'
comment-body: '${index}. ${similarity} #${number}'

25
CHANGELOG.en-US.md

@ -15,6 +15,31 @@ timeline: true
--- ---
## 5.3.3
`2023-3-28`
- Menu
- 🐞 Fix Menu `items` not accept `key` issue. [#41434](https://github.com/ant-design/ant-design/pull/41434) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 Fix submenu themes being overwritten when using `getPopupContainer` to select the main Menu. [#41465](https://github.com/ant-design/ant-design/pull/41465) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 Fix Table filter do not persist filter status when filter dropdown is visible. [#41445](https://github.com/ant-design/ant-design/pull/41445) [@ablakey](https://github.com/ablakey)
- 🐞 Fix Modal using `useModal` is not transparent and prefers user settings. [#41422](https://github.com/ant-design/ant-design/pull/41422) [@luo3house](https://github.com/luo3house)
- Form
- 🐞 Fix the problem that the Form validation state does not change in sequence. [#41412](https://github.com/ant-design/ant-design/pull/41412) [@kiner-tang](https://github.com/kiner-tang)
- 💄 Fix Form component layout exceptions when set props `layout="inline"`. [#41140](https://github.com/ant-design/ant-design/pull/41140) [@itkui](https://github.com/itkui)
- 💄 Fix ConfigProvider `nonce` not working on CSS-in-JS style. [#41482](https://github.com/ant-design/ant-design/pull/41482)
- 💄 Fix Pagination when `size=small`, pagination button active, previous page next page button hover and active styles are lost. [#41462](https://github.com/ant-design/ant-design/pull/41462) [#41458](https://github.com/ant-design/ant-design/pull/41458)
- 💄 Fix the style problem that the bottom border of the Tabs component overlaps with other borders. [#41381](https://github.com/ant-design/ant-design/pull/41381)
- 💄 Fix Dropdown.Button down icon size issue. [#41501](https://github.com/ant-design/ant-design/pull/41501)
- TypeScript
- 🐞 Fix the incorrect type definition of Breadcrumb.Item `menu`. [#41373](https://github.com/ant-design/ant-design/pull/41373)
- 🤖 Optimize Grid Col type. [#41453](https://github.com/ant-design/ant-design/pull/41453) [@vaakian](https://github.com/vaakian)
- 🤖 Optimize Table `resetPagination` type. [#41415](https://github.com/ant-design/ant-design/pull/41415)
- 🤖 Optimize TreeSelect `InternalTreeSelect` type. [#41386](https://github.com/ant-design/ant-design/pull/41386) [@Andarist](https://github.com/Andarist)
- Locales
- 🇮🇷 Improve DatePicker `fa_IR` translation. [#41455](https://github.com/ant-design/ant-design/pull/41455) [@ds1371dani](https://github.com/ds1371dani)
- 🇸🇪 Add the missing content of `sv_SE` language. [#41424](https://github.com/ant-design/ant-design/pull/41424) [@dhalenok](https://github.com/dhalenok)
## 5.3.2 ## 5.3.2
`2023-03-20` `2023-03-20`

25
CHANGELOG.zh-CN.md

@ -15,6 +15,31 @@ timeline: true
--- ---
## 5.3.3
`2023-3-28`
- Menu
- 🐞 修复 Menu `items` 没有使用传入的 `key` 的问题。[#41434](https://github.com/ant-design/ant-design/pull/41434) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 修复 Menu 使用 `getPopupContainer` 选择主菜单时子菜单主题被覆盖。[#41465](https://github.com/ant-design/ant-design/pull/41465) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 修复 Table 过滤器未保持状态当筛选下拉框展示时。[#41445](https://github.com/ant-design/ant-design/pull/41445) [@ablakey](https://github.com/ablakey)
- 🐞 修复 Modal 使用 `useModal` 未透传并优先选择用户设定。[#41422](https://github.com/ant-design/ant-design/pull/41422) [@luo3house](https://github.com/luo3house)
- Form
- 🐞 修复 Form 验证状态不按照顺序改变的问题。[#41412](https://github.com/ant-design/ant-design/pull/41412) [@kiner-tang](https://github.com/kiner-tang)
- 💄 修复 Form 组件 `layout="inline"` 时组件标题与表单项布局异常换行问题。[#41140](https://github.com/ant-design/ant-design/pull/41140) [@itkui](https://github.com/itkui)
- 💄 修复 ConfigProvider `nonce` 对 CSS-in-JS 样式不生效的问题。[#41482](https://github.com/ant-design/ant-design/pull/41482)
- 💄 修复 Pagination `size=small` 时,分页按钮 active、上一页下一页按钮 hover 和 active 样式丢失。[#41462](https://github.com/ant-design/ant-design/pull/41462) [#41458](https://github.com/ant-design/ant-design/pull/41458)
- 💄 修复 Tabs 组件下边框与其他边框叠加的样式问题。[#41381](https://github.com/ant-design/ant-design/pull/41381)
- 💄 修复 Dropdown.Button down 图标尺寸问题。[#41501](https://github.com/ant-design/ant-design/pull/41501)
- TypeScript
- 🐞 修复 Breadcrumb.Item `menu` 类型定义不正确的问题。[#41373](https://github.com/ant-design/ant-design/pull/41373)
- 🤖 优化 Grid Col 类型提示。[#41453](https://github.com/ant-design/ant-design/pull/41453) [@vaakian](https://github.com/vaakian)
- 🤖 优化 Table `resetPagination` 类型提示。[#41415](https://github.com/ant-design/ant-design/pull/41415)
- 🤖 优化 TreeSelect `InternalTreeSelect` 类型提示。[#41386](https://github.com/ant-design/ant-design/pull/41386) [@Andarist](https://github.com/Andarist)
- 国际化
- 🇮🇷 完善 DatePicker `fa_IR` 翻译。[#41455](https://github.com/ant-design/ant-design/pull/41455) [@ds1371dani](https://github.com/ds1371dani)
- 🇸🇪 完善 `sv_SE` 语言缺失内容。[#41424](https://github.com/ant-design/ant-design/pull/41424) [@dhalenok](https://github.com/dhalenok)
## 5.3.2 ## 5.3.2
`2023-03-20` `2023-03-20`

4
components/anchor/index.en-US.md

@ -68,3 +68,7 @@ We recommend using the items form instead.
| href | The target of hyperlink | string | | | | href | The target of hyperlink | string | | |
| target | Specifies where to display the linked URL | string | | | | target | Specifies where to display the linked URL | string | | |
| title | The content of hyperlink | ReactNode | | | | title | The content of hyperlink | ReactNode | | |
## Design Token
<ComponentTokenTable component="Anchor"></ComponentTokenTable>

4
components/anchor/index.zh-CN.md

@ -69,3 +69,7 @@ group:
| href | 锚点链接 | string | - | | | href | 锚点链接 | string | - | |
| target | 该属性指定在何处显示链接的资源 | string | - | | | target | 该属性指定在何处显示链接的资源 | string | - | |
| title | 文字内容 | ReactNode | - | | | title | 文字内容 | ReactNode | - | |
## Design Token
<ComponentTokenTable component="Anchor"></ComponentTokenTable>

4
components/badge/style/index.ts

@ -1,8 +1,8 @@
import type { CSSObject } from '@ant-design/cssinjs'; import type { CSSObject } from '@ant-design/cssinjs';
import { Keyframes } from '@ant-design/cssinjs'; import { Keyframes } from '@ant-design/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken, genPresetColor } from '../../theme/internal';
import { genPresetColor, resetComponent } from '../../style'; import { resetComponent } from '../../style';
interface BadgeToken extends FullToken<'Badge'> { interface BadgeToken extends FullToken<'Badge'> {
badgeFontHeight: number; badgeFontHeight: number;

11
components/breadcrumb/BreadcrumbItem.tsx

@ -1,9 +1,9 @@
import DownOutlined from '@ant-design/icons/DownOutlined'; import DownOutlined from '@ant-design/icons/DownOutlined';
import * as React from 'react'; import * as React from 'react';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import type { DropdownProps } from '../dropdown/dropdown'; import type { DropdownProps } from '../dropdown/dropdown';
import Dropdown from '../dropdown/dropdown'; import Dropdown from '../dropdown/dropdown';
import warning from '../_util/warning';
import BreadcrumbSeparator from './BreadcrumbSeparator'; import BreadcrumbSeparator from './BreadcrumbSeparator';
export interface SeparatorType { export interface SeparatorType {
@ -11,8 +11,9 @@ export interface SeparatorType {
key?: React.Key; key?: React.Key;
} }
type MenuType = DropdownProps['menu']; type MenuType = NonNullable<DropdownProps['menu']>;
interface MenuItem { interface MenuItem {
key?: React.Key;
title?: React.ReactNode; title?: React.ReactNode;
label?: React.ReactNode; label?: React.ReactNode;
path?: string; path?: string;
@ -68,10 +69,10 @@ const BreadcrumbItem: CompoundedComponent = (props: BreadcrumbItemProps) => {
}; };
if (menu) { if (menu) {
const { items, ...menuProps } = menu! || {}; const { items, ...menuProps } = menu || {};
mergeDropDownProps.menu = { mergeDropDownProps.menu = {
...menuProps, ...menuProps,
items: items?.map(({ title, label, path, ...itemProps }, index) => { items: items?.map(({ key, title, label, path, ...itemProps }, index) => {
let mergedLabel: React.ReactNode = label ?? title; let mergedLabel: React.ReactNode = label ?? title;
if (path) { if (path) {
@ -80,7 +81,7 @@ const BreadcrumbItem: CompoundedComponent = (props: BreadcrumbItemProps) => {
return { return {
...itemProps, ...itemProps,
key: index, key: key ?? index,
label: mergedLabel as string, label: mergedLabel as string,
}; };
}), }),

19
components/breadcrumb/__tests__/Breadcrumb.test.tsx

@ -291,6 +291,21 @@ describe('Breadcrumb', () => {
expect(asFragment().firstChild).toMatchSnapshot(); expect(asFragment().firstChild).toMatchSnapshot();
}); });
it('should support Breadcrumb.Item customized menu items key', () => {
const key = 'test-key';
const { container } = render(
<Breadcrumb>
<Breadcrumb.Item dropdownProps={{ open: true }} menu={{ items: [{ key }] }}>
test-item
</Breadcrumb.Item>
</Breadcrumb>,
);
const item = container.querySelector<HTMLElement>('.ant-dropdown-menu-item');
expect(item?.getAttribute('data-menu-id')?.endsWith(key)).toBeTruthy();
});
it('should support string `0` and number `0`', () => { it('should support string `0` and number `0`', () => {
const { container } = render( const { container } = render(
<Breadcrumb <Breadcrumb
@ -343,4 +358,8 @@ describe('Breadcrumb', () => {
const item = await wrapper.findByText('test'); const item = await wrapper.findByText('test');
expect(item).toHaveClass(testClassName); expect(item).toHaveClass(testClassName);
}); });
it('Breadcrumb.Item menu type', () => {
expect(<Breadcrumb.Item menu={{ selectable: true }} />).toBeTruthy();
});
}); });

6
components/breadcrumb/__tests__/__snapshots__/demo-extend.test.ts.snap

@ -317,7 +317,7 @@ exports[`renders components/breadcrumb/demo/overlay.tsx extend context correctly
> >
<li <li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child" class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-0" data-menu-id="rc-menu-uuid-test-1"
role="menuitem" role="menuitem"
tabindex="-1" tabindex="-1"
> >
@ -352,7 +352,7 @@ exports[`renders components/breadcrumb/demo/overlay.tsx extend context correctly
</div> </div>
<li <li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child" class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-1" data-menu-id="rc-menu-uuid-test-2"
role="menuitem" role="menuitem"
tabindex="-1" tabindex="-1"
> >
@ -387,7 +387,7 @@ exports[`renders components/breadcrumb/demo/overlay.tsx extend context correctly
</div> </div>
<li <li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child" class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-2" data-menu-id="rc-menu-uuid-test-3"
role="menuitem" role="menuitem"
tabindex="-1" tabindex="-1"
> >

4
components/breadcrumb/index.en-US.md

@ -125,3 +125,7 @@ function itemRender(route, params, items, paths) {
return <Breadcrumb itemRender={itemRender} items={items} />; return <Breadcrumb itemRender={itemRender} items={items} />;
``` ```
## Design Token
<ComponentTokenTable component="Breadcrumb"></ComponentTokenTable>

4
components/breadcrumb/index.zh-CN.md

@ -126,3 +126,7 @@ function itemRender(item, params, items, paths) {
return <Breadcrumb itemRender={itemRender} items={items} />; return <Breadcrumb itemRender={itemRender} items={items} />;
``` ```
## Design Token
<ComponentTokenTable component="Breadcrumb"></ComponentTokenTable>

4
components/carousel/index.en-US.md

@ -33,8 +33,8 @@ A carousel component. Scales with its container.
| dots | Whether to show the dots at the bottom of the gallery, `object` for `dotsClass` and any others | boolean \| { className?: string } | true | | | dots | Whether to show the dots at the bottom of the gallery, `object` for `dotsClass` and any others | boolean \| { className?: string } | true | |
| easing | Transition interpolation function name | string | `linear` | | | easing | Transition interpolation function name | string | `linear` | |
| effect | Transition effect | `scrollx` \| `fade` | `scrollx` | | | effect | Transition effect | `scrollx` \| `fade` | `scrollx` | |
| afterChange | Callback function called after the current index changes | function(current) | - | | | afterChange | Callback function called after the current index changes | (current: number) => void | - | |
| beforeChange | Callback function called before the current index changes | function(from, to) | - | | | beforeChange | Callback function called before the current index changes | (current: number, next: number) => void | - | |
## Methods ## Methods

4
components/carousel/index.zh-CN.md

@ -34,8 +34,8 @@ demo:
| dots | 是否显示面板指示点,如果为 `object` 则同时可以指定 `dotsClass` 或者 | boolean \| { className?: string } | true | | | dots | 是否显示面板指示点,如果为 `object` 则同时可以指定 `dotsClass` 或者 | boolean \| { className?: string } | true | |
| easing | 动画效果 | string | `linear` | | | easing | 动画效果 | string | `linear` | |
| effect | 动画效果函数 | `scrollx` \| `fade` | `scrollx` | | | effect | 动画效果函数 | `scrollx` \| `fade` | `scrollx` | |
| afterChange | 切换面板的回调 | function(current) | - | | | afterChange | 切换面板的回调 | (current: number) => void | - | |
| beforeChange | 切换面板的回调 | function(from, to) | - | | | beforeChange | 切换面板的回调 | (current: number, next: number) => void | - | |
## 方法 ## 方法

21
components/config-provider/__tests__/icon.test.tsx → components/config-provider/__tests__/nonce.test.tsx

@ -1,8 +1,10 @@
import { createCache, StyleProvider } from '@ant-design/cssinjs';
import { SmileOutlined } from '@ant-design/icons'; import { SmileOutlined } from '@ant-design/icons';
import IconContext from '@ant-design/icons/lib/components/Context'; import IconContext from '@ant-design/icons/lib/components/Context';
import React from 'react'; import React from 'react';
import { render } from '../../../tests/utils';
import ConfigProvider from '..'; import ConfigProvider from '..';
import { render } from '../../../tests/utils';
import Button from '../../button';
describe('ConfigProvider.Icon', () => { describe('ConfigProvider.Icon', () => {
beforeEach(() => { beforeEach(() => {
@ -62,4 +64,21 @@ describe('ConfigProvider.Icon', () => {
expect(styleNode?.nonce).toEqual('light'); expect(styleNode?.nonce).toEqual('light');
expect(container.querySelector('#csp')?.innerHTML).toEqual('light'); expect(container.querySelector('#csp')?.innerHTML).toEqual('light');
}); });
it('cssinjs should support nonce', () => {
render(
<StyleProvider cache={createCache()}>
<ConfigProvider csp={{ nonce: 'bamboo' }}>
<Button />
</ConfigProvider>
</StyleProvider>,
);
const styleList = Array.from(document.querySelectorAll('style'));
expect(styleList.length).toBeTruthy();
styleList.forEach((style) => {
expect(style.nonce).toEqual('bamboo');
});
});
}); });

2
components/config-provider/index.tsx

@ -206,7 +206,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
const shouldWrapSSR = iconPrefixCls !== parentContext.iconPrefixCls; const shouldWrapSSR = iconPrefixCls !== parentContext.iconPrefixCls;
const csp = customCsp || parentContext.csp; const csp = customCsp || parentContext.csp;
const wrapSSR = useStyle(iconPrefixCls); const wrapSSR = useStyle(iconPrefixCls, csp);
const mergedTheme = useTheme(theme, parentContext.theme); const mergedTheme = useTheme(theme, parentContext.theme);

12
components/config-provider/style/index.ts

@ -1,12 +1,20 @@
import { useStyleRegister } from '@ant-design/cssinjs'; import { useStyleRegister } from '@ant-design/cssinjs';
import type { CSPConfig } from '..';
import { resetIcon } from '../../style'; import { resetIcon } from '../../style';
import { useToken } from '../../theme/internal'; import { useToken } from '../../theme/internal';
const useStyle = (iconPrefixCls: string) => { const useStyle = (iconPrefixCls: string, csp?: CSPConfig) => {
const [theme, token] = useToken(); const [theme, token] = useToken();
// Generate style for icons // Generate style for icons
return useStyleRegister( return useStyleRegister(
{ theme, token, hashId: '', path: ['ant-design-icons', iconPrefixCls] }, {
theme,
token,
hashId: '',
path: ['ant-design-icons', iconPrefixCls],
nonce: () => csp?.nonce!,
},
() => [ () => [
{ {
[`.${iconPrefixCls}`]: { [`.${iconPrefixCls}`]: {

1
components/date-picker/locale/fa_IR.ts

@ -12,6 +12,7 @@ const locale: PickerLocale = {
weekPlaceholder: 'انتخاب هفته', weekPlaceholder: 'انتخاب هفته',
rangePlaceholder: ['تاریخ شروع', 'تاریخ پایان'], rangePlaceholder: ['تاریخ شروع', 'تاریخ پایان'],
rangeYearPlaceholder: ['سال شروع', 'سال پایان'], rangeYearPlaceholder: ['سال شروع', 'سال پایان'],
rangeQuarterPlaceholder: ['فصل شروع', 'فصل پایان'],
rangeMonthPlaceholder: ['ماه شروع', 'ماه پایان'], rangeMonthPlaceholder: ['ماه شروع', 'ماه پایان'],
rangeWeekPlaceholder: ['هفته شروع', 'هفته پایان'], rangeWeekPlaceholder: ['هفته شروع', 'هفته پایان'],
...CalendarLocale, ...CalendarLocale,

4
components/divider/index.en-US.md

@ -38,3 +38,7 @@ A divider line separates different content.
| plain | Divider text show as plain style | boolean | true | 4.2.0 | | plain | Divider text show as plain style | boolean | true | 4.2.0 |
| style | The style object of container | CSSProperties | - | | | style | The style object of container | CSSProperties | - | |
| type | The direction type of divider | `horizontal` \| `vertical` | `horizontal` | | | type | The direction type of divider | `horizontal` \| `vertical` | `horizontal` | |
## Design Token
<ComponentTokenTable component="Divider"></ComponentTokenTable>

4
components/divider/index.zh-CN.md

@ -39,3 +39,7 @@ group:
| plain | 文字是否显示为普通正文样式 | boolean | false | 4.2.0 | | plain | 文字是否显示为普通正文样式 | boolean | false | 4.2.0 |
| style | 分割线样式对象 | CSSProperties | - | | | style | 分割线样式对象 | CSSProperties | - | |
| type | 水平还是垂直类型 | `horizontal` \| `vertical` | `horizontal` | | | type | 水平还是垂直类型 | `horizontal` \| `vertical` | `horizontal` | |
## Design Token
<ComponentTokenTable component="Divider"></ComponentTokenTable>

4
components/dropdown/index.en-US.md

@ -72,3 +72,7 @@ Same props from Dropdown. And includes additional props:
## Note ## Note
Please ensure that the child node of `Dropdown` accepts `onMouseEnter`, `onMouseLeave`, `onFocus`, `onClick` events. Please ensure that the child node of `Dropdown` accepts `onMouseEnter`, `onMouseLeave`, `onFocus`, `onClick` events.
## Design Token
<ComponentTokenTable component="Dropdown"></ComponentTokenTable>

4
components/dropdown/index.zh-CN.md

@ -76,3 +76,7 @@ demo:
## 注意 ## 注意
请确保 `Dropdown` 的子元素能接受 `onMouseEnter`、`onMouseLeave`、`onFocus`、`onClick` 事件。 请确保 `Dropdown` 的子元素能接受 `onMouseEnter`、`onMouseLeave`、`onFocus`、`onClick` 事件。
## Design Token
<ComponentTokenTable component="Dropdown"></ComponentTokenTable>

26
components/dropdown/style/button.ts

@ -1,26 +0,0 @@
import type { DropdownToken } from '.';
import type { GenerateStyle } from '../../theme/internal';
const genButtonStyle: GenerateStyle<DropdownToken> = (token) => {
const { componentCls, antCls, paddingXS, opacityLoading } = token;
return {
[`${componentCls}-button`]: {
whiteSpace: 'nowrap',
[`&${antCls}-btn-group > ${antCls}-btn`]: {
[`&-loading, &-loading + ${antCls}-btn`]: {
cursor: 'default',
pointerEvents: 'none',
opacity: opacityLoading,
},
[`&:last-child:not(:first-child):not(${antCls}-btn-icon-only)`]: {
paddingInline: paddingXS,
},
},
},
};
};
export default genButtonStyle;

12
components/dropdown/style/index.ts

@ -11,7 +11,6 @@ import {
import getArrowStyle, { getArrowOffset } from '../../style/placementArrow'; import getArrowStyle, { getArrowOffset } from '../../style/placementArrow';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import genButtonStyle from './button';
import genStatusStyle from './status'; import genStatusStyle from './status';
export interface ComponentToken { export interface ComponentToken {
@ -71,6 +70,11 @@ const genBaseStyle: GenerateStyle<DropdownToken> = (token) => {
content: '""', content: '""',
}, },
[`&-trigger${antCls}-btn > ${iconCls}-down`]: {
fontSize: fontSizeIcon,
transform: 'none',
},
[`${componentCls}-wrap`]: { [`${componentCls}-wrap`]: {
position: 'relative', position: 'relative',
@ -350,11 +354,7 @@ export default genComponentStyleHook(
dropdownPaddingVertical, dropdownPaddingVertical,
dropdownEdgeChildPadding: paddingXXS, dropdownEdgeChildPadding: paddingXXS,
}); });
return [ return [genBaseStyle(dropdownToken), genStatusStyle(dropdownToken)];
genBaseStyle(dropdownToken),
genButtonStyle(dropdownToken),
genStatusStyle(dropdownToken),
];
}, },
(token) => ({ (token) => ({
zIndexPopup: token.zIndexPopupBase + 50, zIndexPopup: token.zIndexPopupBase + 50,

43
components/form/FormItem/ItemHolder.tsx

@ -9,10 +9,10 @@ import omit from 'rc-util/lib/omit';
import * as React from 'react'; import * as React from 'react';
import type { FormItemProps, ValidateStatus } from '.'; import type { FormItemProps, ValidateStatus } from '.';
import { Row } from '../../grid'; import { Row } from '../../grid';
import type { FormItemStatusContextProps, ReportMetaChange } from '../context';
import { FormContext, FormItemInputContext, NoStyleItemContext } from '../context';
import FormItemInput from '../FormItemInput'; import FormItemInput from '../FormItemInput';
import FormItemLabel from '../FormItemLabel'; import FormItemLabel from '../FormItemLabel';
import type { FormItemStatusContextProps, ReportMetaChange } from '../context';
import { FormContext, FormItemInputContext, NoStyleItemContext } from '../context';
import useDebounce from '../hooks/useDebounce'; import useDebounce from '../hooks/useDebounce';
const iconMap = { const iconMap = {
@ -81,29 +81,38 @@ export default function ItemHolder(props: ItemHolderProps) {
}; };
// ======================== Status ======================== // ======================== Status ========================
let mergedValidateStatus: ValidateStatus = '';
if (validateStatus !== undefined) { const getValidateState = (isDebounce = false) => {
mergedValidateStatus = validateStatus; let status: ValidateStatus = '';
} else if (meta.validating) { const _errors = isDebounce ? debounceErrors : meta.errors;
mergedValidateStatus = 'validating'; const _warnings = isDebounce ? debounceWarnings : meta.warnings;
} else if (debounceErrors.length) { if (validateStatus !== undefined) {
mergedValidateStatus = 'error'; status = validateStatus;
} else if (debounceWarnings.length) { } else if (meta.validating) {
mergedValidateStatus = 'warning'; status = 'validating';
} else if (meta.touched || (hasFeedback && meta.validated)) { } else if (_errors.length) {
// success feedback should display when pass hasFeedback prop and current value is valid value status = 'error';
mergedValidateStatus = 'success'; } else if (_warnings.length) {
} status = 'warning';
} else if (meta.touched || (hasFeedback && meta.validated)) {
// success feedback should display when pass hasFeedback prop and current value is valid value
status = 'success';
}
return status;
};
const mergedValidateStatus = getValidateState();
const formItemStatusContext = React.useMemo<FormItemStatusContextProps>(() => { const formItemStatusContext = React.useMemo<FormItemStatusContextProps>(() => {
let feedbackIcon: React.ReactNode; let feedbackIcon: React.ReactNode;
const desplayValidateStatus = getValidateState(true);
if (hasFeedback) { if (hasFeedback) {
const IconNode = mergedValidateStatus && iconMap[mergedValidateStatus]; const IconNode = desplayValidateStatus && iconMap[desplayValidateStatus];
feedbackIcon = IconNode ? ( feedbackIcon = IconNode ? (
<span <span
className={classNames( className={classNames(
`${itemPrefixCls}-feedback-icon`, `${itemPrefixCls}-feedback-icon`,
`${itemPrefixCls}-feedback-icon-${mergedValidateStatus}`, `${itemPrefixCls}-feedback-icon-${desplayValidateStatus}`,
)} )}
> >
<IconNode /> <IconNode />

75
components/form/__tests__/index.test.tsx

@ -1,30 +1,30 @@
import type { ColProps } from 'antd/es/grid';
import classNames from 'classnames';
import type { ChangeEventHandler } from 'react'; import type { ChangeEventHandler } from 'react';
import React, { useState, useEffect, useRef } from 'react'; import React, { version as ReactVersion, useEffect, useRef, useState } from 'react';
import scrollIntoView from 'scroll-into-view-if-needed'; import scrollIntoView from 'scroll-into-view-if-needed';
import classNames from 'classnames';
import type { ColProps } from 'antd/es/grid';
import type { FormInstance } from '..'; import type { FormInstance } from '..';
import Form from '..'; import Form from '..';
import * as Util from '../util'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, pureRender, render, screen, waitFakeTimer } from '../../../tests/utils';
import Button from '../../button'; import Button from '../../button';
import Input from '../../input';
import Select from '../../select';
import Upload from '../../upload';
import Cascader from '../../cascader'; import Cascader from '../../cascader';
import Checkbox from '../../checkbox'; import Checkbox from '../../checkbox';
import ConfigProvider from '../../config-provider';
import DatePicker from '../../date-picker'; import DatePicker from '../../date-picker';
import Drawer from '../../drawer';
import Input from '../../input';
import InputNumber from '../../input-number'; import InputNumber from '../../input-number';
import zhCN from '../../locale/zh_CN';
import Modal from '../../modal';
import Radio from '../../radio'; import Radio from '../../radio';
import Select from '../../select';
import Switch from '../../switch'; import Switch from '../../switch';
import TreeSelect from '../../tree-select'; import TreeSelect from '../../tree-select';
import mountTest from '../../../tests/shared/mountTest'; import Upload from '../../upload';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, pureRender, render, screen, waitFakeTimer } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
import Drawer from '../../drawer';
import zhCN from '../../locale/zh_CN';
import Modal from '../../modal';
import type { NamePath } from '../interface'; import type { NamePath } from '../interface';
import * as Util from '../util';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
const { TextArea } = Input; const { TextArea } = Input;
@ -1626,4 +1626,51 @@ describe('Form', () => {
expect(container.querySelectorAll('.ant-form-item-has-feedback').length).toBe(1); expect(container.querySelectorAll('.ant-form-item-has-feedback').length).toBe(1);
expect(container.querySelectorAll('.ant-form-item-has-success').length).toBe(1); expect(container.querySelectorAll('.ant-form-item-has-success').length).toBe(1);
}); });
it('validate status should be change in order', async () => {
const onChange = jest.fn();
const CustomInput = (props: any) => {
const { status } = Form.Item.useStatus();
useEffect(() => {
onChange(status);
}, [status]);
return <Input {...props} />;
};
const App = () => (
<Form>
<Form.Item>
<Form.Item name="test" label="test" rules={[{ len: 3, message: 'error.' }]}>
<CustomInput />
</Form.Item>
</Form.Item>
</Form>
);
render(<App />);
await waitFakeTimer();
// initial validate
const initTriggerTime = ReactVersion.startsWith('18') ? 2 : 1;
expect(onChange).toHaveBeenCalledTimes(initTriggerTime);
let idx = 1;
expect(onChange).toHaveBeenNthCalledWith(idx++, '');
if (initTriggerTime === 2) {
expect(onChange).toHaveBeenNthCalledWith(idx++, '');
}
// change trigger
await changeValue(0, '1');
expect(onChange).toHaveBeenCalledTimes(initTriggerTime + 2);
expect(onChange).toHaveBeenNthCalledWith(idx++, 'validating');
expect(onChange).toHaveBeenNthCalledWith(idx++, 'error');
await changeValue(0, '11');
expect(onChange).toHaveBeenCalledTimes(initTriggerTime + 4);
expect(onChange).toHaveBeenNthCalledWith(idx++, 'validating');
expect(onChange).toHaveBeenNthCalledWith(idx++, 'error');
await changeValue(0, '111');
expect(onChange).toHaveBeenCalledTimes(initTriggerTime + 6);
expect(onChange).toHaveBeenNthCalledWith(idx++, 'validating');
expect(onChange).toHaveBeenNthCalledWith(idx++, 'success');
});
}); });

5
components/form/style/index.ts

@ -355,10 +355,13 @@ const genInlineStyle: GenerateStyle<FormToken> = (token) => {
[formItemCls]: { [formItemCls]: {
flex: 'none', flex: 'none',
flexWrap: 'nowrap',
marginInlineEnd: token.margin, marginInlineEnd: token.margin,
marginBottom: 0, marginBottom: 0,
'&-row': {
flexWrap: 'nowrap',
},
'&-with-help': { '&-with-help': {
marginBottom: token.marginLG, marginBottom: token.marginLG,
}, },

3
components/grid/col.tsx

@ -3,11 +3,12 @@ import * as React from 'react';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import RowContext from './RowContext'; import RowContext from './RowContext';
import { useColStyle } from './style'; import { useColStyle } from './style';
import type { LiteralUnion } from '../_util/type';
// https://github.com/ant-design/ant-design/issues/14324 // https://github.com/ant-design/ant-design/issues/14324
type ColSpanType = number | string; type ColSpanType = number | string;
type FlexType = number | 'none' | 'auto' | string; type FlexType = number | LiteralUnion<'none' | 'auto'>;
export interface ColSize { export interface ColSize {
flex?: FlexType; flex?: FlexType;

4
components/input/index.en-US.md

@ -120,6 +120,10 @@ Supports all props of `Input`.
| blur | Remove focus | - | | | blur | Remove focus | - | |
| focus | Get focus | (option?: { preventScroll?: boolean, cursor?: 'start' \| 'end' \| 'all' }) | option - 4.10.0 | | focus | Get focus | (option?: { preventScroll?: boolean, cursor?: 'start' \| 'end' \| 'all' }) | option - 4.10.0 |
## Design Token
<ComponentTokenTable component="Input"></ComponentTokenTable>
## FAQ ## FAQ
### Why Input lose focus when change `prefix/suffix/showCount` ### Why Input lose focus when change `prefix/suffix/showCount`

4
components/input/index.zh-CN.md

@ -139,6 +139,10 @@ Input 的其他属性和 React 自带的 [input](https://reactjs.org/docs/dom-el
| textarea | `textarea` 元素 | 5.4.0 | | textarea | `textarea` 元素 | 5.4.0 |
| count | 文字计数元素 | 5.4.0 | | count | 文字计数元素 | 5.4.0 |
## Design Token
<ComponentTokenTable component="Input"></ComponentTokenTable>
## FAQ ## FAQ
### 为什么我动态改变 `prefix/suffix/showCount` 时,Input 会失去焦点? ### 为什么我动态改变 `prefix/suffix/showCount` 时,Input 会失去焦点?

4
components/layout/index.en-US.md

@ -127,3 +127,7 @@ The sidebar.
xxl: '1600px', xxl: '1600px',
} }
``` ```
## Design Token
<ComponentTokenTable component="Layout"></ComponentTokenTable>

4
components/layout/index.zh-CN.md

@ -128,3 +128,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HdS6Q5vUCDcAAA
xxl: '1600px', xxl: '1600px',
} }
``` ```
## Design Token
<ComponentTokenTable component="Layout"></ComponentTokenTable>

11
components/locale/sv_SE.ts

@ -21,6 +21,8 @@ const localeValues: Locale = {
filterConfirm: 'OK', filterConfirm: 'OK',
filterReset: 'Återställ', filterReset: 'Återställ',
filterEmptyText: 'Inga filter', filterEmptyText: 'Inga filter',
filterCheckall: 'Markera alla objekt',
filterSearchPlaceholder: 'Sök i filter',
emptyText: 'Ingen data', emptyText: 'Ingen data',
selectAll: 'Markera nuvarande sida', selectAll: 'Markera nuvarande sida',
selectInvert: 'Invertera nuvarande sida', selectInvert: 'Invertera nuvarande sida',
@ -33,6 +35,11 @@ const localeValues: Locale = {
triggerAsc: 'Klicka för att sortera i stigande ordning', triggerAsc: 'Klicka för att sortera i stigande ordning',
cancelSort: 'Klicka för att avbryta sortering', cancelSort: 'Klicka för att avbryta sortering',
}, },
Tour: {
Next: 'Nästa',
Previous: 'Föregående',
Finish: 'Avsluta',
},
Modal: { Modal: {
okText: 'OK', okText: 'OK',
cancelText: 'Avbryt', cancelText: 'Avbryt',
@ -129,6 +136,10 @@ const localeValues: Locale = {
Image: { Image: {
preview: 'Förhandsgranska', preview: 'Förhandsgranska',
}, },
QRCode: {
expired: 'QR-koden har upphört att gälla',
refresh: 'Uppdatera',
},
}; };
export default localeValues; export default localeValues;

3
components/menu/__tests__/__snapshots__/demo-extend.test.ts.snap

@ -5079,6 +5079,9 @@ Array [
</ul> </ul>
</div> </div>
</div> </div>
<div
style="position: absolute; top: 0px; left: 0px; width: 100%;"
/>
</li> </li>
<li <li
class="ant-menu-item ant-menu-item-only-child" class="ant-menu-item ant-menu-item-only-child"

1
components/menu/demo/submenu-theme.tsx

@ -63,6 +63,7 @@ const App: React.FC = () => {
mode="vertical" mode="vertical"
theme="dark" theme="dark"
items={items} items={items}
getPopupContainer={(node) => node.parentNode as HTMLElement}
/> />
</> </>
); );

4
components/menu/index.en-US.md

@ -142,3 +142,7 @@ Menu will render fully item in flex layout and then collapse it. You need tell f
<Menu style={{ minWidth: 0, flex: "auto" }} /> <Menu style={{ minWidth: 0, flex: "auto" }} />
</div> </div>
``` ```
## Design Token
<ComponentTokenTable component="Menu"></ComponentTokenTable>

4
components/menu/index.zh-CN.md

@ -142,3 +142,7 @@ Menu 初始化时会先全部渲染,然后根据宽度裁剪内容。当处于
<Menu style={{ minWidth: 0, flex: "auto" }} /> <Menu style={{ minWidth: 0, flex: "auto" }} />
</div> </div>
``` ```
## Design Token
<ComponentTokenTable component="Menu"></ComponentTokenTable>

13
components/menu/style/index.tsx

@ -442,10 +442,15 @@ export default (prefixCls: string, injectStyle: boolean): UseComponentStyleResul
return []; return [];
} }
const { colorBgElevated, colorPrimary, colorError, colorErrorHover, colorTextLightSolid } = const {
token; colorBgElevated,
colorPrimary,
const { controlHeightLG, fontSize } = token; colorError,
colorErrorHover,
colorTextLightSolid,
controlHeightLG,
fontSize,
} = token;
const menuArrowSize = (fontSize / 7) * 5; const menuArrowSize = (fontSize / 7) * 5;

2
components/menu/style/theme.tsx

@ -46,7 +46,7 @@ const getThemeStyle = (token: MenuToken, themeSuffix: string): CSSInterpolation
} = token; } = token;
return { return {
[`${componentCls}-${themeSuffix}`]: { [`${componentCls}-${themeSuffix}, ${componentCls}-${themeSuffix} > ${componentCls}`]: {
color: colorItemText, color: colorItemText,
background: colorItemBg, background: colorItemBg,

17
components/modal/__tests__/hook.test.tsx

@ -350,4 +350,21 @@ describe('Modal.hook', () => {
expect(document.body.querySelector('.bamboo')?.textContent).toEqual('好的'); expect(document.body.querySelector('.bamboo')?.textContent).toEqual('好的');
}); });
it('it should call forwarded afterClose', () => {
const afterClose = jest.fn();
const Demo = () => {
const [modal, contextHolder] = Modal.useModal();
React.useEffect(() => {
modal.confirm({ title: 'Confirm', afterClose });
}, []);
return contextHolder;
};
render(<Demo />);
const btns = document.body.querySelectorAll('.ant-btn');
fireEvent.click(btns[btns.length - 1]);
expect(afterClose).toHaveBeenCalledTimes(1);
});
}); });

9
components/modal/useModal/HookModal.tsx

@ -16,7 +16,7 @@ export interface HookModalRef {
} }
const HookModal: React.ForwardRefRenderFunction<HookModalRef, HookModalProps> = ( const HookModal: React.ForwardRefRenderFunction<HookModalRef, HookModalProps> = (
{ afterClose, config }, { afterClose: hookAfterClose, config },
ref, ref,
) => { ) => {
const [open, setOpen] = React.useState(true); const [open, setOpen] = React.useState(true);
@ -26,6 +26,11 @@ const HookModal: React.ForwardRefRenderFunction<HookModalRef, HookModalProps> =
const prefixCls = getPrefixCls('modal'); const prefixCls = getPrefixCls('modal');
const rootPrefixCls = getPrefixCls(); const rootPrefixCls = getPrefixCls();
const afterClose = () => {
hookAfterClose();
innerConfig.afterClose?.();
};
const close = (...args: any[]) => { const close = (...args: any[]) => {
setOpen(false); setOpen(false);
const triggerCancel = args.some((param) => param && param.triggerCancel); const triggerCancel = args.some((param) => param && param.triggerCancel);
@ -59,7 +64,7 @@ const HookModal: React.ForwardRefRenderFunction<HookModalRef, HookModalProps> =
okText={ okText={
innerConfig.okText || (mergedOkCancel ? contextLocale?.okText : contextLocale?.justOkText) innerConfig.okText || (mergedOkCancel ? contextLocale?.okText : contextLocale?.justOkText)
} }
direction={direction} direction={innerConfig.direction || direction}
cancelText={innerConfig.cancelText || contextLocale?.cancelText} cancelText={innerConfig.cancelText || contextLocale?.cancelText}
/> />
); );

38
components/pagination/Pagination.tsx

@ -55,49 +55,51 @@ const Pagination: React.FC<PaginationProps> = ({
const mergedShowSizeChanger = showSizeChanger ?? pagination.showSizeChanger; const mergedShowSizeChanger = showSizeChanger ?? pagination.showSizeChanger;
const getIconsProps = () => { const iconsProps = React.useMemo<Record<PropertyKey, React.ReactNode>>(() => {
const ellipsis = <span className={`${prefixCls}-item-ellipsis`}></span>; const ellipsis = <span className={`${prefixCls}-item-ellipsis`}></span>;
let prevIcon = ( const prevIcon = (
<button className={`${prefixCls}-item-link`} type="button" tabIndex={-1}> <button className={`${prefixCls}-item-link`} type="button" tabIndex={-1}>
<LeftOutlined /> {direction === 'rtl' ? <RightOutlined /> : <LeftOutlined />}
</button> </button>
); );
let nextIcon = ( const nextIcon = (
<button className={`${prefixCls}-item-link`} type="button" tabIndex={-1}> <button className={`${prefixCls}-item-link`} type="button" tabIndex={-1}>
<RightOutlined /> {direction === 'rtl' ? <LeftOutlined /> : <RightOutlined />}
</button> </button>
); );
let jumpPrevIcon = ( const jumpPrevIcon = (
<a className={`${prefixCls}-item-link`}> <a className={`${prefixCls}-item-link`}>
{/* You can use transition effects in the container :) */}
<div className={`${prefixCls}-item-container`}> <div className={`${prefixCls}-item-container`}>
<DoubleLeftOutlined className={`${prefixCls}-item-link-icon`} /> {direction === 'rtl' ? (
<DoubleRightOutlined className={`${prefixCls}-item-link-icon`} />
) : (
<DoubleLeftOutlined className={`${prefixCls}-item-link-icon`} />
)}
{ellipsis} {ellipsis}
</div> </div>
</a> </a>
); );
let jumpNextIcon = ( const jumpNextIcon = (
<a className={`${prefixCls}-item-link`}> <a className={`${prefixCls}-item-link`}>
{/* You can use transition effects in the container :) */}
<div className={`${prefixCls}-item-container`}> <div className={`${prefixCls}-item-container`}>
<DoubleRightOutlined className={`${prefixCls}-item-link-icon`} /> {direction === 'rtl' ? (
<DoubleLeftOutlined className={`${prefixCls}-item-link-icon`} />
) : (
<DoubleRightOutlined className={`${prefixCls}-item-link-icon`} />
)}
{ellipsis} {ellipsis}
</div> </div>
</a> </a>
); );
// change arrows direction in right-to-left direction
if (direction === 'rtl') {
[prevIcon, nextIcon] = [nextIcon, prevIcon];
[jumpPrevIcon, jumpNextIcon] = [jumpNextIcon, jumpPrevIcon];
}
return { prevIcon, nextIcon, jumpPrevIcon, jumpNextIcon }; return { prevIcon, nextIcon, jumpPrevIcon, jumpNextIcon };
}; }, [direction, prefixCls]);
const [contextLocale] = useLocale('Pagination', enUS); const [contextLocale] = useLocale('Pagination', enUS);
const locale = { ...contextLocale, ...customLocale }; const locale = { ...contextLocale, ...customLocale };
const isSmall = size === 'small' || !!(xs && !size && responsive); const isSmall = size === 'small' || !!(xs && !size && responsive);
const selectPrefixCls = getPrefixCls('select', customizeSelectPrefixCls); const selectPrefixCls = getPrefixCls('select', customizeSelectPrefixCls);
const extendedClassName = classNames( const extendedClassName = classNames(
{ {
@ -111,7 +113,7 @@ const Pagination: React.FC<PaginationProps> = ({
return wrapSSR( return wrapSSR(
<RcPagination <RcPagination
{...getIconsProps()} {...iconsProps}
{...restProps} {...restProps}
prefixCls={prefixCls} prefixCls={prefixCls}
selectPrefixCls={selectPrefixCls} selectPrefixCls={selectPrefixCls}

4
components/pagination/index.en-US.md

@ -55,3 +55,7 @@ A long list can be divided into several pages using `Pagination`, and only one p
| total | Total number of data items | number | 0 | | | total | Total number of data items | number | 0 | |
| onChange | Called when the page number or `pageSize` is changed, and it takes the resulting page number and pageSize as its arguments | function(page, pageSize) | - | | | onChange | Called when the page number or `pageSize` is changed, and it takes the resulting page number and pageSize as its arguments | function(page, pageSize) | - | |
| onShowSizeChange | Called when `pageSize` is changed | function(current, size) | - | | | onShowSizeChange | Called when `pageSize` is changed | function(current, size) | - | |
## Design Token
<ComponentTokenTable component="Pagination"></ComponentTokenTable>

4
components/pagination/index.zh-CN.md

@ -56,3 +56,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*WM86SrBC8TsAAA
| total | 数据总数 | number | 0 | | | total | 数据总数 | number | 0 | |
| onChange | 页码或 `pageSize` 改变的回调,参数是改变后的页码及每页条数 | function(page, pageSize) | - | | | onChange | 页码或 `pageSize` 改变的回调,参数是改变后的页码及每页条数 | function(page, pageSize) | - | |
| onShowSizeChange | pageSize 变化的回调 | function(current, size) | - | | | onShowSizeChange | pageSize 变化的回调 | function(current, size) | - | |
## Design Token
<ComponentTokenTable component="Pagination"></ComponentTokenTable>

28
components/pagination/style/index.tsx

@ -5,9 +5,9 @@ import {
initInputToken, initInputToken,
type InputToken, type InputToken,
} from '../../input/style'; } from '../../input/style';
import { genFocusOutline, genFocusStyle, resetComponent } from '../../style';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { genFocusOutline, genFocusStyle, resetComponent } from '../../style';
interface PaginationToken extends InputToken<FullToken<'Pagination'>> { interface PaginationToken extends InputToken<FullToken<'Pagination'>> {
paginationItemSize: number; paginationItemSize: number;
@ -57,7 +57,16 @@ const genPaginationDisabledStyle: GenerateStyle<PaginationToken, CSSObject> = (t
[`&${componentCls}-disabled`]: { [`&${componentCls}-disabled`]: {
cursor: 'not-allowed', cursor: 'not-allowed',
[`&${componentCls}-mini`]: {
[`
&:hover ${componentCls}-item:not(${componentCls}-item-active),
&:active ${componentCls}-item:not(${componentCls}-item-active),
&:hover ${componentCls}-item-link,
&:active ${componentCls}-item-link
`]: {
backgroundColor: 'transparent',
},
},
[`${componentCls}-item`]: { [`${componentCls}-item`]: {
cursor: 'not-allowed', cursor: 'not-allowed',
@ -134,6 +143,12 @@ const genPaginationMiniStyle: GenerateStyle<PaginationToken, CSSObject> = (token
[`&${componentCls}-mini ${componentCls}-item:not(${componentCls}-item-active)`]: { [`&${componentCls}-mini ${componentCls}-item:not(${componentCls}-item-active)`]: {
backgroundColor: 'transparent', backgroundColor: 'transparent',
borderColor: 'transparent', borderColor: 'transparent',
'&:hover': {
backgroundColor: token.colorBgTextHover,
},
'&:active': {
backgroundColor: token.colorBgTextActive,
},
}, },
[`&${componentCls}-mini ${componentCls}-prev, &${componentCls}-mini ${componentCls}-next`]: { [`&${componentCls}-mini ${componentCls}-prev, &${componentCls}-mini ${componentCls}-next`]: {
@ -141,6 +156,15 @@ const genPaginationMiniStyle: GenerateStyle<PaginationToken, CSSObject> = (token
height: token.paginationItemSizeSM, height: token.paginationItemSizeSM,
margin: 0, margin: 0,
lineHeight: `${token.paginationItemSizeSM}px`, lineHeight: `${token.paginationItemSizeSM}px`,
[`&:hover ${componentCls}-item-link`]: {
backgroundColor: token.colorBgTextHover,
},
[`&:active ${componentCls}-item-link`]: {
backgroundColor: token.colorBgTextActive,
},
[`&${componentCls}-disabled:hover ${componentCls}-item-link`]: {
backgroundColor: 'transparent',
},
}, },
[` [`

4
components/select/index.en-US.md

@ -137,6 +137,10 @@ Select component to select value from options.
| key | Group key | string | - | | | key | Group key | string | - | |
| label | Group label | string \| React.Element | - | | | label | Group label | string \| React.Element | - | |
## Design Token
<ComponentTokenTable component="Select"></ComponentTokenTable>
## FAQ ## FAQ
### Why sometime search will show 2 same option when in `tags` mode? ### Why sometime search will show 2 same option when in `tags` mode?

4
components/select/index.zh-CN.md

@ -138,6 +138,10 @@ demo:
| key | Key | string | - | | | key | Key | string | - | |
| label | 组名 | string \| React.Element | - | | | label | 组名 | string \| React.Element | - | |
## Design Token
<ComponentTokenTable component="Select"></ComponentTokenTable>
## FAQ ## FAQ
### `mode="tags"` 模式下为何搜索有时会出现两个相同选项? ### `mode="tags"` 模式下为何搜索有时会出现两个相同选项?

4
components/steps/index.en-US.md

@ -79,3 +79,7 @@ A single step in the step bar.
| status | To specify the status. It will be automatically set by `current` of `Steps` if not configured. Optional values are: `wait` `process` `finish` `error` | string | `wait` | | | status | To specify the status. It will be automatically set by `current` of `Steps` if not configured. Optional values are: `wait` `process` `finish` `error` | string | `wait` | |
| subTitle | Subtitle of the step | ReactNode | - | | | subTitle | Subtitle of the step | ReactNode | - | |
| title | Title of the step | ReactNode | - | | | title | Title of the step | ReactNode | - | |
## Design Token
<ComponentTokenTable component="Steps"></ComponentTokenTable>

4
components/steps/index.zh-CN.md

@ -80,3 +80,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*cFsBQLA0b7UAAA
| status | 指定状态。当不配置该属性时,会使用 Steps 的 `current` 来自动指定状态。可选:`wait` `process` `finish` `error` | string | `wait` | | | status | 指定状态。当不配置该属性时,会使用 Steps 的 `current` 来自动指定状态。可选:`wait` `process` `finish` `error` | string | `wait` | |
| subTitle | 子标题 | ReactNode | - | | | subTitle | 子标题 | ReactNode | - | |
| title | 标题 | ReactNode | - | | | title | 标题 | ReactNode | - | |
## Design Token
<ComponentTokenTable component="Steps"></ComponentTokenTable>

1
components/style/index.ts

@ -3,7 +3,6 @@ import type { CSSObject } from '@ant-design/cssinjs';
import type { AliasToken, DerivativeToken } from '../theme/internal'; import type { AliasToken, DerivativeToken } from '../theme/internal';
export { operationUnit } from './operationUnit'; export { operationUnit } from './operationUnit';
export { genPresetColor } from './presetColor';
export { roundedArrow } from './roundedArrow'; export { roundedArrow } from './roundedArrow';
export const textEllipsis: CSSObject = { export const textEllipsis: CSSObject = {

14
components/table/InternalTable.tsx

@ -65,7 +65,7 @@ interface ChangeEventInfo<RecordType> {
filterStates: FilterState<RecordType>[]; filterStates: FilterState<RecordType>[];
sorterStates: SortState<RecordType>[]; sorterStates: SortState<RecordType>[];
resetPagination: Function; resetPagination: (current?: number, pageSize?: number) => void;
} }
/** Same as `TableProps` but we need record parent render times */ /** Same as `TableProps` but we need record parent render times */
@ -238,16 +238,16 @@ function InternalTable<RecordType extends object = any>(
}; };
if (reset) { if (reset) {
changeEventInfo.resetPagination!(); changeEventInfo.resetPagination?.();
// Reset event param // Reset event param
if (changeInfo.pagination!.current) { if (changeInfo.pagination?.current) {
changeInfo.pagination!.current = 1; changeInfo.pagination.current = 1;
} }
// Trigger pagination events // Trigger pagination events
if (pagination && pagination.onChange) { if (pagination && pagination.onChange) {
pagination.onChange(1, changeInfo.pagination!.pageSize!); pagination.onChange(1, changeInfo.pagination?.pageSize!);
} }
} }
@ -491,10 +491,10 @@ function InternalTable<RecordType extends object = any>(
bottomPaginationNode = renderPagination(defaultPosition); bottomPaginationNode = renderPagination(defaultPosition);
} }
if (topPos) { if (topPos) {
topPaginationNode = renderPagination(topPos!.toLowerCase().replace('top', '')); topPaginationNode = renderPagination(topPos.toLowerCase().replace('top', ''));
} }
if (bottomPos) { if (bottomPos) {
bottomPaginationNode = renderPagination(bottomPos!.toLowerCase().replace('bottom', '')); bottomPaginationNode = renderPagination(bottomPos.toLowerCase().replace('bottom', ''));
} }
} else { } else {
bottomPaginationNode = renderPagination(defaultPosition); bottomPaginationNode = renderPagination(defaultPosition);

6
components/table/Table.tsx

@ -25,9 +25,9 @@ function Table<RecordType extends object = any>(
const ForwardTable = React.forwardRef(Table) as any as RefTable & { const ForwardTable = React.forwardRef(Table) as any as RefTable & {
SELECTION_COLUMN: typeof SELECTION_COLUMN; SELECTION_COLUMN: typeof SELECTION_COLUMN;
EXPAND_COLUMN: typeof EXPAND_COLUMN; EXPAND_COLUMN: typeof EXPAND_COLUMN;
SELECTION_ALL: 'SELECT_ALL'; SELECTION_ALL: typeof SELECTION_ALL;
SELECTION_INVERT: 'SELECT_INVERT'; SELECTION_INVERT: typeof SELECTION_INVERT;
SELECTION_NONE: 'SELECT_NONE'; SELECTION_NONE: typeof SELECTION_NONE;
Column: typeof Column; Column: typeof Column;
ColumnGroup: typeof ColumnGroup; ColumnGroup: typeof ColumnGroup;
Summary: typeof Summary; Summary: typeof Summary;

43
components/table/__tests__/Table.filter.test.tsx

@ -2777,4 +2777,47 @@ describe('Table.filter', () => {
expect(renderedNames(container)).toEqual(['Jack']); expect(renderedNames(container)).toEqual(['Jack']);
}); });
it('changes to table data should not reset the filter dropdown state being changed by a user', () => {
const tableProps = {
key: 'stabletable',
rowKey: 'name',
dataSource: [],
columns: [
{
title: 'Name',
dataIndex: 'name',
filteredValue: [], // User is controlling filteredValue. It begins with no items checked.
filters: [{ text: 'J', value: 'J' }],
onFilter: (value: any, record: any) => record.name.includes(value),
},
],
};
const { container, rerender } = render(createTable(tableProps));
// User opens filter Dropdown.
fireEvent.click(container.querySelector('.ant-dropdown-trigger.ant-table-filter-trigger')!);
// There is one checkbox and it begins unchecked.
expect(container.querySelector<HTMLInputElement>('input[type="checkbox"]')!.checked).toEqual(
false,
);
// User checks it.
fireEvent.click(container.querySelector('input[type="checkbox"]')!);
// The checkbox is now checked.
expect(container.querySelector<HTMLInputElement>('input[type="checkbox"]')!.checked).toEqual(
true,
);
// Table data changes while the dropdown is open and a user is setting filters.
rerender(createTable({ ...tableProps, dataSource: [{ name: 'Foo' }] }));
// The checkbox is still checked.
expect(container.querySelector<HTMLInputElement>('input[type="checkbox"]')!.checked).toEqual(
true,
);
});
}); });

5
components/table/hooks/useFilter/index.tsx

@ -215,7 +215,10 @@ function useFilter<RecordType>({
FilterState<RecordType>[], FilterState<RecordType>[],
Record<string, FilterValue | null>, Record<string, FilterValue | null>,
] { ] {
const mergedColumns = getMergedColumns(rawMergedColumns || []); const mergedColumns = React.useMemo(
() => getMergedColumns(rawMergedColumns || []),
[rawMergedColumns],
);
const [filterStates, setFilterStates] = React.useState<FilterState<RecordType>[]>(() => const [filterStates, setFilterStates] = React.useState<FilterState<RecordType>[]>(() =>
collectFilterStates(mergedColumns, true), collectFilterStates(mergedColumns, true),

2
components/table/hooks/usePagination.ts

@ -31,7 +31,7 @@ function usePagination(
total: number, total: number,
onChange: (current: number, pageSize: number) => void, onChange: (current: number, pageSize: number) => void,
pagination?: TablePaginationConfig | false, pagination?: TablePaginationConfig | false,
): readonly [TablePaginationConfig, () => void] { ): readonly [TablePaginationConfig, (current?: number, pageSize?: number) => void] {
const { total: paginationTotal = 0, ...paginationObj } = const { total: paginationTotal = 0, ...paginationObj } =
pagination && typeof pagination === 'object' ? pagination : {}; pagination && typeof pagination === 'object' ? pagination : {};

12
components/tabs/style/index.ts

@ -29,7 +29,7 @@ const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
tabsCardHorizontalPadding, tabsCardHorizontalPadding,
tabsCardHeadBackground, tabsCardHeadBackground,
tabsCardGutter, tabsCardGutter,
colorSplit, colorBorderSecondary,
} = token; } = token;
return { return {
[`${componentCls}-card`]: { [`${componentCls}-card`]: {
@ -38,7 +38,7 @@ const genCardStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
margin: 0, margin: 0,
padding: tabsCardHorizontalPadding, padding: tabsCardHorizontalPadding,
background: tabsCardHeadBackground, background: tabsCardHeadBackground,
border: `${token.lineWidth}px ${token.lineType} ${colorSplit}`, border: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`,
transition: `all ${token.motionDurationSlow} ${token.motionEaseInOut}`, transition: `all ${token.motionDurationSlow} ${token.motionEaseInOut}`,
}, },
@ -226,7 +226,7 @@ const genDropdownStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
}; };
const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => { const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject => {
const { componentCls, margin, colorSplit } = token; const { componentCls, margin, colorBorderSecondary } = token;
return { return {
// ========================== Top & Bottom ========================== // ========================== Top & Bottom ==========================
[`${componentCls}-top, ${componentCls}-bottom`]: { [`${componentCls}-top, ${componentCls}-bottom`]: {
@ -245,7 +245,7 @@ const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
_skip_check_: true, _skip_check_: true,
value: 0, value: 0,
}, },
borderBottom: `${token.lineWidth}px ${token.lineType} ${colorSplit}`, borderBottom: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`,
content: "''", content: "''",
}, },
@ -714,7 +714,7 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
tabsCardGutter, tabsCardGutter,
tabsHoverColor, tabsHoverColor,
tabsActiveColor, tabsActiveColor,
colorSplit, colorBorderSecondary,
} = token; } = token;
return { return {
@ -799,7 +799,7 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
}, },
padding: `0 ${token.paddingXS}px`, padding: `0 ${token.paddingXS}px`,
background: 'transparent', background: 'transparent',
border: `${token.lineWidth}px ${token.lineType} ${colorSplit}`, border: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`,
borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`, borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`,
outline: 'none', outline: 'none',
cursor: 'pointer', cursor: 'pointer',

4
components/tag/style/index.ts

@ -1,9 +1,9 @@
import type { CSSInterpolation } from '@ant-design/cssinjs'; import type { CSSInterpolation } from '@ant-design/cssinjs';
import type React from 'react'; import type React from 'react';
import capitalize from '../../_util/capitalize'; import capitalize from '../../_util/capitalize';
import { genPresetColor, resetComponent } from '../../style'; import { resetComponent } from '../../style';
import type { FullToken } from '../../theme/internal'; import type { FullToken } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, genPresetColor, mergeToken } from '../../theme/internal';
export interface ComponentToken {} export interface ComponentToken {}

10
components/theme/interface/maps/colors.ts

@ -21,18 +21,21 @@ export interface ColorNeutralMapToken {
/** /**
* @nameZH * @nameZH
* @desc Label Menu * @desc Label Menu
* @descEN The second level of text color is generally used in scenarios where text color is not emphasized, such as label text, menu text selection state, etc.
*/ */
colorTextSecondary: string; colorTextSecondary: string;
/** /**
* @nameZH * @nameZH
* @desc * @desc
* @descEN The third level of text color is generally used for descriptive text, such as form supplementary explanation text, list descriptive text, etc.
*/ */
colorTextTertiary: string; colorTextTertiary: string;
/** /**
* @nameZH * @nameZH
* @desc * @desc
* @descEN The fourth level of text color is the lightest text color, such as form input prompt text, disabled color text, etc.
*/ */
colorTextQuaternary: string; colorTextQuaternary: string;
@ -59,24 +62,28 @@ export interface ColorNeutralMapToken {
/** /**
* @nameZH * @nameZH
* @desc Slider hover * @desc Slider hover
* @descEN The darkest fill color is used to distinguish between the second and third level of fill color, and is currently only used in the hover effect of Slider.
*/ */
colorFill: string; colorFill: string;
/** /**
* @nameZH * @nameZH
* @desc RateSkeleton Hover Table * @desc RateSkeleton Hover Table
* @descEN The second level of fill color can outline the shape of the element more clearly, such as Rate, Skeleton, etc. It can also be used as the Hover state of the third level of fill color, such as Table, etc.
*/ */
colorFillSecondary: string; colorFillSecondary: string;
/** /**
* @nameZH * @nameZH
* @desc SliderSegmented 使 * @desc SliderSegmented 使
* @descEN The third level of fill color is used to outline the shape of the element, such as Slider, Segmented, etc. If there is no emphasis requirement, it is recommended to use the third level of fill color as the default fill color.
*/ */
colorFillTertiary: string; colorFillTertiary: string;
/** /**
* @nameZH * @nameZH
* @desc * @desc
* @descEN The weakest level of fill color is suitable for color blocks that are not easy to attract attention, such as zebra stripes, color blocks that distinguish boundaries, etc.
*/ */
colorFillQuaternary: string; colorFillQuaternary: string;
@ -85,6 +92,7 @@ export interface ColorNeutralMapToken {
/** /**
* @nameZH * @nameZH
* @desc B1 使 token * @desc B1 使 token
* @descEN This color is used for the background color of the overall layout of the page. This token will only be used when it is necessary to be at the B1 visual level in the page. Other usages are wrong.
*/ */
colorBgLayout: string; colorBgLayout: string;
@ -98,12 +106,14 @@ export interface ColorNeutralMapToken {
/** /**
* @nameZH * @nameZH
* @desc token `colorBgContainer` * @desc token `colorBgContainer`
* @descEN Container background color of the popup layer, in dark mode the color value of this token will be a little brighter than `colorBgContainer`. E.g: modal, pop-up, menu, etc.
*/ */
colorBgElevated: string; colorBgElevated: string;
/** /**
* @nameZH * @nameZH
* @desc Tooltip * @desc Tooltip
* @descEN This color is used to draw the user's strong attention to the background color, and is currently only used in the background color of Tooltip.
*/ */
colorBgSpotlight: string; colorBgSpotlight: string;
} }

2
components/theme/interface/maps/style.ts

@ -36,6 +36,8 @@ export interface StyleMapToken {
* @nameZH * @nameZH
* @nameEN Outer Border Radius * @nameEN Outer Border Radius
* @default 4 * @default 4
* @desc
* @descEN Outer border radius
*/ */
borderRadiusOuter: number; borderRadiusOuter: number;
} }

5
components/theme/interface/seeds.ts

@ -69,6 +69,7 @@ export interface SeedToken extends PresetColorType {
* @nameZH * @nameZH
* @nameEN Font family for default text * @nameEN Font family for default text
* @desc Ant Design 使 * @desc Ant Design 使
* @descEN The font family of Ant Design prioritizes the default interface font of the system, and provides a set of alternative font libraries that are suitable for screen display to maintain the readability and readability of the font under different platforms and browsers, reflecting the friendly, stable and professional characteristics.
*/ */
fontFamily: string; fontFamily: string;
@ -76,6 +77,7 @@ export interface SeedToken extends PresetColorType {
* @nameZH * @nameZH
* @nameEN Font family for code text * @nameEN Font family for code text
* @desc Typography codepre kbd * @desc Typography codepre kbd
* @descEN Code font, used for code, pre and kbd elements in Typography
*/ */
fontFamilyCode: string; fontFamilyCode: string;
@ -138,6 +140,8 @@ export interface SeedToken extends PresetColorType {
/** /**
* @nameZH * @nameZH
* @desc
* @descEN The size of the component arrow
*/ */
sizePopupArrow: number; sizePopupArrow: number;
@ -251,6 +255,7 @@ export interface SeedToken extends PresetColorType {
* @nameZH 线 * @nameZH 线
* @nameEN Wireframe Style * @nameEN Wireframe Style
* @desc 线使 V4 * @desc 线使 V4
* @descEN Used to change the visual effect of the component to wireframe, if you need to use the V4 effect, you need to enable the configuration item
* @default false * @default false
*/ */
wireframe: boolean; wireframe: boolean;

2
components/theme/internal.ts

@ -18,6 +18,7 @@ import formatToken from './util/alias';
import type { FullToken } from './util/genComponentStyleHook'; import type { FullToken } from './util/genComponentStyleHook';
import genComponentStyleHook from './util/genComponentStyleHook'; import genComponentStyleHook from './util/genComponentStyleHook';
import statisticToken, { merge as mergeToken, statistic } from './util/statistic'; import statisticToken, { merge as mergeToken, statistic } from './util/statistic';
import genPresetColor from './util/genPresetColor';
const defaultTheme = createTheme(defaultDerivative); const defaultTheme = createTheme(defaultDerivative);
@ -31,6 +32,7 @@ export {
// hooks // hooks
useStyleRegister, useStyleRegister,
genComponentStyleHook, genComponentStyleHook,
genPresetColor,
}; };
export type { export type {
SeedToken, SeedToken,

75
components/theme/util/genComponentStyleHook.ts

@ -2,11 +2,11 @@
import type { CSSInterpolation } from '@ant-design/cssinjs'; import type { CSSInterpolation } from '@ant-design/cssinjs';
import { useStyleRegister } from '@ant-design/cssinjs'; import { useStyleRegister } from '@ant-design/cssinjs';
import { useContext } from 'react'; import { useContext } from 'react';
import { genCommonStyle, genLinkStyle } from '../../style';
import { ConfigContext } from '../../config-provider/context'; import { ConfigContext } from '../../config-provider/context';
import { genCommonStyle, genLinkStyle } from '../../style';
import type { ComponentTokenMap, GlobalToken } from '../interface';
import type { UseComponentStyleResult } from '../internal'; import type { UseComponentStyleResult } from '../internal';
import { mergeToken, statisticToken, useToken } from '../internal'; import { mergeToken, statisticToken, useToken } from '../internal';
import type { ComponentTokenMap, GlobalToken } from '../interface';
export type OverrideTokenWithoutDerivative = ComponentTokenMap; export type OverrideTokenWithoutDerivative = ComponentTokenMap;
export type OverrideComponent = keyof OverrideTokenWithoutDerivative; export type OverrideComponent = keyof OverrideTokenWithoutDerivative;
@ -44,11 +44,19 @@ export default function genComponentStyleHook<ComponentName extends OverrideComp
) { ) {
return (prefixCls: string): UseComponentStyleResult => { return (prefixCls: string): UseComponentStyleResult => {
const [theme, token, hashId] = useToken(); const [theme, token, hashId] = useToken();
const { getPrefixCls, iconPrefixCls } = useContext(ConfigContext); const { getPrefixCls, iconPrefixCls, csp } = useContext(ConfigContext);
const rootPrefixCls = getPrefixCls(); const rootPrefixCls = getPrefixCls();
// Shared config
const sharedConfig: Omit<Parameters<typeof useStyleRegister>[0], 'path'> = {
theme,
token,
hashId,
nonce: () => csp?.nonce!,
};
// Generate style for all a tags in antd component. // Generate style for all a tags in antd component.
useStyleRegister({ theme, token, hashId, path: ['Shared', rootPrefixCls] }, () => [ useStyleRegister({ ...sharedConfig, path: ['Shared', rootPrefixCls] }, () => [
{ {
// Link // Link
'&': genLinkStyle(token), '&': genLinkStyle(token),
@ -56,40 +64,37 @@ export default function genComponentStyleHook<ComponentName extends OverrideComp
]); ]);
return [ return [
useStyleRegister( useStyleRegister({ ...sharedConfig, path: [component, prefixCls, iconPrefixCls] }, () => {
{ theme, token, hashId, path: [component, prefixCls, iconPrefixCls] }, const { token: proxyToken, flush } = statisticToken(token);
() => {
const { token: proxyToken, flush } = statisticToken(token);
const defaultComponentToken = const defaultComponentToken =
typeof getDefaultToken === 'function' ? getDefaultToken(proxyToken) : getDefaultToken; typeof getDefaultToken === 'function' ? getDefaultToken(proxyToken) : getDefaultToken;
const mergedComponentToken = { ...defaultComponentToken, ...token[component] }; const mergedComponentToken = { ...defaultComponentToken, ...token[component] };
const componentCls = `.${prefixCls}`; const componentCls = `.${prefixCls}`;
const mergedToken = mergeToken< const mergedToken = mergeToken<
TokenWithCommonCls<GlobalTokenWithComponent<OverrideComponent>> TokenWithCommonCls<GlobalTokenWithComponent<OverrideComponent>>
>( >(
proxyToken, proxyToken,
{ {
componentCls, componentCls,
prefixCls,
iconCls: `.${iconPrefixCls}`,
antCls: `.${rootPrefixCls}`,
},
mergedComponentToken,
);
const styleInterpolation = styleFn(mergedToken as unknown as FullToken<ComponentName>, {
hashId,
prefixCls, prefixCls,
rootPrefixCls, iconCls: `.${iconPrefixCls}`,
iconPrefixCls, antCls: `.${rootPrefixCls}`,
overrideComponentToken: token[component], },
}); mergedComponentToken,
flush(component, mergedComponentToken); );
return [genCommonStyle(token, prefixCls), styleInterpolation];
}, const styleInterpolation = styleFn(mergedToken as unknown as FullToken<ComponentName>, {
), hashId,
prefixCls,
rootPrefixCls,
iconPrefixCls,
overrideComponentToken: token[component],
});
flush(component, mergedComponentToken);
return [genCommonStyle(token, prefixCls), styleInterpolation];
}),
hashId, hashId,
]; ];
}; };

8
components/style/presetColor.tsx → components/theme/util/genPresetColor.tsx

@ -1,8 +1,8 @@
/* eslint-disable import/prefer-default-export */ /* eslint-disable import/prefer-default-export */
import type { CSSObject } from '@ant-design/cssinjs'; import type { CSSObject } from '@ant-design/cssinjs';
import type { AliasToken, PresetColorKey } from '../theme/internal'; import type { AliasToken, PresetColorKey } from '../internal';
import { PresetColors } from '../theme/internal'; import { PresetColors } from '../interface';
import type { TokenWithCommonCls } from '../theme/util/genComponentStyleHook'; import type { TokenWithCommonCls } from './genComponentStyleHook';
interface CalcColor { interface CalcColor {
/** token[`${colorKey}-1`] */ /** token[`${colorKey}-1`] */
@ -17,7 +17,7 @@ interface CalcColor {
type GenCSS = (colorKey: PresetColorKey, calcColor: CalcColor) => CSSObject; type GenCSS = (colorKey: PresetColorKey, calcColor: CalcColor) => CSSObject;
export function genPresetColor<Token extends TokenWithCommonCls<AliasToken>>( export default function genPresetColor<Token extends TokenWithCommonCls<AliasToken>>(
token: Token, token: Token,
genCss: GenCSS, genCss: GenCSS,
): CSSObject { ): CSSObject {

4
components/tooltip/style/index.ts

@ -1,8 +1,8 @@
import { genPresetColor, resetComponent } from '../../style'; import { resetComponent } from '../../style';
import { initZoomMotion } from '../../style/motion'; import { initZoomMotion } from '../../style/motion';
import getArrowStyle, { MAX_VERTICAL_CONTENT_RADIUS } from '../../style/placementArrow'; import getArrowStyle, { MAX_VERTICAL_CONTENT_RADIUS } from '../../style/placementArrow';
import type { FullToken, GenerateStyle, UseComponentStyleResult } from '../../theme/internal'; import type { FullToken, GenerateStyle, UseComponentStyleResult } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken, genPresetColor } from '../../theme/internal';
export interface ComponentToken { export interface ComponentToken {
zIndexPopup: number; zIndexPopup: number;

7
components/tree-select/index.tsx

@ -66,7 +66,10 @@ export interface TreeSelectProps<
rootClassName?: string; rootClassName?: string;
} }
const InternalTreeSelect = <OptionType extends BaseOptionType | DefaultOptionType = BaseOptionType>( const InternalTreeSelect = <
ValueType = any,
OptionType extends BaseOptionType | DefaultOptionType = BaseOptionType,
>(
{ {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
size: customizeSize, size: customizeSize,
@ -92,7 +95,7 @@ const InternalTreeSelect = <OptionType extends BaseOptionType | DefaultOptionTyp
showArrow, showArrow,
treeExpandAction, treeExpandAction,
...props ...props
}: TreeSelectProps<OptionType>, }: TreeSelectProps<ValueType, OptionType>,
ref: React.Ref<BaseSelectRef>, ref: React.Ref<BaseSelectRef>,
) => { ) => {
const { const {

4
components/typography/index.en-US.md

@ -152,6 +152,10 @@ Basic text writing, including headings, body text, lists, and more.
| onEllipsis | Called when enter or leave ellipsis state | function(ellipsis) | - | 4.2.0 | | onEllipsis | Called when enter or leave ellipsis state | function(ellipsis) | - | 4.2.0 |
| onExpand | Called when expand content | function(event) | - | | | onExpand | Called when expand content | function(event) | - | |
## Design Token
<ComponentTokenTable component="Typography"></ComponentTokenTable>
## FAQ ## FAQ
### How to use Typography.Link in react-router? ### How to use Typography.Link in react-router?

4
components/typography/index.zh-CN.md

@ -153,6 +153,10 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LT2jR41Uj2EAAA
| onEllipsis | 触发省略时的回调 | function(ellipsis) | - | 4.2.0 | | onEllipsis | 触发省略时的回调 | function(ellipsis) | - | 4.2.0 |
| onExpand | 点击展开时的回调 | function(event) | - | | | onExpand | 点击展开时的回调 | function(event) | - | |
## Design Token
<ComponentTokenTable component="Typography"></ComponentTokenTable>
## FAQ ## FAQ
### Typography.Link 如何与 react-router 库集成? ### Typography.Link 如何与 react-router 库集成?

6
package.json

@ -1,6 +1,6 @@
{ {
"name": "antd", "name": "antd",
"version": "5.3.2", "version": "5.3.3",
"description": "An enterprise-class UI design language and React components implementation", "description": "An enterprise-class UI design language and React components implementation",
"title": "Ant Design", "title": "Ant Design",
"keywords": [ "keywords": [
@ -108,7 +108,7 @@
], ],
"dependencies": { "dependencies": {
"@ant-design/colors": "^7.0.0", "@ant-design/colors": "^7.0.0",
"@ant-design/cssinjs": "^1.5.6", "@ant-design/cssinjs": "^1.7.1",
"@ant-design/icons": "^5.0.0", "@ant-design/icons": "^5.0.0",
"@ant-design/react-slick": "~1.0.0", "@ant-design/react-slick": "~1.0.0",
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
@ -131,7 +131,7 @@
"rc-input": "~1.0.4", "rc-input": "~1.0.4",
"rc-input-number": "~7.4.0", "rc-input-number": "~7.4.0",
"rc-mentions": "~2.2.0", "rc-mentions": "~2.2.0",
"rc-menu": "~9.8.2", "rc-menu": "~9.8.3",
"rc-motion": "^2.6.1", "rc-motion": "^2.6.1",
"rc-notification": "~5.0.0", "rc-notification": "~5.0.0",
"rc-pagination": "~3.3.1", "rc-pagination": "~3.3.1",

2
typings/custom-typings.d.ts

@ -8,8 +8,6 @@ declare module '*.svg' {
export default src; export default src;
} }
declare module 'rc-pagination/*';
declare module 'rc-util*'; declare module 'rc-util*';
declare module 'jsonml.js/*'; declare module 'jsonml.js/*';

Loading…
Cancel
Save