mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-18 15:22:28 +08:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
996ba5159b | ||
|
|
6f4dc771f8 | ||
|
|
c5fd0eda1b | ||
|
|
172093048f | ||
|
|
545e431a74 | ||
|
|
eb720d3f84 | ||
|
|
7ffacd6051 | ||
|
|
5edc935d94 | ||
|
|
a93767562a | ||
|
|
7368b05b05 | ||
|
|
e5c777e58d | ||
|
|
367a64676f | ||
|
|
b03f7bd6c3 | ||
|
|
3103d388c8 | ||
|
|
1d437534a0 | ||
|
|
b9a7317c28 | ||
|
|
dc317d1c14 | ||
|
|
ac022b1d15 | ||
|
|
d83e5fdd0a | ||
|
|
bcd58bdc12 | ||
|
|
cf156b9c4a | ||
|
|
3779fec2cf | ||
|
|
ec81e9418e | ||
|
|
a64056eb8a | ||
|
|
0fc27299d5 | ||
|
|
cb2fc79a5f | ||
|
|
b5d7670391 | ||
|
|
4ee9253467 | ||
|
|
d7d2cefe21 | ||
|
|
f77a9ac402 | ||
|
|
e5c4092509 | ||
|
|
eb557abeaa | ||
|
|
c56a434cca | ||
|
|
69a5be7c40 | ||
|
|
f6683ebfc3 | ||
|
|
f434440bd1 | ||
|
|
d5097a88e9 | ||
|
|
c1f5f19001 | ||
|
|
a26e5036e6 | ||
|
|
1f7d200f70 | ||
|
|
c234b33c19 | ||
|
|
9914ceb6be | ||
|
|
a8ce4d404a | ||
|
|
b69c3351b1 | ||
|
|
a21c66a97a |
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"sandboxes": ["antd-reproduction-template-6e93z"]
|
||||
"sandboxes": ["antd-reproduction-template-6e93z"],
|
||||
"node": "14"
|
||||
}
|
||||
|
||||
54
.github/workflows/codeql-analysis.yml
vendored
54
.github/workflows/codeql-analysis.yml
vendored
@@ -1,54 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, 0.12-stable, 1.x-stable, 2.x-stable, 3.x-stable, feature, gh-pages]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '0 13 * * 1'
|
||||
|
||||
jobs:
|
||||
analyse:
|
||||
name: Analyse
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
22
.github/workflows/pr-welcome.yml
vendored
Normal file
22
.github/workflows/pr-welcome.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: PR Welcome
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, reopened]
|
||||
|
||||
jobs:
|
||||
welcome:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions-cool/pr-welcome@v1.1.0
|
||||
with:
|
||||
token: ${{ secrets.ANT_BOT_TOKEN }}
|
||||
refuse-issue-label: '🎱 Collaborate PR only'
|
||||
need-creator-authority: 'write'
|
||||
comment: |
|
||||
Hi @${{ github.event.pull_request.user.login }}. The issue mentioned in this PR needs to be confirmed with the designer or core team. This PR is temporarily not accepted. Thank you again for your contribution! 😊
|
||||
|
||||
你好 @${{ github.event.pull_request.user.login }}。这个 PR 提及的 issue 需要和设计师或核心团队进行确认,暂时不接受 PR,再次感谢你的贡献!😊
|
||||
emoji: 'heart'
|
||||
pr-emoji: 'heart'
|
||||
close: true
|
||||
19
.github/workflows/verify-package-version.yml
vendored
Normal file
19
.github/workflows/verify-package-version.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Verify Package Version
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize, ready_for_review]
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
if: contains(github.event.pull_request.title, 'changelog') || contains(github.event.pull_request.title, 'release')
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: verify-version
|
||||
uses: actions-cool/verify-package-version@v1.1.1
|
||||
with:
|
||||
token: ${{ secrets.ANT_BOT_TOKEN }}
|
||||
title-include-content: 'docs'
|
||||
title-include-version: true
|
||||
open-comment: true
|
||||
@@ -15,8 +15,39 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.12.3
|
||||
|
||||
`2021-02-10`
|
||||
|
||||
- 🛠 Refactor Drawer with React hooks. [#29229](https://github.com/ant-design/ant-design/pull/29229)
|
||||
- 🐞 Fix Table pagination not disappear when `pagination.position` is `['none', 'none']`. [#29256](https://github.com/ant-design/ant-design/pull/29256) [@mumiao](https://github.com/mumiao)
|
||||
- 🐞 Fix TextArea `showCount` should not be interactive. [#29245](https://github.com/ant-design/ant-design/pull/29245)
|
||||
- 🐞 Fix abnormal background color for `multiple` and `disabled` Select in dark. [#29242](https://github.com/ant-design/ant-design/pull/29242)
|
||||
- ⚡️ Optimize Slider align performance of tooltip. [#29308](https://github.com/ant-design/ant-design/pull/29308) [@kerm1it](https://github.com/kerm1it)
|
||||
- ⚡️ Upgrade `@ant-design/colors` to 6.x to reduce gzipped bundle size `1KB`. [#29307](https://github.com/ant-design/ant-design/pull/29307) [@07akioni](https://github.com/07akioni)
|
||||
- 🇷🇺 Add `ru_RU` locale text for Image. [#29271](https://github.com/ant-design/ant-design/pull/29271) [@mumiao](https://github.com/mumiao)
|
||||
- 🇮🇷 Add `fa_IR` locale text for DatePicker, Form, Table, TimePicker and Transfer. [#29232](https://github.com/ant-design/ant-design/pull/29232) [@amiralitaheri](https://github.com/amiralitaheri)
|
||||
- TypeScript
|
||||
- 🤖 Fix type definition for Table FilterDropdownProps `confirm` parameter. [#29241](https://github.com/ant-design/ant-design/pull/29241) [@mumiao](https://github.com/mumiao)
|
||||
|
||||
## 4.12.2
|
||||
|
||||
`2021-02-04`
|
||||
|
||||
- 💄 Make Table expand icon and checkbox same size and aligned. [#29214](https://github.com/ant-design/ant-design/pull/29214)
|
||||
- 🐞 Fix List with `gutter` makes column break line. [#29211](https://github.com/ant-design/ant-design/pull/29211)
|
||||
|
||||
## 4.12.1
|
||||
|
||||
`2021-02-03`
|
||||
|
||||
- 🐞 Fix antd crash when load before page ready. [#29202](https://github.com/ant-design/ant-design/pull/29202)
|
||||
- 🐞 Fix Table pagination `current` change logic when `pageSize` changes. [#29184](https://github.com/ant-design/ant-design/pull/29184)
|
||||
|
||||
## 4.12.0
|
||||
|
||||
`2021-02-03`
|
||||
|
||||
- 🆕 Image.PreviewGroup add `current` prop. [#29153](https://github.com/ant-design/ant-design/pull/29153)
|
||||
- InputNumber
|
||||
- 🆕 InputNumber support `bordered` prop. [#29105](https://github.com/ant-design/ant-design/pull/29105)
|
||||
@@ -38,6 +69,12 @@ timeline: true
|
||||
- Less
|
||||
- 💄 Add less variable `@progress-info-text-color`. [#28981](https://github.com/ant-design/ant-design/pull/28981) [@yuxuan](https://github.com/yuxuan)
|
||||
|
||||
## 4.11.3
|
||||
|
||||
`2021-02-03`
|
||||
|
||||
Wrong release is the same as `4.12.0`, if you need to lock the version, you need to lock it to `4.11.2`.
|
||||
|
||||
## 4.11.2
|
||||
|
||||
`2021-01-26`
|
||||
|
||||
@@ -15,8 +15,39 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.12.3
|
||||
|
||||
`2021-02-10`
|
||||
|
||||
- 🛠 使用 React hooks 重构 Drawer。[#29229](https://github.com/ant-design/ant-design/pull/29229)
|
||||
- 🐞 修复 Table 的 `pagination. position` 为 `['none', 'none']`时分页器仍然展示的问题。[#29256](https://github.com/ant-design/ant-design/pull/29256) [@mumiao](https://github.com/mumiao)
|
||||
- 🐞 修复 TextArea `showCount` 字数会遮挡 Form.Item `extra` 的问题。[#29245](https://github.com/ant-design/ant-design/pull/29245)
|
||||
- 🐞 修复多选 Select 在暗黑模式下禁用的背景颜色异常的问题。[#29242](https://github.com/ant-design/ant-design/pull/29242)
|
||||
- ⚡️ 优化 Slider 提示的对齐性能。[#29308](https://github.com/ant-design/ant-design/pull/29308) [@kerm1it](https://github.com/kerm1it)
|
||||
- ⚡️ 升级 `@ant-design/colors` 依赖到 6.x,减少 gzipped 包体积 `1KB`。[#29307](https://github.com/ant-design/ant-design/pull/29307) [@07akioni](https://github.com/07akioni)
|
||||
- 🇷🇺 为 Image 组件添加 `ru_RU` 俄语翻译。[#29271](https://github.com/ant-design/ant-design/pull/29271) [@mumiao](https://github.com/mumiao)
|
||||
- 🇮🇷 为 DatePicker、Form、Table、TimePicker 和 Transfer 组件添加 `fa_IR` 波斯语翻译。[#29232](https://github.com/ant-design/ant-design/pull/29232) [@amiralitaheri](https://github.com/amiralitaheri)
|
||||
- TypeScript
|
||||
- 🤖 修复 Table FilterDropdownProps 的 `confirm` 入参为可选类型。[#29241](https://github.com/ant-design/ant-design/pull/29241) [@mumiao](https://github.com/mumiao)
|
||||
|
||||
## 4.12.2
|
||||
|
||||
`2021-02-04`
|
||||
|
||||
- 💄 调整 Table 展开图标和选择框的大小一致并对齐。[#29214](https://github.com/ant-design/ant-design/pull/29214)
|
||||
- 🐞 修复 List 配置 `gutter` 时列会折行的问题。[#29211](https://github.com/ant-design/ant-design/pull/29211)
|
||||
|
||||
## 4.12.1
|
||||
|
||||
`2021-02-03`
|
||||
|
||||
- 🐞 修复 antd 在页面加载之前载入导致的页面崩溃问题。[#29202](https://github.com/ant-design/ant-design/pull/29202)
|
||||
- 🐞 修正 Table 改变 `pageSize` 重置 `current` 的逻辑,现在若超出会重置到最大页数。[#29184](https://github.com/ant-design/ant-design/pull/29184)
|
||||
|
||||
## 4.12.0
|
||||
|
||||
`2021-02-03`
|
||||
|
||||
- 🆕 Image.PreviewGroup 添加 `current` 属性支持受控模式。[#29153](https://github.com/ant-design/ant-design/pull/29153)
|
||||
- InputNumber
|
||||
- 🆕 InputNumber 支持 `bordered` 属性。[#29105](https://github.com/ant-design/ant-design/pull/29105)
|
||||
@@ -38,6 +69,12 @@ timeline: true
|
||||
- Less
|
||||
- 💄 增加 less 变量 `@progress-info-text-color`。 [#28981](https://github.com/ant-design/ant-design/pull/28981) [@yuxuan](https://github.com/yuxuan)
|
||||
|
||||
## 4.11.3
|
||||
|
||||
`2021-02-03`
|
||||
|
||||
错误的发布,与 `4.12.0` 相同,如果需要锁定版本,需要锁定到 `4.11.2`。
|
||||
|
||||
## 4.11.2
|
||||
|
||||
`2021-01-26`
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import getDataOrAriaProps from '../getDataOrAriaProps';
|
||||
import Wave from '../wave';
|
||||
import TransButton from '../transButton';
|
||||
import { isStyleSupport, isFlexSupported } from '../styleChecker';
|
||||
import { isStyleSupport } from '../styleChecker';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
describe('Test utils function', () => {
|
||||
@@ -208,10 +208,6 @@ describe('Test utils function', () => {
|
||||
});
|
||||
|
||||
describe('style', () => {
|
||||
it('isFlexSupported', () => {
|
||||
expect(isFlexSupported).toBe(true);
|
||||
});
|
||||
|
||||
it('isStyleSupport', () => {
|
||||
expect(isStyleSupport('color')).toBe(true);
|
||||
expect(isStyleSupport('not-existed')).toBe(false);
|
||||
|
||||
@@ -12,13 +12,16 @@ export const isStyleSupport = (styleName: string | Array<string>): boolean => {
|
||||
return false;
|
||||
};
|
||||
|
||||
export const isFlexSupported = isStyleSupport(['flex', 'webkitFlex', 'Flex', 'msFlex']);
|
||||
|
||||
export const isFlexGapSupported = (() => {
|
||||
let flexGapSupported: boolean | undefined;
|
||||
export const detectFlexGapSupported = () => {
|
||||
if (!canUseDocElement()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flexGapSupported !== undefined) {
|
||||
return flexGapSupported;
|
||||
}
|
||||
|
||||
// create flex container with row-gap set
|
||||
const flex = document.createElement('div');
|
||||
flex.style.display = 'flex';
|
||||
@@ -31,8 +34,8 @@ export const isFlexGapSupported = (() => {
|
||||
|
||||
// append to the DOM (needed to obtain scrollHeight)
|
||||
document.body.appendChild(flex);
|
||||
const isSupported = flex.scrollHeight === 1; // flex container should be 1px high from the row-gap
|
||||
flexGapSupported = flex.scrollHeight === 1; // flex container should be 1px high from the row-gap
|
||||
document.body.removeChild(flex);
|
||||
|
||||
return isSupported;
|
||||
})();
|
||||
return flexGapSupported;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,14 @@ import { PickerLocale } from '../generatePicker';
|
||||
const locale: PickerLocale = {
|
||||
lang: {
|
||||
placeholder: 'انتخاب تاریخ',
|
||||
yearPlaceholder: 'انتخاب سال',
|
||||
quarterPlaceholder: 'انتخاب فصل',
|
||||
monthPlaceholder: 'انتخاب ماه',
|
||||
weekPlaceholder: 'انتخاب هفته',
|
||||
rangePlaceholder: ['تاریخ شروع', 'تاریخ پایان'],
|
||||
rangeYearPlaceholder: ['سال شروع', 'سال پایان'],
|
||||
rangeMonthPlaceholder: ['ماه شروع', 'ماه پایان'],
|
||||
rangeWeekPlaceholder: ['هفته شروع', 'هفته پایان'],
|
||||
...CalendarLocale,
|
||||
},
|
||||
timePickerLocale: {
|
||||
|
||||
@@ -147,4 +147,15 @@ describe('Drawer', () => {
|
||||
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should support ref', () => {
|
||||
const ref = React.createRef();
|
||||
mount(
|
||||
<Drawer visible ref={ref} width={400}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(typeof ref.current.push).toBe('function');
|
||||
expect(typeof ref.current.pull).toBe('function');
|
||||
});
|
||||
});
|
||||
|
||||
23
components/drawer/__tests__/type.test.tsx
Normal file
23
components/drawer/__tests__/type.test.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import Drawer from '..';
|
||||
|
||||
describe('Drawer.typescript', () => {
|
||||
it('Drawer', () => {
|
||||
const onClose = jest.fn();
|
||||
const wrapper = (
|
||||
<Drawer
|
||||
title="Basic Drawer"
|
||||
placement="right"
|
||||
closable={false}
|
||||
onClose={onClose}
|
||||
visible={false}
|
||||
>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
</Drawer>
|
||||
);
|
||||
|
||||
expect(wrapper).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -3,11 +3,16 @@ import RcDrawer from 'rc-drawer';
|
||||
import getScrollBarSize from 'rc-util/lib/getScrollBarSize';
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import { ConfigContext, DirectionType } from '../config-provider';
|
||||
import { tuple } from '../_util/type';
|
||||
import useForceUpdate from '../_util/hooks/useForceUpdate';
|
||||
|
||||
const DrawerContext = React.createContext<Drawer | null>(null);
|
||||
type DrawerRef = {
|
||||
push(): void;
|
||||
pull(): void;
|
||||
};
|
||||
|
||||
const DrawerContext = React.createContext<DrawerRef | null>(null);
|
||||
|
||||
type EventType =
|
||||
| React.KeyboardEvent<HTMLDivElement>
|
||||
@@ -50,6 +55,7 @@ export interface DrawerProps {
|
||||
keyboard?: boolean;
|
||||
footer?: React.ReactNode;
|
||||
footerStyle?: React.CSSProperties;
|
||||
level?: string | string[] | null | undefined;
|
||||
}
|
||||
|
||||
export interface IDrawerState {
|
||||
@@ -61,223 +67,222 @@ interface InternalDrawerProps extends DrawerProps {
|
||||
}
|
||||
|
||||
const defaultPushState: PushState = { distance: 180 };
|
||||
class Drawer extends React.Component<InternalDrawerProps, IDrawerState> {
|
||||
static defaultProps = {
|
||||
width: 256,
|
||||
height: 256,
|
||||
closable: true,
|
||||
placement: 'right' as placementType,
|
||||
maskClosable: true,
|
||||
mask: true,
|
||||
level: null,
|
||||
keyboard: true,
|
||||
push: defaultPushState,
|
||||
};
|
||||
|
||||
readonly state = {
|
||||
push: false,
|
||||
};
|
||||
|
||||
parentDrawer: Drawer | null;
|
||||
|
||||
destroyClose: boolean;
|
||||
|
||||
public componentDidMount() {
|
||||
// fix: delete drawer in child and re-render, no push started.
|
||||
// <Drawer>{show && <Drawer />}</Drawer>
|
||||
const { visible } = this.props;
|
||||
if (visible && this.parentDrawer) {
|
||||
this.parentDrawer.push();
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidUpdate(preProps: DrawerProps) {
|
||||
const { visible } = this.props;
|
||||
if (preProps.visible !== visible && this.parentDrawer) {
|
||||
if (visible) {
|
||||
this.parentDrawer.push();
|
||||
} else {
|
||||
this.parentDrawer.pull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
// unmount drawer in child, clear push.
|
||||
if (this.parentDrawer) {
|
||||
this.parentDrawer.pull();
|
||||
this.parentDrawer = null;
|
||||
}
|
||||
}
|
||||
|
||||
push = () => {
|
||||
if (this.props.push) {
|
||||
this.setState({ push: true });
|
||||
}
|
||||
};
|
||||
|
||||
pull = () => {
|
||||
if (this.props.push) {
|
||||
this.setState({ push: false });
|
||||
}
|
||||
};
|
||||
|
||||
onDestroyTransitionEnd = () => {
|
||||
const isDestroyOnClose = this.getDestroyOnClose();
|
||||
if (!isDestroyOnClose) {
|
||||
return;
|
||||
}
|
||||
if (!this.props.visible) {
|
||||
this.destroyClose = true;
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
getDestroyOnClose = () => this.props.destroyOnClose && !this.props.visible;
|
||||
|
||||
getPushDistance = () => {
|
||||
const { push } = this.props;
|
||||
let distance: number | string;
|
||||
if (typeof push === 'boolean') {
|
||||
distance = push ? defaultPushState.distance : 0;
|
||||
} else {
|
||||
distance = push!.distance;
|
||||
}
|
||||
return parseFloat(String(distance || 0));
|
||||
};
|
||||
|
||||
// get drawer push width or height
|
||||
getPushTransform = (placement?: placementType) => {
|
||||
const distance = this.getPushDistance();
|
||||
|
||||
if (placement === 'left' || placement === 'right') {
|
||||
return `translateX(${placement === 'left' ? distance : -distance}px)`;
|
||||
}
|
||||
if (placement === 'top' || placement === 'bottom') {
|
||||
return `translateY(${placement === 'top' ? distance : -distance}px)`;
|
||||
}
|
||||
};
|
||||
|
||||
getOffsetStyle() {
|
||||
const { placement, width, height, visible, mask } = this.props;
|
||||
// https://github.com/ant-design/ant-design/issues/24287
|
||||
if (!visible && !mask) {
|
||||
return {};
|
||||
}
|
||||
const offsetStyle: any = {};
|
||||
if (placement === 'left' || placement === 'right') {
|
||||
offsetStyle.width = width;
|
||||
} else {
|
||||
offsetStyle.height = height;
|
||||
}
|
||||
return offsetStyle;
|
||||
}
|
||||
|
||||
getRcDrawerStyle = () => {
|
||||
const { zIndex, placement, mask, style } = this.props;
|
||||
const { push } = this.state;
|
||||
// 当无 mask 时,将 width 应用到外层容器上
|
||||
// 解决 https://github.com/ant-design/ant-design/issues/12401 的问题
|
||||
const offsetStyle = mask ? {} : this.getOffsetStyle();
|
||||
return {
|
||||
const Drawer = React.forwardRef<DrawerRef, InternalDrawerProps>(
|
||||
(
|
||||
{
|
||||
width = 256,
|
||||
height = 256,
|
||||
closable = true,
|
||||
placement = 'right' as placementType,
|
||||
maskClosable = true,
|
||||
mask = true,
|
||||
level = null,
|
||||
keyboard = true,
|
||||
push = defaultPushState,
|
||||
closeIcon = <CloseOutlined />,
|
||||
bodyStyle,
|
||||
drawerStyle,
|
||||
prefixCls,
|
||||
className,
|
||||
direction,
|
||||
visible,
|
||||
children,
|
||||
zIndex,
|
||||
transform: push ? this.getPushTransform(placement) : undefined,
|
||||
...offsetStyle,
|
||||
...style,
|
||||
};
|
||||
};
|
||||
destroyOnClose,
|
||||
style,
|
||||
title,
|
||||
headerStyle,
|
||||
onClose,
|
||||
footer,
|
||||
footerStyle,
|
||||
...rest
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
const [internalPush, setPush] = React.useState(false);
|
||||
const parentDrawer = React.useContext(DrawerContext);
|
||||
const destroyClose = React.useRef<boolean>(false);
|
||||
|
||||
renderHeader() {
|
||||
const { title, prefixCls, closable, headerStyle } = this.props;
|
||||
if (!title && !closable) {
|
||||
return null;
|
||||
}
|
||||
React.useEffect(() => {
|
||||
// fix: delete drawer in child and re-render, no push started.
|
||||
// <Drawer>{show && <Drawer />}</Drawer>
|
||||
if (visible && parentDrawer) {
|
||||
parentDrawer.push();
|
||||
}
|
||||
|
||||
const headerClassName = title ? `${prefixCls}-header` : `${prefixCls}-header-no-title`;
|
||||
return (
|
||||
<div className={headerClassName} style={headerStyle}>
|
||||
{title && <div className={`${prefixCls}-title`}>{title}</div>}
|
||||
{closable && this.renderCloseIcon()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return () => {
|
||||
if (parentDrawer) {
|
||||
parentDrawer.pull();
|
||||
// parentDrawer = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
renderFooter() {
|
||||
const { footer, footerStyle, prefixCls } = this.props;
|
||||
if (!footer) {
|
||||
return null;
|
||||
}
|
||||
React.useEffect(() => {
|
||||
if (parentDrawer) {
|
||||
if (visible) {
|
||||
parentDrawer.push();
|
||||
} else {
|
||||
parentDrawer.pull();
|
||||
}
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
const footerClassName = `${prefixCls}-footer`;
|
||||
return (
|
||||
<div className={footerClassName} style={footerStyle}>
|
||||
{footer}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCloseIcon() {
|
||||
const { closable, closeIcon = <CloseOutlined />, prefixCls, onClose } = this.props;
|
||||
return (
|
||||
closable && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
aria-label="Close"
|
||||
className={`${prefixCls}-close`}
|
||||
style={
|
||||
{
|
||||
'--scroll-bar': `${getScrollBarSize()}px`,
|
||||
} as any
|
||||
const operations = React.useMemo(
|
||||
() => ({
|
||||
push() {
|
||||
if (push) {
|
||||
setPush(true);
|
||||
}
|
||||
>
|
||||
{closeIcon}
|
||||
</button>
|
||||
)
|
||||
},
|
||||
pull() {
|
||||
if (push) {
|
||||
setPush(false);
|
||||
}
|
||||
},
|
||||
}),
|
||||
[push],
|
||||
);
|
||||
}
|
||||
|
||||
// render drawer body dom
|
||||
renderBody = () => {
|
||||
const { bodyStyle, drawerStyle, prefixCls, visible } = this.props;
|
||||
if (this.destroyClose && !visible) {
|
||||
return null;
|
||||
}
|
||||
this.destroyClose = false;
|
||||
React.useImperativeHandle(ref, () => operations, [operations]);
|
||||
|
||||
const containerStyle: React.CSSProperties = {};
|
||||
const isDestroyOnClose = destroyOnClose && !visible;
|
||||
|
||||
const isDestroyOnClose = this.getDestroyOnClose();
|
||||
const onDestroyTransitionEnd = () => {
|
||||
if (!isDestroyOnClose) {
|
||||
return;
|
||||
}
|
||||
if (!visible) {
|
||||
destroyClose.current = true;
|
||||
forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
if (isDestroyOnClose) {
|
||||
// Increase the opacity transition, delete children after closing.
|
||||
containerStyle.opacity = 0;
|
||||
containerStyle.transition = 'opacity .3s';
|
||||
const getOffsetStyle = () => {
|
||||
// https://github.com/ant-design/ant-design/issues/24287
|
||||
if (!visible && !mask) {
|
||||
return {};
|
||||
}
|
||||
const offsetStyle: any = {};
|
||||
if (placement === 'left' || placement === 'right') {
|
||||
offsetStyle.width = width;
|
||||
} else {
|
||||
offsetStyle.height = height;
|
||||
}
|
||||
return offsetStyle;
|
||||
};
|
||||
|
||||
const getRcDrawerStyle = () => {
|
||||
// get drawer push width or height
|
||||
const getPushTransform = (_placement?: placementType) => {
|
||||
let distance: number | string;
|
||||
if (typeof push === 'boolean') {
|
||||
distance = push ? defaultPushState.distance : 0;
|
||||
} else {
|
||||
distance = push!.distance;
|
||||
}
|
||||
distance = parseFloat(String(distance || 0));
|
||||
|
||||
if (_placement === 'left' || _placement === 'right') {
|
||||
return `translateX(${_placement === 'left' ? distance : -distance}px)`;
|
||||
}
|
||||
if (_placement === 'top' || _placement === 'bottom') {
|
||||
return `translateY(${_placement === 'top' ? distance : -distance}px)`;
|
||||
}
|
||||
};
|
||||
|
||||
// 当无 mask 时,将 width 应用到外层容器上
|
||||
// 解决 https://github.com/ant-design/ant-design/issues/12401 的问题
|
||||
const offsetStyle = mask ? {} : getOffsetStyle();
|
||||
return {
|
||||
zIndex,
|
||||
transform: internalPush ? getPushTransform(placement) : undefined,
|
||||
...offsetStyle,
|
||||
...style,
|
||||
};
|
||||
};
|
||||
|
||||
function renderCloseIcon() {
|
||||
return (
|
||||
closable && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
aria-label="Close"
|
||||
className={`${prefixCls}-close`}
|
||||
style={
|
||||
{
|
||||
'--scroll-bar': `${getScrollBarSize()}px`,
|
||||
} as any
|
||||
}
|
||||
>
|
||||
{closeIcon}
|
||||
</button>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${prefixCls}-wrapper-body`}
|
||||
style={{
|
||||
...containerStyle,
|
||||
...drawerStyle,
|
||||
}}
|
||||
onTransitionEnd={this.onDestroyTransitionEnd}
|
||||
>
|
||||
{this.renderHeader()}
|
||||
<div className={`${prefixCls}-body`} style={bodyStyle}>
|
||||
{this.props.children}
|
||||
function renderHeader() {
|
||||
if (!title && !closable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const headerClassName = title ? `${prefixCls}-header` : `${prefixCls}-header-no-title`;
|
||||
return (
|
||||
<div className={headerClassName} style={headerStyle}>
|
||||
{title && <div className={`${prefixCls}-title`}>{title}</div>}
|
||||
{closable && renderCloseIcon()}
|
||||
</div>
|
||||
{this.renderFooter()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
// render Provider for Multi-level drawer
|
||||
renderProvider = (value: Drawer) => {
|
||||
this.parentDrawer = value;
|
||||
function renderFooter() {
|
||||
if (!footer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { prefixCls, placement, className, mask, direction, visible, ...rest } = this.props;
|
||||
const footerClassName = `${prefixCls}-footer`;
|
||||
return (
|
||||
<div className={footerClassName} style={footerStyle}>
|
||||
{footer}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// render drawer body dom
|
||||
const renderBody = () => {
|
||||
if (destroyClose.current && !visible) {
|
||||
return null;
|
||||
}
|
||||
destroyClose.current = false;
|
||||
|
||||
const containerStyle: React.CSSProperties = {};
|
||||
|
||||
if (isDestroyOnClose) {
|
||||
// Increase the opacity transition, delete children after closing.
|
||||
containerStyle.opacity = 0;
|
||||
containerStyle.transition = 'opacity .3s';
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${prefixCls}-wrapper-body`}
|
||||
style={{
|
||||
...containerStyle,
|
||||
...drawerStyle,
|
||||
}}
|
||||
onTransitionEnd={onDestroyTransitionEnd}
|
||||
>
|
||||
{renderHeader()}
|
||||
<div className={`${prefixCls}-body`} style={bodyStyle}>
|
||||
{children}
|
||||
</div>
|
||||
{renderFooter()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const drawerClassName = classNames(
|
||||
{
|
||||
@@ -286,61 +291,60 @@ class Drawer extends React.Component<InternalDrawerProps, IDrawerState> {
|
||||
},
|
||||
className,
|
||||
);
|
||||
const offsetStyle = mask ? this.getOffsetStyle() : {};
|
||||
const offsetStyle = mask ? getOffsetStyle() : {};
|
||||
|
||||
return (
|
||||
<DrawerContext.Provider value={this}>
|
||||
<DrawerContext.Provider value={operations}>
|
||||
<RcDrawer
|
||||
handler={false}
|
||||
{...omit(rest, [
|
||||
'zIndex',
|
||||
'closable',
|
||||
'closeIcon',
|
||||
'destroyOnClose',
|
||||
'drawerStyle',
|
||||
'headerStyle',
|
||||
'bodyStyle',
|
||||
'footerStyle',
|
||||
'footer',
|
||||
'title',
|
||||
'push',
|
||||
'width',
|
||||
'height',
|
||||
])}
|
||||
{...{
|
||||
placement,
|
||||
prefixCls,
|
||||
maskClosable,
|
||||
level,
|
||||
keyboard,
|
||||
children,
|
||||
onClose,
|
||||
...rest,
|
||||
}}
|
||||
{...offsetStyle}
|
||||
prefixCls={prefixCls}
|
||||
open={visible}
|
||||
showMask={mask}
|
||||
placement={placement}
|
||||
style={this.getRcDrawerStyle()}
|
||||
style={getRcDrawerStyle()}
|
||||
className={drawerClassName}
|
||||
>
|
||||
{this.renderBody()}
|
||||
{renderBody()}
|
||||
</RcDrawer>
|
||||
</DrawerContext.Provider>
|
||||
);
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
render() {
|
||||
return <DrawerContext.Consumer>{this.renderProvider}</DrawerContext.Consumer>;
|
||||
}
|
||||
}
|
||||
Drawer.displayName = 'Drawer';
|
||||
|
||||
const DrawerWrapper: React.FC<DrawerProps> = props => {
|
||||
const { prefixCls: customizePrefixCls, getContainer: customizeGetContainer } = props;
|
||||
const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const DrawerWrapper: React.FC<DrawerProps> = React.forwardRef<DrawerRef, DrawerProps>(
|
||||
(props, ref) => {
|
||||
const { prefixCls: customizePrefixCls, getContainer: customizeGetContainer } = props;
|
||||
const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
|
||||
const prefixCls = getPrefixCls('drawer', customizePrefixCls);
|
||||
const getContainer =
|
||||
// 有可能为 false,所以不能直接判断
|
||||
customizeGetContainer === undefined && getPopupContainer
|
||||
? () => getPopupContainer(document.body)
|
||||
: customizeGetContainer;
|
||||
const prefixCls = getPrefixCls('drawer', customizePrefixCls);
|
||||
const getContainer =
|
||||
// 有可能为 false,所以不能直接判断
|
||||
customizeGetContainer === undefined && getPopupContainer
|
||||
? () => getPopupContainer(document.body)
|
||||
: customizeGetContainer;
|
||||
|
||||
return (
|
||||
<Drawer {...props} prefixCls={prefixCls} getContainer={getContainer} direction={direction} />
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Drawer
|
||||
{...props}
|
||||
ref={ref}
|
||||
prefixCls={prefixCls}
|
||||
getContainer={getContainer}
|
||||
direction={direction}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
DrawerWrapper.displayName = 'DrawerWrapper';
|
||||
|
||||
|
||||
@@ -316,6 +316,8 @@ type Rule = RuleConfig | ((form: FormInstance) => RuleConfig);
|
||||
| validateTrigger | Set validate trigger event. Must be the sub set of `validateTrigger` in Form.Item | string \| string\[] |
|
||||
| validator | Customize validation rule. Accept Promise as return. See [example](#components-form-demo-register) | ([rule](#Rule), value) => Promise |
|
||||
| whitespace | Failed if only has whitespace | boolean |
|
||||
| defaultField | Validate rule for all array elements, valid when `type` is `array` | [rule](#Rule) |
|
||||
| fields | Validate rule for child elements, valid when `type` is `array` or `object` | Record<string, [rule](#Rule)> |
|
||||
|
||||
## Migrate to v4
|
||||
|
||||
|
||||
@@ -315,6 +315,8 @@ type Rule = RuleConfig | ((form: FormInstance) => RuleConfig);
|
||||
| validateTrigger | 设置触发验证时机,必须是 Form.Item 的 `validateTrigger` 的子集 | string \| string\[] |
|
||||
| validator | 自定义校验,接收 Promise 作为返回值。[示例](#components-form-demo-register)参考 | ([rule](#Rule), value) => Promise |
|
||||
| whitespace | 如果字段仅包含空格则校验不通过 | boolean |
|
||||
| defaultField | 仅在 `type` 为 `array` 类型时有效,用于指定数组元素的校验规则 | [rule](#Rule) |
|
||||
| fields | 仅在 `type` 为 `array` 或 `object` 类型时有效,用于指定子元素的校验规则 | Record<string, [rule](#Rule)> |
|
||||
|
||||
## 从 v3 升级到 v4
|
||||
|
||||
|
||||
@@ -191,9 +191,7 @@
|
||||
|
||||
.@{ant-prefix}-input-textarea-show-count {
|
||||
&::after {
|
||||
position: absolute;
|
||||
bottom: -22px;
|
||||
width: 100%;
|
||||
margin-bottom: -22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as styleChecker from '../../_util/styleChecker';
|
||||
jest.mock('../../_util/styleChecker', () => ({
|
||||
canUseDocElement: () => true,
|
||||
isStyleSupport: () => true,
|
||||
isFlexGapSupported: true,
|
||||
detectFlexGapSupported: () => true,
|
||||
}));
|
||||
|
||||
describe('Grid.Gap', () => {
|
||||
@@ -20,8 +20,9 @@ describe('Grid.Gap', () => {
|
||||
|
||||
expect(wrapper.find('.ant-row').props().style).toEqual(
|
||||
expect.objectContaining({
|
||||
'--column-gap': '16px',
|
||||
'--row-gap': '8px',
|
||||
marginLeft: -8,
|
||||
rowGap: 8,
|
||||
marginRight: -8,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import RowContext from './RowContext';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { isFlexGapSupported } from '../_util/styleChecker';
|
||||
import { detectFlexGapSupported } from '../_util/styleChecker';
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/14324
|
||||
type ColSpanType = number | string;
|
||||
@@ -103,24 +103,21 @@ const Col = React.forwardRef<HTMLDivElement, ColProps>((props, ref) => {
|
||||
sizeClassObj,
|
||||
);
|
||||
|
||||
let mergedStyle: React.CSSProperties = { ...style };
|
||||
if (gutter && !isFlexGapSupported) {
|
||||
mergedStyle = {
|
||||
...(gutter[0]! > 0
|
||||
? {
|
||||
paddingLeft: gutter[0]! / 2,
|
||||
paddingRight: gutter[0]! / 2,
|
||||
}
|
||||
: {}),
|
||||
...(gutter[1]! > 0
|
||||
? {
|
||||
paddingTop: gutter[1]! / 2,
|
||||
paddingBottom: gutter[1]! / 2,
|
||||
}
|
||||
: {}),
|
||||
...mergedStyle,
|
||||
};
|
||||
const mergedStyle: React.CSSProperties = {};
|
||||
// Horizontal gutter use padding
|
||||
if (gutter && gutter[0] > 0) {
|
||||
const horizontalGutter = gutter[0] / 2;
|
||||
mergedStyle.paddingLeft = horizontalGutter;
|
||||
mergedStyle.paddingRight = horizontalGutter;
|
||||
}
|
||||
|
||||
// Vertical gutter use padding when gap not support
|
||||
if (gutter && gutter[1] > 0 && !detectFlexGapSupported()) {
|
||||
const verticalGutter = gutter[1] / 2;
|
||||
mergedStyle.paddingTop = verticalGutter;
|
||||
mergedStyle.paddingBottom = verticalGutter;
|
||||
}
|
||||
|
||||
if (flex) {
|
||||
mergedStyle.flex = parseFlex(flex);
|
||||
|
||||
@@ -132,7 +129,7 @@ const Col = React.forwardRef<HTMLDivElement, ColProps>((props, ref) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...others} style={mergedStyle} className={classes} ref={ref}>
|
||||
<div {...others} style={{ ...mergedStyle, ...style }} className={classes} ref={ref}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@ import ResponsiveObserve, {
|
||||
ScreenMap,
|
||||
responsiveArray,
|
||||
} from '../_util/responsiveObserve';
|
||||
import { isFlexGapSupported } from '../_util/styleChecker';
|
||||
import { detectFlexGapSupported } from '../_util/styleChecker';
|
||||
|
||||
const RowAligns = tuple('top', 'middle', 'bottom', 'stretch');
|
||||
const RowJustify = tuple('start', 'end', 'center', 'space-around', 'space-between');
|
||||
@@ -95,37 +95,19 @@ const Row = React.forwardRef<HTMLDivElement, RowProps>((props, ref) => {
|
||||
);
|
||||
|
||||
// Add gutter related style
|
||||
let rowStyle: React.CSSProperties & {
|
||||
'--column-gap'?: string | number;
|
||||
'--row-gap'?: string | number;
|
||||
} = {};
|
||||
const rowStyle: React.CSSProperties = {};
|
||||
const horizontalGutter = gutters[0] > 0 ? gutters[0] / -2 : undefined;
|
||||
const verticalGutter = gutters[1] > 0 ? gutters[1] / -2 : undefined;
|
||||
|
||||
if (isFlexGapSupported) {
|
||||
rowStyle = {
|
||||
'--column-gap': 0,
|
||||
'--row-gap': 0,
|
||||
};
|
||||
rowStyle.marginLeft = horizontalGutter;
|
||||
rowStyle.marginRight = horizontalGutter;
|
||||
|
||||
if (gutters[0]! > 0) {
|
||||
const gap = gutters[0];
|
||||
rowStyle.columnGap = gap;
|
||||
rowStyle['--column-gap'] = `${gap}px`;
|
||||
}
|
||||
if (gutters[1]! > 0) {
|
||||
const gap = gutters[1];
|
||||
rowStyle.rowGap = gap;
|
||||
rowStyle['--row-gap'] = `${gap}px`;
|
||||
}
|
||||
if (detectFlexGapSupported()) {
|
||||
// Set gap direct if flex gap support
|
||||
[, rowStyle.rowGap] = gutters;
|
||||
} else {
|
||||
const horizontalGutter = gutters[0]! > 0 ? gutters[0] / -2 : undefined;
|
||||
const verticalGutter = gutters[1]! > 0 ? gutters[1] / -2 : undefined;
|
||||
|
||||
rowStyle = {
|
||||
marginLeft: horizontalGutter,
|
||||
marginRight: horizontalGutter,
|
||||
marginTop: verticalGutter,
|
||||
marginBottom: verticalGutter,
|
||||
};
|
||||
rowStyle.marginTop = verticalGutter;
|
||||
rowStyle.marginBottom = verticalGutter;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
.@{ant-prefix}-col@{class}-@{index} {
|
||||
display: block;
|
||||
flex: 0 0 percentage((@index / @grid-columns));
|
||||
min-width: 0;
|
||||
max-width: percentage((@index / @grid-columns));
|
||||
max-width: calc(percentage((@index / @grid-columns)) - ~'var(--column-gap)');
|
||||
}
|
||||
.@{ant-prefix}-col@{class}-push-@{index} {
|
||||
left: percentage((@index / @grid-columns));
|
||||
|
||||
@@ -46,10 +46,11 @@
|
||||
|
||||
&-textarea {
|
||||
&-show-count::after {
|
||||
display: block;
|
||||
float: right;
|
||||
color: @text-color-secondary;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
content: attr(data-count);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/5FrZKStG_/List.svg
|
||||
| locale | 默认文案设置,目前包括空数据文案 | object | {emptyText: `暂无数据`} | |
|
||||
| pagination | 对应的 `pagination` 配置, 设置 false 不显示 | boolean \| object | false | |
|
||||
| renderItem | 当使用 dataSource 时,可以用 `renderItem` 自定义渲染列表项 | (item) => ReactNode | - | |
|
||||
| rowKey | 当 `renderItem` 自定义渲染列表项有效时,自定义每一行的 `key` 的获取方式 | ((item: T) => string) | `list-item-${index}` | |
|
||||
| size | list 的尺寸 | `default` \| `large` \| `small` | `default` | |
|
||||
| split | 是否展示分割线 | boolean | true | |
|
||||
|
||||
|
||||
@@ -70699,7 +70699,7 @@ exports[`Locale Provider should display the text as fa 1`] = `
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
0
|
||||
0 عدد
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
@@ -70891,7 +70891,7 @@ exports[`Locale Provider should display the text as fa 1`] = `
|
||||
<span
|
||||
class="ant-transfer-list-header-selected"
|
||||
>
|
||||
0
|
||||
0 عدد
|
||||
</span>
|
||||
<span
|
||||
class="ant-transfer-list-header-title"
|
||||
@@ -235109,7 +235109,7 @@ exports[`Locale Provider should display the text as ru 1`] = `
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Ok
|
||||
ОК
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
@@ -13,12 +13,18 @@ const localeValues: Locale = {
|
||||
DatePicker,
|
||||
TimePicker,
|
||||
Calendar,
|
||||
global: {
|
||||
placeholder: 'لطفاً انتخاب کنید',
|
||||
},
|
||||
Table: {
|
||||
filterTitle: 'منوی فیلتر',
|
||||
filterConfirm: 'تایید',
|
||||
filterReset: 'پاک کردن',
|
||||
filterEmptyText: 'بدون فیلتر',
|
||||
emptyText: 'بدون داده',
|
||||
selectAll: 'انتخاب صفحهی کنونی',
|
||||
selectInvert: 'معکوس کردن انتخابها در صفحه ی کنونی',
|
||||
selectNone: 'انتخاب هیچکدام',
|
||||
selectionAll: 'انتخاب همه دادهها',
|
||||
sortTitle: 'مرتب سازی',
|
||||
expand: 'باز شدن ردیف',
|
||||
@@ -39,8 +45,14 @@ const localeValues: Locale = {
|
||||
Transfer: {
|
||||
titles: ['', ''],
|
||||
searchPlaceholder: 'جستجو',
|
||||
itemUnit: '',
|
||||
itemsUnit: '',
|
||||
itemUnit: 'عدد',
|
||||
itemsUnit: 'عدد',
|
||||
remove: 'حذف',
|
||||
selectCurrent: 'انتخاب صفحه فعلی',
|
||||
removeCurrent: 'پاک کردن انتخابهای صفحه فعلی',
|
||||
selectAll: 'انتخاب همه',
|
||||
removeAll: 'پاک کردن همه انتخابها',
|
||||
selectInvert: 'معکوس کردن انتخابها در صفحه ی کنونی',
|
||||
},
|
||||
Upload: {
|
||||
uploading: 'در حال آپلود...',
|
||||
@@ -57,7 +69,7 @@ const localeValues: Locale = {
|
||||
},
|
||||
Text: {
|
||||
edit: 'ویرایش',
|
||||
copy: 'کپس',
|
||||
copy: 'کپی',
|
||||
copied: 'کپی شد',
|
||||
expand: 'توسعه',
|
||||
},
|
||||
@@ -65,6 +77,7 @@ const localeValues: Locale = {
|
||||
back: 'برگشت',
|
||||
},
|
||||
Form: {
|
||||
optional: '(اختیاری)',
|
||||
defaultValidateMessages: {
|
||||
default: 'خطا در ${label}',
|
||||
required: 'فیلد ${label} اجباریست',
|
||||
@@ -104,8 +117,8 @@ const localeValues: Locale = {
|
||||
},
|
||||
array: {
|
||||
len: 'تعداد ${label} باید ${len} باشد.',
|
||||
min: 'تعداد ${lable} حداقل باید ${min} باشد',
|
||||
max: 'تعداد ${lable} حداکثر باید ${max} باشد',
|
||||
min: 'تعداد ${label} حداقل باید ${min} باشد',
|
||||
max: 'تعداد ${label} حداکثر باید ${max} باشد',
|
||||
range: 'مقدار ${label} باید بین ${min}-${max} باشد',
|
||||
},
|
||||
pattern: {
|
||||
@@ -113,6 +126,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
},
|
||||
},
|
||||
Image: {
|
||||
preview: 'نمایش',
|
||||
},
|
||||
};
|
||||
|
||||
export default localeValues;
|
||||
|
||||
@@ -124,6 +124,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
},
|
||||
},
|
||||
Image: {
|
||||
preview: 'Превью',
|
||||
},
|
||||
};
|
||||
|
||||
export default localeValues;
|
||||
|
||||
@@ -67,7 +67,7 @@ Select component to select value from options.
|
||||
| onBlur | Called when blur | function | - | |
|
||||
| onChange | Called when select an option or input value change | function(value, option:Option \| Array<Option>) | - | |
|
||||
| onClear | Called when clear | function | - | 4.6.0 |
|
||||
| onDeselect | Called when a option is deselected, param is the selected option's value. Only called for `multiple` or `tags`, effective in multiple or tags mode only | function(string \| number \| LabeledValue) | - | |
|
||||
| onDeselect | Called when an option is deselected, param is the selected option's value. Only called for `multiple` or `tags`, effective in multiple or tags mode only | function(string \| number \| LabeledValue) | - | |
|
||||
| onDropdownVisibleChange | Called when dropdown open | function(open) | - | |
|
||||
| onFocus | Called when focus | function | - | |
|
||||
| onInputKeyDown | Called when key pressed | function | - | |
|
||||
@@ -75,7 +75,7 @@ Select component to select value from options.
|
||||
| onMouseLeave | Called when mouse leave | function | - | |
|
||||
| onPopupScroll | Called when dropdown scrolls | function | - | |
|
||||
| onSearch | Callback function that is fired when input changed | function(value: string) | - | |
|
||||
| onSelect | Called when a option is selected, the params are option's value (or key) and option instance | function(string \| number \| LabeledValue, option: Option) | - | |
|
||||
| onSelect | Called when an option is selected, the params are option's value (or key) and option instance | function(string \| number \| LabeledValue, option: Option) | - | |
|
||||
|
||||
> Note, if you find that the drop-down menu scrolls with the page, or you need to trigger Select in other popup layers, please try to use `getPopupContainer={triggerNode => triggerNode.parentElement}` to fix the drop-down popup rendering node in the parent element of the trigger .
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
background: @input-disabled-bg;
|
||||
cursor: not-allowed;
|
||||
|
||||
.@{select-prefix-cls}-multiple& {
|
||||
background: @select-multiple-disabled-background;
|
||||
}
|
||||
|
||||
input {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ const SliderTooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
|
||||
rafRef.current = raf(() => {
|
||||
innerRef.current?.forcePopupAlign();
|
||||
rafRef.current = null;
|
||||
keepAlign();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,7 +30,7 @@ const SliderTooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
|
||||
}
|
||||
|
||||
return cancelKeepAlign;
|
||||
}, [visible]);
|
||||
}, [visible, props.title]);
|
||||
|
||||
return <Tooltip ref={composeRef(innerRef, ref)} {...props} />;
|
||||
});
|
||||
|
||||
@@ -238,9 +238,10 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Controlled state in `columns` is not a good idea that makes too many code (1000+ line?) to read
|
||||
* state out and then put it back to title render. Move these code into `hooks` but still too
|
||||
* complex. We should provides Table props like `sorter` & `filter` to handle control in next big version.
|
||||
* Controlled state in `columns` is not a good idea that makes too many code (1000+ line?) to
|
||||
* read state out and then put it back to title render. Move these code into `hooks` but still
|
||||
* too complex. We should provides Table props like `sorter` & `filter` to handle control in next
|
||||
* big version.
|
||||
*/
|
||||
|
||||
// ============================ Sorter =============================
|
||||
@@ -439,18 +440,19 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
|
||||
/>
|
||||
);
|
||||
const defaultPosition = direction === 'rtl' ? 'left' : 'right';
|
||||
if (mergedPagination.position !== null && Array.isArray(mergedPagination.position)) {
|
||||
const topPos = mergedPagination.position.find(p => p.indexOf('top') !== -1);
|
||||
const bottomPos = mergedPagination.position.find(p => p.indexOf('bottom') !== -1);
|
||||
if (!topPos && !bottomPos) {
|
||||
const { position } = mergedPagination;
|
||||
if (position !== null && Array.isArray(position)) {
|
||||
const topPos = position.find(p => p.indexOf('top') !== -1);
|
||||
const bottomPos = position.find(p => p.indexOf('bottom') !== -1);
|
||||
const isDisable = position.every(p => `${p}` === 'none');
|
||||
if (!topPos && !bottomPos && !isDisable) {
|
||||
bottomPaginationNode = renderPagination(defaultPosition);
|
||||
} else {
|
||||
if (topPos) {
|
||||
topPaginationNode = renderPagination(topPos!.toLowerCase().replace('top', ''));
|
||||
}
|
||||
if (bottomPos) {
|
||||
bottomPaginationNode = renderPagination(bottomPos!.toLowerCase().replace('bottom', ''));
|
||||
}
|
||||
}
|
||||
if (topPos) {
|
||||
topPaginationNode = renderPagination(topPos!.toLowerCase().replace('top', ''));
|
||||
}
|
||||
if (bottomPos) {
|
||||
bottomPaginationNode = renderPagination(bottomPos!.toLowerCase().replace('bottom', ''));
|
||||
}
|
||||
} else {
|
||||
bottomPaginationNode = renderPagination(defaultPosition);
|
||||
|
||||
@@ -22,6 +22,11 @@ describe('Table.pagination', () => {
|
||||
{ key: 3, name: 'Jerry' },
|
||||
];
|
||||
|
||||
const longData = [];
|
||||
for (let i = 0; i < 100; i += 1) {
|
||||
longData.push({ key: i, name: `${i}` });
|
||||
}
|
||||
|
||||
const pagination = { className: 'my-page', pageSize: 2 };
|
||||
|
||||
function createTable(props) {
|
||||
@@ -192,7 +197,7 @@ describe('Table.pagination', () => {
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/24913
|
||||
it('should onChange called when pageSize change', () => {
|
||||
it('should called onChange when pageSize change', () => {
|
||||
const onChange = jest.fn();
|
||||
const onShowSizeChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
@@ -223,6 +228,91 @@ describe('Table.pagination', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/29175
|
||||
it('should change page to max page count when pageSize change without pagination.total', () => {
|
||||
const onChange = jest.fn();
|
||||
const onShowSizeChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
createTable({
|
||||
pagination: {
|
||||
current: 10,
|
||||
pageSize: 10,
|
||||
onChange,
|
||||
onShowSizeChange,
|
||||
},
|
||||
dataSource: longData,
|
||||
}),
|
||||
);
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
expect(wrapper.find('.ant-select-item-option').length).toBe(4);
|
||||
wrapper.find('.ant-select-item-option').at(1).simulate('click');
|
||||
const newPageSize = parseInt(wrapper.find('.ant-select-item-option').at(1).text(), 10);
|
||||
expect(onChange).toHaveBeenCalledWith(longData.length / newPageSize, 20);
|
||||
});
|
||||
|
||||
it('should change page to max page count when pageSize change with pagination.total', () => {
|
||||
const onChange = jest.fn();
|
||||
const onShowSizeChange = jest.fn();
|
||||
const total = 20000;
|
||||
const wrapper = mount(
|
||||
createTable({
|
||||
pagination: {
|
||||
current: total / 10,
|
||||
pageSize: 10,
|
||||
onChange,
|
||||
total,
|
||||
onShowSizeChange,
|
||||
},
|
||||
dataSource: longData,
|
||||
}),
|
||||
);
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
expect(wrapper.find('.ant-select-item-option').length).toBe(4);
|
||||
wrapper.find('.ant-select-item-option').at(1).simulate('click');
|
||||
const newPageSize = parseInt(wrapper.find('.ant-select-item-option').at(1).text(), 10);
|
||||
expect(onChange).toHaveBeenCalledWith(total / newPageSize, 20);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/29175
|
||||
it('should not change page to max page if current is not greater max page when pageSize change', () => {
|
||||
const onChange = jest.fn();
|
||||
const onShowSizeChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
createTable({
|
||||
pagination: {
|
||||
current: 4,
|
||||
pageSize: 10,
|
||||
onChange,
|
||||
onShowSizeChange,
|
||||
},
|
||||
dataSource: longData,
|
||||
}),
|
||||
);
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
expect(wrapper.find('.ant-select-item-option').length).toBe(4);
|
||||
wrapper.find('.ant-select-item-option').at(1).simulate('click');
|
||||
expect(onChange).toHaveBeenCalledWith(4, 20);
|
||||
});
|
||||
|
||||
it('should reset current to max page when data length is cut', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
createTable({
|
||||
pagination: {
|
||||
current: 10,
|
||||
pageSize: 10,
|
||||
onChange,
|
||||
},
|
||||
dataSource: longData,
|
||||
}),
|
||||
);
|
||||
expect(wrapper.find('.ant-pagination-item-active').text()).toBe('10');
|
||||
wrapper.setProps({
|
||||
dataSource: longData.filter(item => item.key < 60),
|
||||
});
|
||||
expect(wrapper.find('.ant-pagination-item-active').text()).toBe('6');
|
||||
});
|
||||
|
||||
it('specify the position of pagination', () => {
|
||||
const wrapper = mount(createTable({ pagination: { position: ['topLeft'] } }));
|
||||
expect(wrapper.find('.ant-spin-container').children()).toHaveLength(2);
|
||||
@@ -234,13 +324,17 @@ describe('Table.pagination', () => {
|
||||
expect(wrapper.find('.ant-spin-container').children()).toHaveLength(3);
|
||||
expect(wrapper.find('.ant-spin-container').childAt(0).find('.ant-pagination')).toHaveLength(1);
|
||||
expect(wrapper.find('.ant-spin-container').childAt(2).find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: ['none', 'none'] } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(0);
|
||||
wrapper.setProps({ pagination: { position: ['invalid'] } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
wrapper.setProps({ pagination: { position: ['invalid', 'invalid'] } });
|
||||
expect(wrapper.find('.ant-pagination')).toHaveLength(1);
|
||||
});
|
||||
|
||||
/**
|
||||
* `pagination` is not designed to accept `true` value, but in practice, many people assign `true`
|
||||
* to `pagination`, since they misunderstand that `pagination` can accept a boolean value.
|
||||
* `pagination` is not designed to accept `true` value, but in practice, many people assign
|
||||
* `true` to `pagination`, since they misunderstand that `pagination` can accept a boolean value.
|
||||
*/
|
||||
it('Accepts pagination as true', () => {
|
||||
const wrapper = mount(createTable({ pagination: true }));
|
||||
|
||||
@@ -68,12 +68,10 @@ export default function usePagination(
|
||||
},
|
||||
);
|
||||
|
||||
if (!paginationTotal) {
|
||||
// Reset `current` if data length changed. Only reset when paginationObj do not have total
|
||||
const maxPage = Math.ceil(total / mergedPagination.pageSize!);
|
||||
if (maxPage < mergedPagination.current!) {
|
||||
mergedPagination.current = 1;
|
||||
}
|
||||
// Reset `current` if data length or pageSize changed
|
||||
const maxPage = Math.ceil((paginationTotal || total) / mergedPagination.pageSize!);
|
||||
if (mergedPagination.current! > maxPage) {
|
||||
mergedPagination.current = maxPage;
|
||||
}
|
||||
|
||||
const refreshPagination = (current: number = 1, pageSize?: number) => {
|
||||
@@ -84,14 +82,11 @@ export default function usePagination(
|
||||
};
|
||||
|
||||
const onInternalChange: PaginationProps['onChange'] = (current, pageSize) => {
|
||||
const paginationPageSize = mergedPagination?.pageSize;
|
||||
if (pageSize && pageSize !== paginationPageSize) {
|
||||
current = 1;
|
||||
if (pagination) {
|
||||
pagination.onChange?.(current, pageSize);
|
||||
}
|
||||
if (pagination && pagination.onChange) pagination.onChange(current, pageSize);
|
||||
|
||||
refreshPagination(current, pageSize);
|
||||
onChange(current, pageSize || paginationPageSize!);
|
||||
onChange(current, pageSize || mergedPagination?.pageSize!);
|
||||
};
|
||||
|
||||
if (pagination === false) {
|
||||
|
||||
@@ -157,7 +157,7 @@ Properties for pagination.
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| position | Specify the position of `Pagination`, could be `topLeft` \| `topCenter` \| `topRight` \|`bottomLeft` \| `bottomCenter` \| `bottomRight` | Array | \[`bottomRight`] |
|
||||
| position | Specify the position of `Pagination`, could be`topLeft` \| `topCenter` \| `topRight` \|`bottomLeft` \| `bottomCenter` \| `bottomRight` | Array | \[`bottomRight`] |
|
||||
|
||||
More about pagination, please check [`Pagination`](/components/pagination/).
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ export interface FilterDropdownProps {
|
||||
prefixCls: string;
|
||||
setSelectedKeys: (selectedKeys: React.Key[]) => void;
|
||||
selectedKeys: React.Key[];
|
||||
confirm: (param: FilterConfirmProps) => void;
|
||||
confirm: (param?: FilterConfirmProps) => void;
|
||||
clearFilters?: () => void;
|
||||
filters?: ColumnFilterItem[];
|
||||
visible: boolean;
|
||||
|
||||
@@ -416,9 +416,8 @@
|
||||
display: inline-flex;
|
||||
float: left;
|
||||
box-sizing: border-box;
|
||||
|
||||
width: ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2)) * 2 + @border-width-base * 3;
|
||||
height: ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2)) * 2 + @border-width-base * 3;
|
||||
width: @expand-icon-size;
|
||||
height: @expand-icon-size;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
line-height: ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2)) * 2 + @border-width-base *
|
||||
@@ -427,8 +426,12 @@
|
||||
border: @border-width-base @border-style-base @table-border-color;
|
||||
border-radius: @border-radius-base;
|
||||
outline: none;
|
||||
transform: scale((unit(@checkbox-size) / unit(@expand-icon-size)));
|
||||
transform-origin: bottom;
|
||||
transition: all 0.3s;
|
||||
user-select: none;
|
||||
@expand-icon-size: ceil(((@font-size-sm * 1.4 - @border-width-base * 3) / 2)) * 2 +
|
||||
@border-width-base * 3;
|
||||
|
||||
&:focus,
|
||||
&:hover,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { TimePickerLocale } from '../index';
|
||||
|
||||
const locale: TimePickerLocale = {
|
||||
placeholder: 'انتخاب زمان',
|
||||
rangePlaceholder: ['زمان شروع', 'زمان پایان'],
|
||||
};
|
||||
|
||||
export default locale;
|
||||
|
||||
@@ -68,7 +68,7 @@ exports[`renders ./components/typography/demo/basic.md correctly 1`] = `
|
||||
<li>
|
||||
<a
|
||||
class="ant-typography"
|
||||
href="/docs/pattern/navigation"
|
||||
href="/docs/spec/overview"
|
||||
>
|
||||
Patterns
|
||||
</a>
|
||||
@@ -76,7 +76,7 @@ exports[`renders ./components/typography/demo/basic.md correctly 1`] = `
|
||||
<li>
|
||||
<a
|
||||
class="ant-typography"
|
||||
href="/docs/resource/download"
|
||||
href="/docs/resources"
|
||||
>
|
||||
Resource Download
|
||||
</a>
|
||||
@@ -164,7 +164,7 @@ exports[`renders ./components/typography/demo/basic.md correctly 1`] = `
|
||||
<li>
|
||||
<a
|
||||
class="ant-typography"
|
||||
href="/docs/spec/proximity"
|
||||
href="/docs/spec/proximity-cn"
|
||||
>
|
||||
设计原则
|
||||
</a>
|
||||
@@ -172,7 +172,7 @@ exports[`renders ./components/typography/demo/basic.md correctly 1`] = `
|
||||
<li>
|
||||
<a
|
||||
class="ant-typography"
|
||||
href="/docs/pattern/navigation"
|
||||
href="/docs/spec/overview-cn"
|
||||
>
|
||||
设计模式
|
||||
</a>
|
||||
@@ -180,7 +180,7 @@ exports[`renders ./components/typography/demo/basic.md correctly 1`] = `
|
||||
<li>
|
||||
<a
|
||||
class="ant-typography"
|
||||
href="/docs/resource/download"
|
||||
href="/docs/resources-cn"
|
||||
>
|
||||
设计资源
|
||||
</a>
|
||||
|
||||
@@ -51,10 +51,10 @@ ReactDOM.render(
|
||||
<Link href="/docs/spec/proximity">Principles</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/docs/pattern/navigation">Patterns</Link>
|
||||
<Link href="/docs/spec/overview">Patterns</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/docs/resource/download">Resource Download</Link>
|
||||
<Link href="/docs/resources">Resource Download</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</Paragraph>
|
||||
@@ -84,13 +84,13 @@ ReactDOM.render(
|
||||
<Paragraph>
|
||||
<ul>
|
||||
<li>
|
||||
<Link href="/docs/spec/proximity">设计原则</Link>
|
||||
<Link href="/docs/spec/proximity-cn">设计原则</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/docs/pattern/navigation">设计模式</Link>
|
||||
<Link href="/docs/spec/overview-cn">设计模式</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/docs/resource/download">设计资源</Link>
|
||||
<Link href="/docs/resources-cn">设计资源</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</Paragraph>
|
||||
|
||||
@@ -49,7 +49,7 @@ antd use shallow compare of props to optimize performance. You should always pas
|
||||
|
||||
### After I set the `value` of an `Input`/`Select`(etc.) component, the value cannot be changed by user's action.
|
||||
|
||||
Try `defaultValue` or `onChange` to change `value`, and please read [React's documentation](https://facebook.github.io/react/docs/forms.html#controlled-components).
|
||||
Try `onChange` to change `value`, and please read [React's documentation](https://reactjs.org/docs/forms.html#controlled-components).
|
||||
|
||||
### Components are not vertically aligned when placed in single row.
|
||||
|
||||
@@ -59,7 +59,7 @@ Try [Space](https://ant.design/components/space/) component to make them aligned
|
||||
|
||||
Yes, antd is designed to help you develop a complete background application. To do so, we override some global styles for styling convenience, and currently these cannot be removed or changed. More info at https://github.com/ant-design/ant-design/issues/4331 .
|
||||
|
||||
Alternatively, follow the instructions in [How to avoid modifying global styles?](docs/react/customize-theme#How-to-avoid-modifying-global-styles-?)
|
||||
Alternatively, follow the instructions in [How to avoid modifying global styles?](/docs/react/customize-theme#How-to-avoid-modifying-global-styles)
|
||||
|
||||
### I cannot install `antd` and `antd`'s dependencies in mainland China.
|
||||
|
||||
@@ -116,7 +116,7 @@ Or you can simply upgrade to [antd@4.0](https://github.com/ant-design/ant-design
|
||||
|
||||
Static methods like message/notification/Modal.confirm are not using the same render tree as `<Button />`, but rendered to indepent DOM node created by `ReactDOM.render`, which cannot access React context from ConfigProvider. Consider two solutions here:
|
||||
|
||||
1. Replace original usages with [message.useMessage](https://ant.design/components/message/#components-message-demo-hooks), [notification.useNotification](https://ant.design/components/notification/#Why-I-can-not-access-context,-redux-in-notification) and [Modal.useModal](https://ant.design/components/modal/#Why-I-can-not-access-context,-redux-in-Modal.xxx).
|
||||
1. Replace original usages with [message.useMessage](/components/message/#components-message-demo-hooks), [notification.useNotification](/components/notification/#Why-I-can-not-access-context,-redux,-ConfigProvider-locale/prefixCls-in-notification) and [Modal.useModal](/components/modal/#Why-I-can-not-access-context,-redux,-ConfigProvider-locale/prefixCls-in-Modal.xxx).
|
||||
|
||||
2. Use `message.config`, `notification.config` and `Modal.config` to config `prefixCls` globally.
|
||||
|
||||
|
||||
@@ -45,11 +45,11 @@ https://ant.design/components/select/#Select-props
|
||||
|
||||
### 为什么修改组件传入的对象或数组属性组件不会更新?
|
||||
|
||||
antd 内部会对 props 进行浅比较实现性能优化。当状态变更,你总是应该传递一个新的对象。具体请参考[React 的文档](https://reactjs.org/docs/thinking-in-react.html)
|
||||
antd 内部会对 props 进行浅比较实现性能优化。当状态变更,你总是应该传递一个新的对象。具体请参考 [React 的文档](https://reactjs.org/docs/thinking-in-react.html)
|
||||
|
||||
### 当我设置了 `Input`/`Select` 等的 `value` 时它就无法修改了。
|
||||
|
||||
尝试使用 `defaultValue` 或 `onChange` 来改变 `value`,请参考 [React 的文档](https://reactjs.org/docs/forms.html#controlled-components)。
|
||||
尝试使用 `onChange` 来改变 `value`,请参考 [React 的文档](https://reactjs.org/docs/forms.html#controlled-components)。
|
||||
|
||||
### 多个组件放一排时没有垂直对齐怎么办?
|
||||
|
||||
@@ -57,7 +57,7 @@ antd 内部会对 props 进行浅比较实现性能优化。当状态变更,
|
||||
|
||||
### antd 覆盖了我的全局样式!
|
||||
|
||||
是的,antd 在设计的时候就是用来开发一个完整的应用的,为了方便,我们覆盖了一些全局样式,现在还不能移除,想要了解更多请追踪这个 issue:https://github.com/ant-design/ant-design/issues/4331 ,或者参考这个教程 [How to avoid modifying global styles?](docs/react/customize-theme#How-to-avoid-modifying-global-styles-?)
|
||||
是的,antd 在设计的时候就是用来开发一个完整的应用的,为了方便,我们覆盖了一些全局样式,现在还不能移除,想要了解更多请追踪 [这个 issue](https://github.com/ant-design/ant-design/issues/4331),或者参考这个教程 [How to avoid modifying global styles?](/docs/react/customize-theme#How-to-avoid-modifying-global-styles)
|
||||
|
||||
### 我没法安装 `antd` 和 `antd` 的依赖,顺便提一句,我在中国大陆。
|
||||
|
||||
@@ -116,7 +116,7 @@ antd 内部会对 props 进行浅比较实现性能优化。当状态变更,
|
||||
- 重现链接:https://codesandbox.io/s/dank-brook-v1csy
|
||||
- 相同 issue:[#15572](https://github.com/ant-design/ant-design/issues/15572)、[#16436](https://github.com/ant-design/ant-design/issues/16436)、[#11938](https://github.com/ant-design/ant-design/issues/11938)、[#11735](https://github.com/ant-design/ant-design/issues/11735)、[#11586](https://github.com/ant-design/ant-design/issues/11586)、[#10425](https://github.com/ant-design/ant-design/issues/10425)、[#11053](https://github.com/ant-design/ant-design/issues/11053)
|
||||
|
||||
就像[这个回复](https://github.com/ant-design/ant-design/issues/15572#issuecomment-475476135)里解释的一样,这是因为 `<DatePicker mode="year" />` 不等于 `YearPicker`,`<RangePicker mode="month" />` 不等于 `MonthRangePicker`。 `mode` 属性是在 antd 3.0 时,为了控制面板展现状态而添加的属性,以支持[展示时间面板](https://github.com/ant-design/ant-design/issues/5190)等需求而添加的。`mode` 只会简单的改变当前显示的面板,不会修改默认的交互行为(比如 DatePicker 依然是点击日才会完成选择并关闭面板)。
|
||||
就像 [这个回复](https://github.com/ant-design/ant-design/issues/15572#issuecomment-475476135) 里解释的一样,这是因为 `<DatePicker mode="year" />` 不等于 `YearPicker`,`<RangePicker mode="month" />` 不等于 `MonthRangePicker`。 `mode` 属性是在 antd 3.0 时,为了控制面板展现状态而添加的属性,以支持[展示时间面板](https://github.com/ant-design/ant-design/issues/5190)等需求而添加的。`mode` 只会简单的改变当前显示的面板,不会修改默认的交互行为(比如 DatePicker 依然是点击日才会完成选择并关闭面板)。
|
||||
|
||||
同样的,`disabledDate` 对于任何 `<DatePicker />` 也只会针对**日面板**生效,[并不会对 `<DatePicker mode="year/month" />` 上的年/月面板生效](https://github.com/ant-design/ant-design/issues/9008#issuecomment-358554118)。
|
||||
|
||||
@@ -130,7 +130,7 @@ antd 内部会对 props 进行浅比较实现性能优化。当状态变更,
|
||||
|
||||
message/notification/Modal.confirm 等静态方法不同于 `<Button />` 的渲染方式,是单独渲染在 `ReactDOM.render` 生成的 DOM 树节点上,无法共享 ConfigProvider 提供的 context 信息。你有两种解决方式:
|
||||
|
||||
1. 使用官方提供的 [message.useMessage](<[message.useMessage](https://ant.design/components/message-cn/#components-message-demo-hooks)>)、[notification.useNotification](https://ant.design/components/notification-cn/#%E4%B8%BA%E4%BB%80%E4%B9%88-notification-%E4%B8%8D%E8%83%BD%E8%8E%B7%E5%8F%96-context%E3%80%81redux-%E7%9A%84%E5%86%85%E5%AE%B9%EF%BC%9F) 和 [Modal.useModal](https://ant.design/components/modal-cn/#%E4%B8%BA%E4%BB%80%E4%B9%88-Modal-%E6%96%B9%E6%B3%95%E4%B8%8D%E8%83%BD%E8%8E%B7%E5%8F%96-context%E3%80%81redux-%E7%9A%84%E5%86%85%E5%AE%B9%EF%BC%9F) 来调用这些方法。
|
||||
1. 使用官方提供的 [message.useMessage](/components/message-cn/#components-message-demo-hooks)、[notification.useNotification](/components/notification-cn/#%E4%B8%BA%E4%BB%80%E4%B9%88-notification-%E4%B8%8D%E8%83%BD%E8%8E%B7%E5%8F%96-context%E3%80%81redux-%E7%9A%84%E5%86%85%E5%AE%B9%E5%92%8C-ConfigProvider-%E7%9A%84-locale/prefixCls-%E9%85%8D%E7%BD%AE%EF%BC%9F) 和 [Modal.useModal](/components/modal-cn/#%E4%B8%BA%E4%BB%80%E4%B9%88-Modal-%E6%96%B9%E6%B3%95%E4%B8%8D%E8%83%BD%E8%8E%B7%E5%8F%96-context%E3%80%81redux%E3%80%81%E7%9A%84%E5%86%85%E5%AE%B9%E5%92%8C-ConfigProvider-locale/prefixCls-%E9%85%8D%E7%BD%AE%EF%BC%9F) 来调用这些方法。
|
||||
|
||||
2. 使用 `message.config`、`notification.config` 和 `Modal.config` 方法全局设置 `prefixCls`。
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Please find below some of the design resources and tools about Ant Design that w
|
||||
- Mobile Components
|
||||
- https://gw.alipayobjects.com/zos/basement_prod/c0c3852c-d245-4330-886b-cb02ef49eb6d.svg
|
||||
- Sketch Symbols File for Mobile
|
||||
- http://p.tb.cn/rmsportal_3436_AntDesignMobile_20Template_20V1.0.sketch
|
||||
- https://gw.alipayobjects.com/os/bmw-prod/d6266aef-25b7-4892-b275-ce214121831c.sketch
|
||||
- Ant Design Pro
|
||||
- https://gw.alipayobjects.com/zos/basement_prod/5edc7f4d-3302-4710-963b-7b6c77ea8d06.svg
|
||||
- Common Templates and Pages
|
||||
|
||||
@@ -21,7 +21,7 @@ toc: false
|
||||
- Mobile Components
|
||||
- https://gw.alipayobjects.com/zos/basement_prod/c0c3852c-d245-4330-886b-cb02ef49eb6d.svg
|
||||
- 移动组件 Sketch 模板
|
||||
- http://p.tb.cn/rmsportal_3436_AntDesignMobile_20Template_20V1.0.sketch
|
||||
- https://gw.alipayobjects.com/os/bmw-prod/d6266aef-25b7-4892-b275-ce214121831c.sketch
|
||||
- Ant Design Pro
|
||||
- https://gw.alipayobjects.com/zos/basement_prod/5edc7f4d-3302-4710-963b-7b6c77ea8d06.svg
|
||||
- 典型页面 + 通用业务模板
|
||||
|
||||
@@ -40,7 +40,7 @@ ReactDOM.render(<Palette color={{ name: 'gray', count: 13 }} direction="horizont
|
||||
|
||||
### Data Visualization Color Palette
|
||||
|
||||
Data visualization color palette is based on the basic color palette and neutral color palette, and based on the principle that AntV's "effective, clear, accurate and beautiful". [View Palette](https://antv.vision/en/docs/specification/principles/visual)
|
||||
Data visualization color palette is based on the basic color palette and neutral color palette, and based on the principle that AntV's "effective, clear, accurate and beautiful". [View Palette](https://antv.vision/en/docs/specification/language/palette)
|
||||
|
||||
### Palette Generation Tool
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ ReactDOM.render(<Palette color={{ name: 'gray', count: 13 }} direction="horizont
|
||||
|
||||
### 数据可视化色板
|
||||
|
||||
数据可视化色板是在基础色板以及中性色板的基础上,融合 AntV 「有效、清晰、准确、美」的原则产生的。[查看色板](https://antv.vision/zh/docs/specification/principles/basic/)
|
||||
数据可视化色板是在基础色板以及中性色板的基础上,融合 AntV 「有效、清晰、准确、美」的原则产生的。[查看色板](https://antv.vision/zh/docs/specification/language/palette)
|
||||
|
||||
### 色板生成工具
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ title: 概览
|
||||
|
||||
> 这是一份动态更新的设计文档,你的阅读和反馈是我们前进的动力。
|
||||
|
||||
秉承 [Ant Design 设计价值观](/docs/spec/values),针对企业级产品反复出现的设计问题,设计模式给出一般性的解决方案,实现跨应用交互一致性的有效融合。依照设计模式使用 [antd 组件](/docs/react/introduce)设计界面,既减少了不必要的认知成本,又能够提升交互确定性和设计的效率。考虑到需要适应各种独特的业务场景,模式的规则具有一定的灵活性,变不离宗,通过了解设计模式的构建逻辑,可以衍生出更具场景特性的解决方案。设计模式包含以下内容:
|
||||
秉承 [Ant Design 设计价值观](/docs/spec/values),针对企业级产品反复出现的设计问题,设计模式给出一般性的解决方案,实现跨应用交互一致性的有效融合。依照设计模式使用 [antd 组件](/docs/react/introduce)设计界面,既减少了不必要的认知成本,又能够提升交互确定性和设计的效率。考虑到需要适应各种独特的业务场景,模式的规则具有一定的灵活性,万变不离其宗,通过了解设计模式的构建逻辑,可以衍生出更具场景特性的解决方案。设计模式包含以下内容:
|
||||
|
||||
### 原则
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "antd",
|
||||
"version": "4.12.0",
|
||||
"version": "4.12.3",
|
||||
"description": "An enterprise-class UI design language and React components implementation",
|
||||
"title": "Ant Design",
|
||||
"keywords": [
|
||||
@@ -109,8 +109,8 @@
|
||||
"ie >= 11"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^5.0.0",
|
||||
"@ant-design/icons": "^4.4.0",
|
||||
"@ant-design/colors": "^6.0.0",
|
||||
"@ant-design/icons": "^4.5.0",
|
||||
"@ant-design/react-slick": "~0.28.1",
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"array-tree-filter": "^2.1.0",
|
||||
|
||||
59
scripts/check-version-md.js
Normal file
59
scripts/check-version-md.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const chalk = require('chalk');
|
||||
const fs = require('fs');
|
||||
const { join } = require('path');
|
||||
const moment = require('moment');
|
||||
|
||||
const getChangelogByVersion = (content, version) => {
|
||||
const lines = content.split('\n');
|
||||
const changeLog = [];
|
||||
const startPattern = new RegExp(`^## ${version}`);
|
||||
const stopPattern = /^## /; // 前一个版本
|
||||
let begin = false;
|
||||
for (let i = 0; i < lines.length; i += 1) {
|
||||
const line = lines[i];
|
||||
if (begin && stopPattern.test(line)) {
|
||||
break;
|
||||
}
|
||||
if (begin && line) {
|
||||
changeLog.push(line);
|
||||
}
|
||||
if (!begin) {
|
||||
begin = startPattern.test(line);
|
||||
}
|
||||
}
|
||||
|
||||
return changeLog.join('\n');
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
const packageJson = require(join(__dirname, '..', 'package.json'));
|
||||
|
||||
const { version } = packageJson;
|
||||
|
||||
const changeLogContent = fs.readFileSync(join(__dirname, '..', 'CHANGELOG.en-US.md')).toString();
|
||||
|
||||
const changeLog = getChangelogByVersion(changeLogContent, version);
|
||||
if (!changeLog) {
|
||||
console.log('\n');
|
||||
console.log(chalk.red('[check-version-md]: No changelog found for the version to be released'));
|
||||
console.log('\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (changeLog) {
|
||||
const text = changeLog.split('\n')[0];
|
||||
if (text.trim().startsWith('`') && text.trim().endsWith('`')) {
|
||||
const date = moment(text.trim().replace('`', '').replace('`', ''));
|
||||
if (date.isBetween(moment().add(-2, 'day'), moment().add(2, 'day'))) {
|
||||
console.log('\n');
|
||||
console.log(chalk.blue('[check-version-md]: Check Passed'));
|
||||
console.log('\n');
|
||||
process.exit(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.log('\n');
|
||||
console.log(chalk.red('[check-version-md]: The date wrongly written'));
|
||||
console.log('\n');
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
echo "[CLEAN] clean"
|
||||
npm run clean
|
||||
|
||||
echo "[TEST ALL] test changlog"
|
||||
node ./scripts/check-version-md.js
|
||||
|
||||
echo "[TEST ALL] check-commit"
|
||||
npm run check-commit
|
||||
|
||||
|
||||
@@ -124,6 +124,12 @@
|
||||
object-fit: cover;
|
||||
background: #d8d8d8;
|
||||
border-radius: 100%;
|
||||
cursor: pointer;
|
||||
transition: box-shadow 0.3s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: @shadow-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user