mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-24 02:59:58 +08:00
chore: merge master into feature (#26421)
This commit is contained in:
commit
a8b4e3c2e5
@ -26,6 +26,7 @@ timeline: true
|
||||
|
||||
`2020-08-23`
|
||||
|
||||
- 💄 Darker `@text-color` for WCAG 2.0 on contrast ratio. [#25630](https://github.com/ant-design/ant-design/pull/25630)
|
||||
- 🔥 New Image component. [#26296](https://github.com/ant-design/ant-design/pull/26296)
|
||||
- 🔥 Table support `sticky` prop to sticky header and scroll bar. [#25939](https://github.com/ant-design/ant-design/pull/25939)
|
||||
- Form
|
||||
@ -63,10 +64,8 @@ timeline: true
|
||||
- 💄 Optimize the display effect of Descriptions when there is more content. [#25903](https://github.com/ant-design/ant-design/pull/25903)
|
||||
- 🆕 message could be detroied by `message.desctroy(key)`. [#26052](https://github.com/ant-design/ant-design/pull/26052) [@lihqi](https://github.com/lihqi)
|
||||
- 💄 Adjust InputNumber handler bar to be hidden when `readOnly`. [#25998](https://github.com/ant-design/ant-design/pull/25998)
|
||||
- 💄 Lighten `@text-color` color in dark theme. [#25923](https://github.com/ant-design/ant-design/pull/25923)
|
||||
- 💄 Darken `@text-color` color in default theme. [#25630](https://github.com/ant-design/ant-design/pull/25630)
|
||||
- Locale
|
||||
- 🏴 Add Galician Locale support. [#26015](https://github.com/ant-design/ant-design/pull/26015) [@barreeeiroo](https://github.com/barreeeiroo)
|
||||
- 🌐 Add Galician locale support. [#26015](https://github.com/ant-design/ant-design/pull/26015) [@barreeeiroo](https://github.com/barreeeiroo)
|
||||
- 🇱🇹 Add Lithuanian locale support. [#26312](https://github.com/ant-design/ant-design/pull/26312) [@mslotvinskij](https://github.com/mslotvinskij)
|
||||
- 🌐 Use `kmr_IQ` to replace `ku_IQ`. [#26030](https://github.com/ant-design/ant-design/pull/26030)
|
||||
- RTL
|
||||
|
@ -26,6 +26,7 @@ timeline: true
|
||||
|
||||
`2020-08-23`
|
||||
|
||||
- 💄 加深默认文本 `@text-color` 以满足 WCAG 2.0 对比度的规范。[#25630](https://github.com/ant-design/ant-design/pull/25630)
|
||||
- 🔥 新增图片组件 Image。[#26296](https://github.com/ant-design/ant-design/pull/26296)
|
||||
- 🔥 Table 新增 `sticky` 属性以支持固定表头和滚动条。[#25939](https://github.com/ant-design/ant-design/pull/25939)
|
||||
- Form
|
||||
@ -33,22 +34,22 @@ timeline: true
|
||||
- 🆕 Form.List 中的 `add` 方法支持第二个 `index` 参数。[#26081](https://github.com/ant-design/ant-design/pull/26081)
|
||||
- 🆕 虚拟滚动支持无闪动滚动,修复 Select/TreeSelect 滚动时列表空白的问题。[#26306](https://github.com/ant-design/ant-design/pull/26306)
|
||||
- Typography
|
||||
- 🆕 新增 Typography.Text success 类型。[#26145](https://github.com/ant-design/ant-design/pull/26145) [@llwslc](https://github.com/llwslc)
|
||||
- 🆕 新增 Typography.Text `success` 类型。[#26145](https://github.com/ant-design/ant-design/pull/26145) [@llwslc](https://github.com/llwslc)
|
||||
- 🆕 Typography `copyable` 支持隐藏提示,`editable` 支持设置图标与提示。[#25953](https://github.com/ant-design/ant-design/pull/25953) [@llwslc](https://github.com/llwslc)
|
||||
- 🆕 新增 Typography.Title 5 级标题。[#25861](https://github.com/ant-design/ant-design/pull/25861)
|
||||
- 🆕 Typography 的 `editable` 配置中增加了 `maxLength` & `autoSize` 支持。[#25373](https://github.com/ant-design/ant-design/pull/25373) [@CornerSkyless](https://github.com/CornerSkyless)
|
||||
- 🆕 Typography 的 `editable` 配置中增加了 `maxLength` 和 `autoSize` 属性。[#25373](https://github.com/ant-design/ant-design/pull/25373) [@CornerSkyless](https://github.com/CornerSkyless)
|
||||
- 🐞 修复 Transfer 搜索空格时 `filterOption` 没有触发的问题。[#26335](https://github.com/ant-design/ant-design/pull/26335)
|
||||
- Progress
|
||||
- 🐞 修复 Progress `steps` 属性对于 `trailColor` 不生效的问题。[#26323](https://github.com/ant-design/ant-design/pull/26323)
|
||||
- 🐞 修复 Progress 当 `type="circle"` 时 `success.percent` 不生效的问题。[#26307](https://github.com/ant-design/ant-design/pull/26307)
|
||||
- 🐞 修复 Textarea 当 `value` 为 `undefined` 时未显示 `defaultValue` 问题。[#26327](https://github.com/ant-design/ant-design/pull/26327)
|
||||
- Cascader
|
||||
- 🐞 修复 Cascader 在按下 ESC 键,然后通过输入进行搜索时 options 不展开的问题。[#26271](https://github.com/ant-design/ant-design/pull/26271) [@flyerH](https://github.com/flyerH)
|
||||
- 🐞 修复 Cascader 在按下 ESC 键,然后通过输入进行搜索时 `options` 不展开的问题。[#26271](https://github.com/ant-design/ant-design/pull/26271) [@flyerH](https://github.com/flyerH)
|
||||
- 💄 优化 Cascader 清除动画效果。[#26186](https://github.com/ant-design/ant-design/pull/26186)
|
||||
- 🗑 移除遗留的 Button.Group 支持,请使用 Space 代替。[#26260](https://github.com/ant-design/ant-design/pull/26260)
|
||||
- Select
|
||||
- 🆕 Select 支持 onClear 属性。[#25907](https://github.com/ant-design/ant-design/pull/25907)
|
||||
- 🐞 修复 Select mode="tags" 搜索显示两条重复条目的问题。[#25907](https://github.com/ant-design/ant-design/pull/25907)
|
||||
- 🆕 Select 支持 `onClear` 属性。[#25907](https://github.com/ant-design/ant-design/pull/25907)
|
||||
- 🐞 修复 Select `mode="tags"` 搜索显示两条重复条目的问题。[#25907](https://github.com/ant-design/ant-design/pull/25907)
|
||||
- 🐞 修复 Select 聚焦时被禁用的样式异常问题。[#26255](https://github.com/ant-design/ant-design/pull/26255)
|
||||
- 🐞 修复多选模式的 Select 在 `showArrow` 时图标重叠问题。[#26168](https://github.com/ant-design/ant-design/pull/26168) [@zhangchen915](https://github.com/zhangchen915)
|
||||
- DatePicker
|
||||
@ -63,12 +64,10 @@ timeline: true
|
||||
- 💄 优化 Descriptions 在内容比较多时的显示效果。[#25903](https://github.com/ant-design/ant-design/pull/25903)
|
||||
- 🆕 message 支持通过 `message.desctroy(key)` 销毁。[#26052](https://github.com/ant-design/ant-design/pull/26052) [@lihqi](https://github.com/lihqi)
|
||||
- 💄 调整 InputNumber 操作栏在 `readOnly` 时为隐藏。[#25998](https://github.com/ant-design/ant-design/pull/25998)
|
||||
- 💄 加深暗色主题下 `@text-color` 颜色。[#25923](https://github.com/ant-design/ant-design/pull/25923)
|
||||
- 💄 加深默认主题下 `@text-color` 颜色。[#25630](https://github.com/ant-design/ant-design/pull/25630)
|
||||
- 国际化
|
||||
- 🏴 添加加利西亚语支持。[#26015](https://github.com/ant-design/ant-design/pull/26015) [@barreeeiroo](https://github.com/barreeeiroo)
|
||||
- 🌐 添加加利西亚语支持。[#26015](https://github.com/ant-design/ant-design/pull/26015) [@barreeeiroo](https://github.com/barreeeiroo)
|
||||
- 🇱🇹 添加立陶宛语支持。[#26312](https://github.com/ant-design/ant-design/pull/26312) [@mslotvinskij](https://github.com/mslotvinskij)
|
||||
- 🌐 用 `kmr_IQ` 替换 `ku_IQ` 缩写。[#26030](https://github.com/ant-design/ant-design/pull/26030)
|
||||
- 🌐 新增 `kmr_IQ` 语言包用以代替 ku_IQ。[#26030](https://github.com/ant-design/ant-design/pull/26030)
|
||||
- RTL
|
||||
- 💄 优化 Tree RTL 模式下连接线的样式。[#26205](https://github.com/ant-design/ant-design/pull/26205)
|
||||
- 💄 优化 Dropdown RTL 写法避免暗黑模式样式覆盖。[#26206](https://github.com/ant-design/ant-design/pull/26206)
|
||||
|
@ -13,6 +13,7 @@ class AffixMounter extends React.Component<{
|
||||
offsetBottom?: number;
|
||||
offsetTop?: number;
|
||||
onTestUpdatePosition?(): void;
|
||||
onChange?: () => void;
|
||||
}> {
|
||||
private container: HTMLDivElement;
|
||||
|
||||
@ -131,13 +132,15 @@ describe('Affix Render', () => {
|
||||
|
||||
it('updatePosition when offsetTop changed', async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
const onChange = jest.fn();
|
||||
|
||||
affixMounterWrapper = mount(<AffixMounter offsetTop={0} />, {
|
||||
affixMounterWrapper = mount(<AffixMounter offsetTop={0} onChange={onChange} />, {
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
await sleep(20);
|
||||
|
||||
await movePlaceholder(-100);
|
||||
expect(onChange).toHaveBeenLastCalledWith(true);
|
||||
expect(affixMounterWrapper.instance().affix.state.affixStyle?.top).toBe(0);
|
||||
affixMounterWrapper.setProps({
|
||||
offsetTop: 10,
|
||||
|
@ -141,6 +141,17 @@ describe('Badge', () => {
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('Badge should work when status/color is empty string', () => {
|
||||
const wrapper = mount(
|
||||
<>
|
||||
<Badge color="" text="text" />
|
||||
<Badge status="" text="text" />
|
||||
</>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.ant-badge')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('render Badge size when contains children', () => {
|
||||
const wrapper = render(
|
||||
<>
|
||||
|
@ -62,9 +62,7 @@ const Badge: CompoundedComponent = ({
|
||||
return displayCount as string | number | null;
|
||||
};
|
||||
|
||||
const hasStatus = (): boolean => {
|
||||
return !!status || !!color;
|
||||
};
|
||||
const hasStatus = (): boolean => (status !== null && status !== undefined) || (color !== null && color !== undefined);
|
||||
|
||||
const isZero = () => {
|
||||
const numberedDisplayCount = getNumberedDisplayCount();
|
||||
|
@ -29,11 +29,13 @@ describe('Calendar', () => {
|
||||
|
||||
it('Calendar should be selectable', () => {
|
||||
const onSelect = jest.fn();
|
||||
const wrapper = mount(<Calendar onSelect={onSelect} />);
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(<Calendar onSelect={onSelect} onChange={onChange} />);
|
||||
wrapper.find('.ant-picker-cell').at(0).simulate('click');
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.anything());
|
||||
const value = onSelect.mock.calls[0][0];
|
||||
expect(Moment.isMoment(value)).toBe(true);
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('only Valid range should be selectable', () => {
|
||||
|
@ -6055,7 +6055,7 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
<span>
|
||||
Click to upload
|
||||
Click to upload
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
|
@ -177,9 +177,7 @@ const Demo = () => {
|
||||
extra="longgggggggggggggggggggggggggggggggggg"
|
||||
>
|
||||
<Upload name="logo" action="/upload.do" listType="picture">
|
||||
<Button>
|
||||
<UploadOutlined /> Click to upload
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Click to upload</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
|
||||
|
@ -338,6 +338,10 @@ Before Modal opens, children elements do not exist in the view. You can set `for
|
||||
|
||||
Components inside Form.Item with name property will turn into controlled mode, which makes `defaultValue` not work anymore. Please try `initialValues` of Form to set default value.
|
||||
|
||||
### Why can not call `ref` of From at first time?
|
||||
|
||||
`ref` only receives the mounted instance. please ref React official doc: https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs
|
||||
|
||||
### Why `resetFields` will re-mount component?
|
||||
|
||||
`resetFields` will re-mount component under Field to clean up customize component side effects (like async data, cached state, etc.). It's by design.
|
||||
|
@ -340,6 +340,10 @@ validator(rule, value, callback) => {
|
||||
|
||||
当你为 Form.Item 设置 `name` 属性后,子组件会转为受控模式。因而 `defaultValue` 不会生效。你需要在 Form 上通过 `initialValues` 设置默认值。
|
||||
|
||||
### 为什么第一次调用 `ref` 的 From 为空?
|
||||
|
||||
`ref` 仅在节点被加载时才会被赋值,请参考 React 官方文档:https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs
|
||||
|
||||
### 为什么 `resetFields` 会重新 mount 组件?
|
||||
|
||||
`resetFields` 会重置整个 Field,因而其子组件也会重新 mount 从而消除自定义组件可能存在的副作用(例如异步数据、状态等等)。
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -46,6 +46,7 @@ import jaJP from '../ja_JP';
|
||||
import knIN from '../kn_IN';
|
||||
import koKR from '../ko_KR';
|
||||
import kmrIQ from '../kmr_IQ';
|
||||
import kuIQ from '../ku_IQ';
|
||||
import lvLV from '../lv_LV';
|
||||
import ltLT from '../lt_LT';
|
||||
import mkMK from '../mk_MK';
|
||||
@ -103,6 +104,7 @@ const locales = [
|
||||
knIN,
|
||||
koKR,
|
||||
kmrIQ,
|
||||
kuIQ,
|
||||
ltLT,
|
||||
mkMK,
|
||||
msMY,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import TestUtils from 'react-dom/test-utils';
|
||||
import Modal from '..';
|
||||
import { destroyFns } from '../Modal';
|
||||
|
||||
@ -140,6 +141,42 @@ describe('Modal.confirm triggers callbacks correctly', () => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should close confirm modal when click cancel button', () => {
|
||||
jest.useFakeTimers();
|
||||
const onCancel = jest.fn();
|
||||
Modal.confirm({
|
||||
title: 'title',
|
||||
content: 'content',
|
||||
onCancel,
|
||||
});
|
||||
jest.runAllTimers();
|
||||
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(1);
|
||||
$$('.ant-btn')[0].click();
|
||||
jest.runAllTimers();
|
||||
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(0);
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should close confirm modal when press ESC', () => {
|
||||
jest.useFakeTimers();
|
||||
const onCancel = jest.fn();
|
||||
Modal.confirm({
|
||||
title: 'title',
|
||||
content: 'content',
|
||||
onCancel,
|
||||
});
|
||||
jest.runAllTimers();
|
||||
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(1);
|
||||
TestUtils.Simulate.keyDown($$('.ant-modal')[0], {
|
||||
keyCode: 27,
|
||||
});
|
||||
jest.runAllTimers();
|
||||
expect($$(`.ant-modal-confirm-confirm`)).toHaveLength(0);
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should not close modals when click confirm button when onOk has argument', () => {
|
||||
jest.useFakeTimers();
|
||||
['info', 'success', 'warning', 'error'].forEach(type => {
|
||||
|
@ -735,7 +735,7 @@ exports[`renders ./components/select/demo/custom-dropdown-menu.md correctly 1`]
|
||||
|
||||
exports[`renders ./components/select/demo/custom-tag-render.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-select-multiple ant-select-show-search"
|
||||
class="ant-select ant-select-multiple ant-select-show-arrow ant-select-show-search"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
@ -825,6 +825,33 @@ exports[`renders ./components/select/demo/custom-tag-render.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@ -1091,7 +1118,7 @@ exports[`renders ./components/select/demo/label-in-value.md correctly 1`] = `
|
||||
exports[`renders ./components/select/demo/multiple.md correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-select ant-select-multiple ant-select-show-search"
|
||||
class="ant-select ant-select-multiple ant-select-allow-clear ant-select-show-search"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
@ -1195,6 +1222,33 @@ Array [
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-clear"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>,
|
||||
<br />,
|
||||
<div
|
||||
|
@ -7,11 +7,11 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
允许自定义选择标签的样式
|
||||
允许自定义选择标签的样式。
|
||||
|
||||
## en-US
|
||||
|
||||
Allows for custom rendering of tags
|
||||
Allows for custom rendering of tags.
|
||||
|
||||
```jsx
|
||||
import { Select, Tag } from 'antd';
|
||||
@ -31,6 +31,7 @@ function tagRender(props) {
|
||||
ReactDOM.render(
|
||||
<Select
|
||||
mode="multiple"
|
||||
showArrow
|
||||
tagRender={tagRender}
|
||||
defaultValue={['gold', 'cyan']}
|
||||
style={{ width: '100%' }}
|
||||
|
@ -31,6 +31,7 @@ ReactDOM.render(
|
||||
<>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Please select"
|
||||
defaultValue={['a10', 'c12']}
|
||||
|
@ -7,11 +7,11 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
tags select,随意输入的内容(scroll the menu)
|
||||
tags select,随意输入的内容(scroll the menu)。
|
||||
|
||||
## en-US
|
||||
|
||||
Select with tags, transform input to tag (scroll the menu)
|
||||
Select with tags, transform input to tag (scroll the menu).
|
||||
|
||||
```jsx
|
||||
import { Select } from 'antd';
|
||||
|
@ -27,7 +27,10 @@ Select component to select value from options.
|
||||
| allowClear | Show clear button | boolean | false | |
|
||||
| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when `mode` is set to `multiple` or `tags` | boolean | true | |
|
||||
| autoFocus | Get focus by default | boolean | false | |
|
||||
| bordered | Whether has border style | boolean | true | |
|
||||
| clearIcon | The custom clear icon | ReactNode | - | |
|
||||
| defaultActiveFirstOption | Whether active first option by default | boolean | true | |
|
||||
| defaultOpen | Initial open state of dropdown | boolean | - | |
|
||||
| defaultValue | Initial selected option | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
|
||||
| disabled | Whether disabled select | boolean | false | |
|
||||
| dropdownClassName | The className of dropdown menu | string | - | |
|
||||
@ -36,32 +39,20 @@ Select component to select value from options.
|
||||
| dropdownStyle | The style of dropdown menu | CSSProperties | - | |
|
||||
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded | boolean \| function(inputValue, option) | true | |
|
||||
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. [Example](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
|
||||
| labelInValue | Whether to embed label in value, turn the format of value from `string` to `{ value: string, label: ReactNode }` | boolean | false | |
|
||||
| labelInValue | Whether to embed label in value, turn the format of value from `string` to { value: string, label: ReactNode } | boolean | false | |
|
||||
| listHeight | Config popup height | number | 256 | |
|
||||
| loading | Indicate loading state | boolean | false | |
|
||||
| maxTagCount | Max tag count to show | number | - | |
|
||||
| maxTagTextLength | Max tag text length to show | number | - | |
|
||||
| maxTagPlaceholder | Placeholder for not showing tags | ReactNode \| function(omittedValues) | - | |
|
||||
| tagRender | Customize tag render | (props) => ReactNode | - | |
|
||||
| maxTagTextLength | Max tag text length to show | number | - | |
|
||||
| menuItemSelectedIcon | The custom menuItemSelected icon with multiple options | ReactNode | - | |
|
||||
| mode | Set mode of Select | `multiple` \| `tags` | - | |
|
||||
| notFoundContent | Specify content to show when no result matches | ReactNode | `Not Found` | |
|
||||
| options | Select options. Will get better perf than jsx definition | { label, value }[] | - | |
|
||||
| optionFilterProp | Which prop value of option will be used for filter if filterOption is true | string | `value` | |
|
||||
| optionLabelProp | Which prop value of option will render as content of select. [Example](https://codesandbox.io/s/antd-reproduction-template-tk678) | string | `children` | |
|
||||
| placeholder | Placeholder of select | string \| ReactNode | - | |
|
||||
| showArrow | Whether to show the drop-down arrow | boolean | true(for single select), false(for multiple select) | |
|
||||
| showSearch | Whether show search input in single mode | boolean | false | |
|
||||
| size | Size of Select input | `large` \| `middle` \| `small` | - | |
|
||||
| suffixIcon | The custom suffix icon | ReactNode | - | |
|
||||
| removeIcon | The custom remove icon | ReactNode | - | |
|
||||
| clearIcon | The custom clear icon | ReactNode | - | |
|
||||
| menuItemSelectedIcon | The custom menuItemSelected icon with multiple options | ReactNode | - | |
|
||||
| tokenSeparators | Separator used to tokenize on tag \| multiple mode | string\[] | - | |
|
||||
| value | Current selected option | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
|
||||
| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 |
|
||||
| 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 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) | - | |
|
||||
| onDropdownVisibleChange | Called when dropdown open | function(open) | - | |
|
||||
| onFocus | Called when focus | function | - | |
|
||||
| onInputKeyDown | Called when key pressed | function | - | |
|
||||
| onMouseEnter | Called when mouse enter | function | - | |
|
||||
@ -69,11 +60,22 @@ Select component to select value from options.
|
||||
| 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) | - | |
|
||||
| defaultOpen | Initial open state of dropdown | boolean | - | |
|
||||
| open | Controlled open state of dropdown | boolean | - | |
|
||||
| onDropdownVisibleChange | Call when dropdown open | function(open) | - | |
|
||||
| loading | Indicate loading state | boolean | false | |
|
||||
| bordered | Whether has border style | boolean | true | |
|
||||
| options | Select options. Will get better perf than jsx definition | { label, value }[] | - | |
|
||||
| optionFilterProp | Which prop value of option will be used for filter if filterOption is true | string | `value` | |
|
||||
| optionLabelProp | Which prop value of option will render as content of select. [Example](https://codesandbox.io/s/antd-reproduction-template-tk678) | string | `children` | |
|
||||
| placeholder | Placeholder of select | string \| ReactNode | - | |
|
||||
| removeIcon | The custom remove icon | ReactNode | - | |
|
||||
| showArrow | Whether to show the drop-down arrow | boolean | true(for single select), false(for multiple select) | |
|
||||
| showSearch | Whether show search input in single mode | boolean | false | |
|
||||
| size | Size of Select input | `large` \| `middle` \| `small` | - | |
|
||||
| suffixIcon | The custom suffix icon | ReactNode | - | |
|
||||
| tagRender | Customize tag render | (props) => ReactNode | - | |
|
||||
| tokenSeparators | Separator used to tokenize on `tag` and `multiple` mode | string\[] | - | |
|
||||
| value | Current selected option | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
|
||||
| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 |
|
||||
|
||||
> 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 .
|
||||
|
||||
### Select Methods
|
||||
|
||||
@ -86,10 +88,10 @@ Select component to select value from options.
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --------- | ------------------------------------------ | ---------------- | ------- | ------- |
|
||||
| className | The additional class to option | string | - | |
|
||||
| disabled | Disable this option | boolean | false | |
|
||||
| title | `title` of Select after select this Option | string | - | |
|
||||
| value | Default to filter with this property | string \| number | - | |
|
||||
| className | The additional class to option | string | - | |
|
||||
|
||||
### OptGroup props
|
||||
|
||||
|
@ -28,7 +28,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
|
||||
| allowClear | 支持清除 | boolean | false | |
|
||||
| autoClearSearchValue | 是否在选中项后清空搜索框,只在 `mode` 为 `multiple` 或 `tags` 时有效 | boolean | true | |
|
||||
| autoFocus | 默认获取焦点 | boolean | false | |
|
||||
| bordered | 是否有边框 | boolean | true | |
|
||||
| clearIcon | 自定义的多选框清空图标 | ReactNode | - | |
|
||||
| defaultActiveFirstOption | 是否默认高亮第一个选项 | boolean | true | |
|
||||
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |
|
||||
| defaultValue | 指定默认选中的条目 | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| dropdownClassName | 下拉菜单的 className 属性 | string | - | |
|
||||
@ -37,32 +40,20 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
|
||||
| dropdownStyle | 下拉菜单的 style 属性 | CSSProperties | - | |
|
||||
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 true,反之则返回 false | boolean \| function(inputValue, option) | true | |
|
||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
|
||||
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 `string` 变为 `{ value: string, label: ReactNode }` 的格式 | boolean | false | |
|
||||
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 `string` 变为 { value: string, label: ReactNode } 的格式 | boolean | false | |
|
||||
| listHeight | 设置弹窗滚动高度 | number | 256 | |
|
||||
| loading | 加载中状态 | boolean | false | |
|
||||
| maxTagCount | 最多显示多少个 tag | number | - | |
|
||||
| maxTagTextLength | 最大显示的 tag 文本长度 | number | - | |
|
||||
| maxTagPlaceholder | 隐藏 tag 时显示的内容 | ReactNode \| function(omittedValues) | - | |
|
||||
| tagRender | 自定义 tag 内容 render | (props) => ReactNode | - | |
|
||||
| maxTagTextLength | 最大显示的 tag 文本长度 | number | - | |
|
||||
| menuItemSelectedIcon | 自定义多选时当前选中的条目图标 | ReactNode | - | |
|
||||
| mode | 设置 Select 的模式为多选或标签 | `multiple` \| `tags` | - | |
|
||||
| notFoundContent | 当下拉列表为空时显示的内容 | ReactNode | `Not Found` | |
|
||||
| options | 数据化配置选项内容,相比 jsx 定义会获得更好的渲染性能 | { label, value }[] | - | |
|
||||
| optionFilterProp | 搜索时过滤对应的 option 属性,如设置为 children 表示对内嵌内容进行搜索。[示例](https://codesandbox.io/s/antd-reproduction-template-tk678) | string | `value` | |
|
||||
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 `value` | string | `children` | |
|
||||
| placeholder | 选择框默认文字 | string | - | |
|
||||
| showArrow | 是否显示下拉小箭头 | boolean | 单选为 true,多选为 false | |
|
||||
| showSearch | 使单选模式可搜索 | boolean | false | |
|
||||
| size | 选择框大小 | `large` \| `middle` \| `small` | - | |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
|
||||
| removeIcon | 自定义的多选框清除图标 | ReactNode | - | |
|
||||
| clearIcon | 自定义的多选框清空图标 | ReactNode | - | |
|
||||
| menuItemSelectedIcon | 自定义多选时当前选中的条目图标 | ReactNode | - | |
|
||||
| tokenSeparators | 在 tags 和 multiple 模式下自动分词的分隔符 | string\[] | - | |
|
||||
| value | 指定当前选中的条目 | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
|
||||
| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 4.1.0 |
|
||||
| onBlur | 失去焦点时回调 | function | - | |
|
||||
| onChange | 选中 option,或 input 的 value 变化时,调用此函数 | function(value, option:Option \| Array<Option>) | - | |
|
||||
| onClear | 清除内容时回调 | function | - | 4.6.0 |
|
||||
| onDeselect | 取消选中时调用,参数为选中项的 value (或 key) 值,仅在 multiple 或 tags 模式下生效 | function(string \| number \| LabeledValue) | - | |
|
||||
| onDeselect | 取消选中时调用,参数为选中项的 value (或 key) 值,仅在 `multiple` 或 `tags` 模式下生效 | function(string \| number \| LabeledValue) | - | |
|
||||
| onDropdownVisibleChange | 展开下拉菜单的回调 | function(open) | - | |
|
||||
| onFocus | 获得焦点时回调 | function | - | |
|
||||
| onInputKeyDown | 按键按下时回调 | function | - | |
|
||||
| onMouseEnter | 鼠标移入时回调 | function | - | |
|
||||
@ -70,11 +61,20 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
|
||||
| onPopupScroll | 下拉列表滚动时的回调 | function | - | |
|
||||
| onSearch | 文本框值变化时回调 | function(value: string) | - | |
|
||||
| onSelect | 被选中时调用,参数为选中项的 value (或 key) 值 | function(string \| number \| LabeledValue, option: Option) | - | |
|
||||
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |
|
||||
| open | 是否展开下拉菜单 | boolean | - | |
|
||||
| onDropdownVisibleChange | 展开下拉菜单的回调 | function(open) | - | |
|
||||
| loading | 加载中状态 | boolean | false | |
|
||||
| bordered | 是否有边框 | boolean | true | |
|
||||
| options | 数据化配置选项内容,相比 jsx 定义会获得更好的渲染性能 | { label, value }[] | - | |
|
||||
| optionFilterProp | 搜索时过滤对应的 option 属性,如设置为 children 表示对内嵌内容进行搜索。[示例](https://codesandbox.io/s/antd-reproduction-template-tk678) | string | `value` | |
|
||||
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 `value` | string | `children` | |
|
||||
| placeholder | 选择框默认文字 | string | - | |
|
||||
| removeIcon | 自定义的多选框清除图标 | ReactNode | - | |
|
||||
| showArrow | 是否显示下拉小箭头 | boolean | 单选为 true,多选为 false | |
|
||||
| showSearch | 使单选模式可搜索 | boolean | false | |
|
||||
| size | 选择框大小 | `large` \| `middle` \| `small` | - | |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
|
||||
| tagRender | 自定义 tag 内容 render | (props) => ReactNode | - | |
|
||||
| tokenSeparators | 在 `tags` 和 `multiple` 模式下自动分词的分隔符 | string\[] | - | |
|
||||
| value | 指定当前选中的条目 | string \| string\[]<br />number \| number\[]<br />LabeledValue \| LabeledValue[] | - | |
|
||||
| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 4.1.0 |
|
||||
|
||||
> 注意,如果发现下拉菜单跟随页面滚动,或者需要在其他弹层中触发 Select,请尝试使用 `getPopupContainer={triggerNode => triggerNode.parentElement}` 将下拉弹层渲染节点固定在触发器的父元素中。
|
||||
|
||||
@ -89,10 +89,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --------- | --------------------------------- | ---------------- | ------ | ---- |
|
||||
| className | Option 器类名 | string | - | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| title | 选中该 Option 后,Select 的 title | string | - | |
|
||||
| value | 默认根据此属性值进行筛选 | string \| number | - | |
|
||||
| className | Option 器类名 | string | - | |
|
||||
|
||||
### OptGroup props
|
||||
|
||||
|
49
components/space/Item.tsx
Normal file
49
components/space/Item.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import * as React from 'react';
|
||||
import { LastIndexContext } from '.';
|
||||
import { SizeType } from '../config-provider/SizeContext';
|
||||
|
||||
const spaceSize = {
|
||||
small: 8,
|
||||
middle: 16,
|
||||
large: 24,
|
||||
};
|
||||
|
||||
export interface ItemProps {
|
||||
className: string;
|
||||
children: React.ReactNode;
|
||||
index: number;
|
||||
direction?: 'horizontal' | 'vertical';
|
||||
size?: SizeType | number;
|
||||
marginDirection: 'marginLeft' | 'marginRight';
|
||||
}
|
||||
|
||||
export default function Item({
|
||||
className,
|
||||
direction,
|
||||
index,
|
||||
size,
|
||||
marginDirection,
|
||||
children,
|
||||
}: ItemProps) {
|
||||
const latestIndex = React.useContext(LastIndexContext);
|
||||
|
||||
if (children === null || children === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={
|
||||
index >= latestIndex
|
||||
? {}
|
||||
: {
|
||||
[direction === 'vertical' ? 'marginBottom' : marginDirection]:
|
||||
typeof size === 'string' ? spaceSize[size] : size,
|
||||
}
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -395,12 +395,6 @@ exports[`renders ./components/space/demo/debug.md correctly 1`] = `
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
/>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
/>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
|
@ -43,8 +43,8 @@ describe('Space', () => {
|
||||
</Space>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.ant-space-item').at(0).prop('style').marginRight).toBe(10);
|
||||
expect(wrapper.find('.ant-space-item').at(1).prop('style').marginRight).toBeUndefined();
|
||||
expect(wrapper.find('div.ant-space-item').at(0).prop('style').marginRight).toBe(10);
|
||||
expect(wrapper.find('div.ant-space-item').at(1).prop('style').marginRight).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should render vertical space width customize size', () => {
|
||||
@ -55,8 +55,8 @@ describe('Space', () => {
|
||||
</Space>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.ant-space-item').at(0).prop('style').marginBottom).toBe(10);
|
||||
expect(wrapper.find('.ant-space-item').at(1).prop('style').marginBottom).toBeUndefined();
|
||||
expect(wrapper.find('div.ant-space-item').at(0).prop('style').marginBottom).toBe(10);
|
||||
expect(wrapper.find('div.ant-space-item').at(1).prop('style').marginBottom).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should render correct with children', () => {
|
||||
@ -78,7 +78,7 @@ describe('Space', () => {
|
||||
</Space>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.ant-space-item').length).toBe(3);
|
||||
expect(wrapper.find('div.ant-space-item').length).toBe(3);
|
||||
});
|
||||
|
||||
it('should be keep store', () => {
|
||||
|
@ -32,6 +32,8 @@ ReactDOM.render(
|
||||
{false}
|
||||
{1}
|
||||
Button
|
||||
{null}
|
||||
{undefined}
|
||||
</Space>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -2,6 +2,9 @@ import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { ConfigConsumerProps, ConfigContext } from '../config-provider';
|
||||
import { SizeType } from '../config-provider/SizeContext';
|
||||
import Item from './Item';
|
||||
|
||||
export const LastIndexContext = React.createContext(0);
|
||||
|
||||
export interface SpaceProps {
|
||||
prefixCls?: string;
|
||||
@ -13,12 +16,6 @@ export interface SpaceProps {
|
||||
align?: 'start' | 'end' | 'center' | 'baseline';
|
||||
}
|
||||
|
||||
const spaceSize = {
|
||||
small: 8,
|
||||
middle: 16,
|
||||
large: 24,
|
||||
};
|
||||
|
||||
const Space: React.FC<SpaceProps> = props => {
|
||||
const { getPrefixCls, space, direction: directionConfig }: ConfigConsumerProps = React.useContext(
|
||||
ConfigContext,
|
||||
@ -56,25 +53,32 @@ const Space: React.FC<SpaceProps> = props => {
|
||||
|
||||
const marginDirection = directionConfig === 'rtl' ? 'marginLeft' : 'marginRight';
|
||||
|
||||
// Calculate latest one
|
||||
let latestIndex = 0;
|
||||
const nodes = React.Children.map(children, (child, i) => {
|
||||
if (child !== null && child !== undefined) {
|
||||
latestIndex = i;
|
||||
}
|
||||
|
||||
/* eslint-disable react/no-array-index-key */
|
||||
return (
|
||||
<Item
|
||||
className={itemClassName}
|
||||
key={`${itemClassName}-${i}`}
|
||||
direction={direction}
|
||||
size={size}
|
||||
index={i}
|
||||
marginDirection={marginDirection}
|
||||
>
|
||||
{child}
|
||||
</Item>
|
||||
);
|
||||
/* eslint-enable */
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={cn} {...otherProps}>
|
||||
{React.Children.map(children, (child, i) => (
|
||||
<div
|
||||
className={itemClassName}
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={`${itemClassName}-${i}`}
|
||||
style={
|
||||
i === len - 1 || child === null || child === undefined
|
||||
? {}
|
||||
: {
|
||||
[direction === 'vertical' ? 'marginBottom' : marginDirection]:
|
||||
typeof size === 'string' ? spaceSize[size] : size,
|
||||
}
|
||||
}
|
||||
>
|
||||
{child}
|
||||
</div>
|
||||
))}
|
||||
<LastIndexContext.Provider value={latestIndex}>{nodes}</LastIndexContext.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -172,6 +172,7 @@ Properties for expandable.
|
||||
| defaultExpandedRowKeys | Initial expanded row keys | string\[] | - |
|
||||
| expandIcon | Customize row expand Icon. Ref [example](https://codesandbox.io/s/fervent-bird-nuzpr) | function(props): ReactNode | - |
|
||||
| expandIconColumnIndex | Customize expand icon column index. Not render when `-1` | number | - |
|
||||
| expandedRowClassName | Expanded row's className | function(record, index, indent): string | - |
|
||||
| expandedRowKeys | Current expanded row keys | string\[] | - |
|
||||
| expandedRowRender | Expanded container render for each row | function(record, index, indent, expanded): ReactNode | - |
|
||||
| expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | false |
|
||||
|
@ -179,6 +179,7 @@ const columns = [
|
||||
| defaultExpandedRowKeys | 默认展开的行 | string\[] | - |
|
||||
| expandIcon | 自定义展开图标,参考[示例](https://codesandbox.io/s/fervent-bird-nuzpr) | function(props): ReactNode | - |
|
||||
| expandIconColumnIndex | 自定义展开按钮的列顺序,`-1` 时不展示 | number | - |
|
||||
| expandedRowClassName | 展开行的 className | function(record, index, indent): string | - |
|
||||
| expandedRowKeys | 展开的行,控制属性 | string\[] | - |
|
||||
| expandedRowRender | 额外的展开行 | function(record, index, indent, expanded): ReactNode | - |
|
||||
| expandRowByClick | 通过点击行来展开子行 | boolean | false |
|
||||
|
@ -412,12 +412,13 @@
|
||||
float: left;
|
||||
box-sizing: border-box;
|
||||
|
||||
width: ceil(@font-size-sm * 1.4);
|
||||
height: ceil(@font-size-sm * 1.4);
|
||||
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;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
line-height: @font-size-sm;
|
||||
vertical-align: floor((@font-size-base - ceil(@font-size-sm * 1.4)) / 2);
|
||||
line-height: ceil((@font-size-sm * 1.4 - @border-width-base * 3) / 2) * 2 + @border-width-base *
|
||||
3;
|
||||
// vertical-align: floor((@font-size-base - ceil(@font-size-sm * 1.4)) / 2);
|
||||
background: @table-expand-icon-bg;
|
||||
border: @border-width-base @border-style-base @border-color-split;
|
||||
border-radius: @border-radius-base;
|
||||
@ -440,7 +441,7 @@
|
||||
}
|
||||
|
||||
&::before {
|
||||
top: 7px;
|
||||
top: ceil((@font-size-sm * 1.4 - @border-width-base * 3) / 2);
|
||||
right: 3px;
|
||||
left: 3px;
|
||||
height: @border-width-base;
|
||||
@ -449,7 +450,7 @@
|
||||
&::after {
|
||||
top: 3px;
|
||||
bottom: 3px;
|
||||
left: 7px;
|
||||
left: ceil((@font-size-sm * 1.4 - @border-width-base * 3) / 2);
|
||||
width: @border-width-base;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
@ -474,7 +475,8 @@
|
||||
}
|
||||
|
||||
.@{table-prefix-cls}-row-indent + & {
|
||||
margin-top: (@font-size-base * @line-height-base - ceil(@font-size-sm * 1.4)) / 2;
|
||||
margin-top: (@font-size-base * @line-height-base - @border-width-base * 3) / 2 -
|
||||
ceil((@font-size-sm * 1.4 - @border-width-base * 3) / 2);
|
||||
margin-right: @padding-xs;
|
||||
}
|
||||
}
|
||||
|
@ -30,16 +30,16 @@ const OperationsSlot = {
|
||||
const options = ['left', 'right'];
|
||||
|
||||
const Demo = () => {
|
||||
const [positon, setPosition] = React.useState(['left', 'right']);
|
||||
const [position, setPosition] = React.useState(['left', 'right']);
|
||||
|
||||
const slot = React.useMemo(() => {
|
||||
if (positon.length === 0) return null;
|
||||
if (position.length === 0) return null;
|
||||
|
||||
return positon.reduce(
|
||||
(acc, direaction) => ({ ...acc, [direaction]: OperationsSlot[direaction] }),
|
||||
return position.reduce(
|
||||
(acc, direction) => ({ ...acc, [direction]: OperationsSlot[direction] }),
|
||||
{},
|
||||
);
|
||||
}, [positon]);
|
||||
}, [position]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -61,7 +61,7 @@ const Demo = () => {
|
||||
<Divider />
|
||||
<CheckboxGroup
|
||||
options={options}
|
||||
value={positon}
|
||||
value={position}
|
||||
onChange={value => {
|
||||
setPosition(value);
|
||||
}}
|
||||
|
@ -45,4 +45,9 @@ describe('TreeSelect', () => {
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('should support notFoundContent', () => {
|
||||
const wrapper = mount(<TreeSelect treeIcon open notFoundContent="notFoundContent" />);
|
||||
expect(wrapper.text()).toBe('notFoundContent');
|
||||
});
|
||||
});
|
||||
|
@ -35,12 +35,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ========================== Tree ==========================
|
||||
.antTreeFn(@select-tree-prefix-cls);
|
||||
|
||||
// change switcher icon rotation in rtl direction
|
||||
.@{select-tree-prefix-cls} {
|
||||
// >>> Switcher
|
||||
.antTreeFn(@select-tree-prefix-cls);
|
||||
|
||||
// change switcher icon rotation in rtl direction
|
||||
& &-switcher {
|
||||
&_close {
|
||||
.@{select-tree-prefix-cls}-switcher-icon {
|
||||
|
@ -9,6 +9,8 @@
|
||||
|
||||
.antCheckboxFn(@checkbox-prefix-cls: ~'@{ant-prefix}-tree-checkbox');
|
||||
|
||||
.antTreeFn(@tree-prefix-cls);
|
||||
.@{tree-prefix-cls} {
|
||||
.antTreeFn(@tree-prefix-cls);
|
||||
}
|
||||
|
||||
@import './rtl';
|
||||
|
@ -31,233 +31,231 @@
|
||||
|
||||
.antTreeFn(@custom-tree-prefix-cls) {
|
||||
@custom-tree-node-prefix-cls: ~'@{custom-tree-prefix-cls}-treenode';
|
||||
.@{custom-tree-prefix-cls} {
|
||||
.reset-component;
|
||||
background: @tree-bg;
|
||||
.reset-component;
|
||||
background: @tree-bg;
|
||||
border-radius: @border-radius-base;
|
||||
transition: background-color 0.3s;
|
||||
|
||||
&-focused:not(:hover):not(&-active-focused) {
|
||||
background: @primary-1;
|
||||
}
|
||||
|
||||
// =================== Virtual List ===================
|
||||
&-list-holder-inner {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&.@{custom-tree-prefix-cls}-block-node {
|
||||
.@{custom-tree-prefix-cls}-list-holder-inner {
|
||||
align-items: stretch;
|
||||
|
||||
// >>> Title
|
||||
.@{custom-tree-prefix-cls}-node-content-wrapper {
|
||||
flex: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== TreeNode =====================
|
||||
.@{custom-tree-node-prefix-cls} {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 0 0 (@padding-xs / 2) 0;
|
||||
outline: none;
|
||||
// Disabled
|
||||
&-disabled {
|
||||
// >>> Title
|
||||
.@{custom-tree-prefix-cls}-node-content-wrapper {
|
||||
color: @disabled-color;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-active .@{custom-tree-prefix-cls}-node-content-wrapper {
|
||||
background: @tree-node-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
// >>> Indent
|
||||
&-indent {
|
||||
align-self: stretch;
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
|
||||
&-unit {
|
||||
display: inline-block;
|
||||
width: @tree-title-height;
|
||||
}
|
||||
}
|
||||
|
||||
// >>> Switcher
|
||||
& &-switcher {
|
||||
.antTreeSwitcherIcon();
|
||||
flex: none;
|
||||
|
||||
width: @tree-title-height;
|
||||
height: @tree-title-height;
|
||||
margin: 0;
|
||||
line-height: @tree-title-height;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
&-noop {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&_close {
|
||||
.@{custom-tree-prefix-cls}-switcher-icon {
|
||||
svg {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-loading-icon {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
&-leaf-line {
|
||||
z-index: 1;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
&::before {
|
||||
position: absolute;
|
||||
height: @tree-title-height;
|
||||
margin-left: -1px;
|
||||
border-left: 1px solid @normal-color;
|
||||
content: ' ';
|
||||
}
|
||||
&::after {
|
||||
position: absolute;
|
||||
width: @tree-title-height - 14px;
|
||||
height: @tree-title-height - 10px;
|
||||
margin-left: -1px;
|
||||
border-bottom: 1px solid @normal-color;
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// >>> Checkbox
|
||||
& &-checkbox {
|
||||
top: initial;
|
||||
margin: ((@tree-title-height - @checkbox-size) / 2) 8px 0 0;
|
||||
}
|
||||
|
||||
// >>> Title
|
||||
& &-node-content-wrapper {
|
||||
min-height: @tree-title-height;
|
||||
margin: 0;
|
||||
padding: 0 4px;
|
||||
color: inherit;
|
||||
line-height: @tree-title-height;
|
||||
background: transparent;
|
||||
border-radius: @border-radius-base;
|
||||
transition: background-color 0.3s;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s, border 0s, line-height 0s;
|
||||
|
||||
&-focused:not(:hover):not(&-active-focused) {
|
||||
background: @primary-1;
|
||||
&:hover {
|
||||
background-color: @tree-node-hover-bg;
|
||||
}
|
||||
|
||||
// =================== Virtual List ===================
|
||||
&-list-holder-inner {
|
||||
align-items: flex-start;
|
||||
&.@{custom-tree-prefix-cls}-node-selected {
|
||||
background-color: @tree-node-selected-bg;
|
||||
}
|
||||
|
||||
&.@{custom-tree-prefix-cls}-block-node {
|
||||
.@{custom-tree-prefix-cls}-list-holder-inner {
|
||||
align-items: stretch;
|
||||
|
||||
// >>> Title
|
||||
.@{custom-tree-prefix-cls}-node-content-wrapper {
|
||||
flex: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== TreeNode =====================
|
||||
.@{custom-tree-node-prefix-cls} {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 0 0 (@padding-xs / 2) 0;
|
||||
outline: none;
|
||||
// Disabled
|
||||
&-disabled {
|
||||
// >>> Title
|
||||
.@{custom-tree-prefix-cls}-node-content-wrapper {
|
||||
color: @disabled-color;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-active .@{custom-tree-prefix-cls}-node-content-wrapper {
|
||||
background: @tree-node-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
// >>> Indent
|
||||
&-indent {
|
||||
align-self: stretch;
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
|
||||
&-unit {
|
||||
display: inline-block;
|
||||
width: @tree-title-height;
|
||||
}
|
||||
}
|
||||
|
||||
// >>> Switcher
|
||||
& &-switcher {
|
||||
.antTreeSwitcherIcon();
|
||||
flex: none;
|
||||
|
||||
// Icon
|
||||
.@{custom-tree-prefix-cls}-iconEle {
|
||||
display: inline-block;
|
||||
width: @tree-title-height;
|
||||
height: @tree-title-height;
|
||||
margin: 0;
|
||||
line-height: @tree-title-height;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
&-noop {
|
||||
cursor: default;
|
||||
vertical-align: top;
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&_close {
|
||||
.@{custom-tree-prefix-cls}-switcher-icon {
|
||||
svg {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
// ==================== Draggable =====================
|
||||
&-node-content-wrapper[draggable='true'] {
|
||||
line-height: @tree-title-height - 4px;
|
||||
border-top: 2px transparent solid;
|
||||
border-bottom: 2px transparent solid;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
&-loading-icon {
|
||||
color: @primary-color;
|
||||
}
|
||||
.@{custom-tree-node-prefix-cls}.drag-over {
|
||||
> [draggable] {
|
||||
color: white;
|
||||
background-color: @primary-color;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
.@{custom-tree-node-prefix-cls}.drag-over-gap-top {
|
||||
> [draggable] {
|
||||
border-top-color: @primary-color;
|
||||
}
|
||||
}
|
||||
.@{custom-tree-node-prefix-cls}.drag-over-gap-bottom {
|
||||
> [draggable] {
|
||||
border-bottom-color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-leaf-line {
|
||||
z-index: 1;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
// ==================== Show Line =====================
|
||||
&-show-line {
|
||||
// ================ Indent lines ================
|
||||
.@{custom-tree-prefix-cls}-indent {
|
||||
&-unit {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
&:first-child::after {
|
||||
position: absolute;
|
||||
top: calc(100% - @tree-title-height - 4px);
|
||||
right: @tree-title-height / 2;
|
||||
bottom: -4px;
|
||||
border-right: 1px solid @border-color-base;
|
||||
content: '';
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
height: @tree-title-height;
|
||||
margin-left: -1px;
|
||||
border-left: 1px solid @normal-color;
|
||||
content: ' ';
|
||||
top: calc(100% - 4px);
|
||||
right: -@tree-title-height / 2;
|
||||
bottom: -@tree-title-height - 4px;
|
||||
border-right: 1px solid @border-color-base;
|
||||
content: '';
|
||||
}
|
||||
&::after {
|
||||
position: absolute;
|
||||
width: @tree-title-height - 14px;
|
||||
height: @tree-title-height - 10px;
|
||||
margin-left: -1px;
|
||||
border-bottom: 1px solid @normal-color;
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// >>> Checkbox
|
||||
& &-checkbox {
|
||||
top: initial;
|
||||
margin: ((@tree-title-height - @checkbox-size) / 2) 8px 0 0;
|
||||
}
|
||||
|
||||
// >>> Title
|
||||
& &-node-content-wrapper {
|
||||
min-height: @tree-title-height;
|
||||
margin: 0;
|
||||
padding: 0 4px;
|
||||
color: inherit;
|
||||
line-height: @tree-title-height;
|
||||
background: transparent;
|
||||
border-radius: @border-radius-base;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
background-color: @tree-node-hover-bg;
|
||||
}
|
||||
|
||||
&.@{custom-tree-prefix-cls}-node-selected {
|
||||
background-color: @tree-node-selected-bg;
|
||||
}
|
||||
|
||||
// Icon
|
||||
.@{custom-tree-prefix-cls}-iconEle {
|
||||
display: inline-block;
|
||||
width: @tree-title-height;
|
||||
height: @tree-title-height;
|
||||
line-height: @tree-title-height;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
&:empty {
|
||||
&-end::before,
|
||||
&-end-first-level::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Draggable =====================
|
||||
&-node-content-wrapper[draggable='true'] {
|
||||
line-height: @tree-title-height - 4px;
|
||||
border-top: 2px transparent solid;
|
||||
border-bottom: 2px transparent solid;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.@{custom-tree-node-prefix-cls}.drag-over {
|
||||
> [draggable] {
|
||||
color: white;
|
||||
background-color: @primary-color;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
.@{custom-tree-node-prefix-cls}.drag-over-gap-top {
|
||||
> [draggable] {
|
||||
border-top-color: @primary-color;
|
||||
}
|
||||
}
|
||||
.@{custom-tree-node-prefix-cls}.drag-over-gap-bottom {
|
||||
> [draggable] {
|
||||
border-bottom-color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Show Line =====================
|
||||
&-show-line {
|
||||
// ================ Indent lines ================
|
||||
.@{custom-tree-prefix-cls}-indent {
|
||||
&-unit {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
&:first-child::after {
|
||||
position: absolute;
|
||||
top: calc(100% - @tree-title-height - 4px);
|
||||
right: @tree-title-height / 2;
|
||||
bottom: -4px;
|
||||
border-right: 1px solid @border-color-base;
|
||||
content: '';
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: calc(100% - 4px);
|
||||
right: -@tree-title-height / 2;
|
||||
bottom: -@tree-title-height - 4px;
|
||||
border-right: 1px solid @border-color-base;
|
||||
content: '';
|
||||
}
|
||||
|
||||
&-end::before,
|
||||
&-end-first-level::after {
|
||||
display: none;
|
||||
}
|
||||
/* Motion should hide line of measure */
|
||||
.@{custom-tree-node-prefix-cls}-motion:not(.@{tree-motion}-leave):not(.@{tree-motion}-appear-active) {
|
||||
.@{custom-tree-prefix-cls}-indent-unit {
|
||||
&::after,
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Motion should hide line of measure */
|
||||
.@{custom-tree-node-prefix-cls}-motion:not(.@{tree-motion}-leave):not(.@{tree-motion}-appear-active) {
|
||||
.@{custom-tree-prefix-cls}-indent-unit {
|
||||
&::after,
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============== Cover Background ==============
|
||||
.@{custom-tree-prefix-cls}-switcher {
|
||||
z-index: 1;
|
||||
background: @component-background;
|
||||
}
|
||||
// ============== Cover Background ==============
|
||||
.@{custom-tree-prefix-cls}-switcher {
|
||||
z-index: 1;
|
||||
background: @component-background;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import Dragger from './Dragger';
|
||||
import UploadList from './UploadList';
|
||||
import {
|
||||
RcFile,
|
||||
ShowUploadListInterface,
|
||||
UploadProps,
|
||||
UploadFile,
|
||||
UploadLocale,
|
||||
@ -217,35 +218,6 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
|
||||
forceUpdate,
|
||||
}));
|
||||
|
||||
const renderUploadList = (locale: UploadLocale) => {
|
||||
const {
|
||||
showRemoveIcon,
|
||||
showPreviewIcon,
|
||||
showDownloadIcon,
|
||||
removeIcon,
|
||||
downloadIcon,
|
||||
} = showUploadList as any;
|
||||
return (
|
||||
<UploadList
|
||||
listType={listType}
|
||||
items={getFileList()}
|
||||
previewFile={previewFile}
|
||||
onPreview={onPreview}
|
||||
onDownload={onDownload}
|
||||
onRemove={handleRemove}
|
||||
showRemoveIcon={!disabled && showRemoveIcon}
|
||||
showPreviewIcon={showPreviewIcon}
|
||||
showDownloadIcon={showDownloadIcon}
|
||||
removeIcon={removeIcon}
|
||||
downloadIcon={downloadIcon}
|
||||
iconRender={iconRender}
|
||||
locale={{ ...locale, ...propLocale }}
|
||||
isImageUrl={isImageUrl}
|
||||
progress={progress}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
|
||||
const prefixCls = getPrefixCls('upload', customizePrefixCls);
|
||||
@ -271,11 +243,37 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
|
||||
delete rcUploadProps.id;
|
||||
}
|
||||
|
||||
const uploadList = showUploadList ? (
|
||||
<LocaleReceiver componentName="Upload" defaultLocale={defaultLocale.Upload}>
|
||||
{renderUploadList}
|
||||
</LocaleReceiver>
|
||||
) : null;
|
||||
const renderUploadList = (button?: React.ReactNode) =>
|
||||
showUploadList ? (
|
||||
<LocaleReceiver componentName="Upload" defaultLocale={defaultLocale.Upload}>
|
||||
{(locale: UploadLocale) => {
|
||||
const { showRemoveIcon, showPreviewIcon, showDownloadIcon, removeIcon, downloadIcon } =
|
||||
typeof showUploadList === 'boolean' ? ({} as ShowUploadListInterface) : showUploadList;
|
||||
return (
|
||||
<UploadList
|
||||
listType={listType}
|
||||
items={getFileList()}
|
||||
previewFile={previewFile}
|
||||
onPreview={onPreview}
|
||||
onDownload={onDownload}
|
||||
onRemove={handleRemove}
|
||||
showRemoveIcon={!disabled && showRemoveIcon}
|
||||
showPreviewIcon={showPreviewIcon}
|
||||
showDownloadIcon={showDownloadIcon}
|
||||
removeIcon={removeIcon}
|
||||
downloadIcon={downloadIcon}
|
||||
iconRender={iconRender}
|
||||
locale={{ ...locale, ...propLocale }}
|
||||
isImageUrl={isImageUrl}
|
||||
progress={progress}
|
||||
appendAction={button}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</LocaleReceiver>
|
||||
) : (
|
||||
button
|
||||
);
|
||||
|
||||
if (type === 'drag') {
|
||||
const dragCls = classNames(
|
||||
@ -302,7 +300,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
|
||||
<div className={`${prefixCls}-drag-container`}>{children}</div>
|
||||
</RcUpload>
|
||||
</div>
|
||||
{uploadList}
|
||||
{renderUploadList()}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -323,8 +321,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
|
||||
if (listType === 'picture-card') {
|
||||
return (
|
||||
<span className={classNames(className, `${prefixCls}-picture-card-wrapper`)}>
|
||||
{uploadList}
|
||||
{uploadButton}
|
||||
{renderUploadList(uploadButton)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -332,7 +329,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
|
||||
return (
|
||||
<span className={className}>
|
||||
{uploadButton}
|
||||
{uploadList}
|
||||
{renderUploadList()}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
@ -36,6 +36,7 @@ const InternalUploadList: React.ForwardRefRenderFunction<unknown, UploadListProp
|
||||
removeIcon: customRemoveIcon,
|
||||
downloadIcon: customDownloadIcon,
|
||||
progress: progressProps,
|
||||
appendAction,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
@ -334,13 +335,13 @@ const InternalUploadList: React.ForwardRefRenderFunction<unknown, UploadListProp
|
||||
[`${prefixCls}-list-rtl`]: direction === 'rtl',
|
||||
});
|
||||
const animationDirection = listType === 'picture-card' ? 'animate-inline' : 'animate';
|
||||
const transitionName = list.length === 0 ? '' : `${prefixCls}-${animationDirection}`;
|
||||
return (
|
||||
<Animate
|
||||
transitionName={`${prefixCls}-${animationDirection}`}
|
||||
component="div"
|
||||
className={listClassNames}
|
||||
>
|
||||
<Animate transitionName={transitionName} component="div" className={listClassNames}>
|
||||
{list}
|
||||
{React.isValidElement(appendAction)
|
||||
? React.cloneElement(appendAction, { key: 'appendAction' })
|
||||
: appendAction}
|
||||
</Animate>
|
||||
);
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -10,4 +10,21 @@ describe('Upload.typescript', () => {
|
||||
);
|
||||
expect(upload).toBeTruthy();
|
||||
});
|
||||
|
||||
it('showUploadList', () => {
|
||||
const upload = (
|
||||
<Upload
|
||||
showUploadList={{
|
||||
showPreviewIcon: true,
|
||||
showRemoveIcon: true,
|
||||
showDownloadIcon: true,
|
||||
removeIcon: 'Remove',
|
||||
downloadIcon: 'Download',
|
||||
}}
|
||||
>
|
||||
<span>click to upload</span>
|
||||
</Upload>
|
||||
);
|
||||
expect(upload).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -880,4 +880,27 @@ describe('Upload List', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should render button inside UploadList when listStyle is picture-card', () => {
|
||||
const wrapper = mount(
|
||||
<Upload
|
||||
action="http://jsonplaceholder.typicode.com/posts/"
|
||||
listType="picture-card"
|
||||
fileList={[
|
||||
{
|
||||
uid: '0',
|
||||
name: 'xxx.png',
|
||||
},
|
||||
]}
|
||||
showUploadList
|
||||
>
|
||||
<button className="trigger" type="button">
|
||||
upload
|
||||
</button>
|
||||
</Upload>,
|
||||
);
|
||||
expect(wrapper.exists('.ant-upload-list button.trigger')).toBe(true);
|
||||
wrapper.setProps({ showUploadList: false });
|
||||
expect(wrapper.exists('.ant-upload-list button.trigger')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
@ -61,13 +61,13 @@ class Avatar extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { loading, imageUrl } = this.state;
|
||||
const uploadButton = (
|
||||
<div>
|
||||
{this.state.loading ? <LoadingOutlined /> : <PlusOutlined />}
|
||||
<div className="ant-upload-text">Upload</div>
|
||||
{loading ? <LoadingOutlined /> : <PlusOutlined />}
|
||||
<div style={{ marginTop: 8 }}>Upload</div>
|
||||
</div>
|
||||
);
|
||||
const { imageUrl } = this.state;
|
||||
return (
|
||||
<Upload
|
||||
name="avatar"
|
||||
|
@ -37,9 +37,7 @@ const props = {
|
||||
|
||||
ReactDOM.render(
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<UploadOutlined /> Click to Upload
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Click to Upload</Button>
|
||||
</Upload>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -45,9 +45,7 @@ const props = {
|
||||
|
||||
ReactDOM.render(
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<UploadOutlined /> Click to Upload
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Click to Upload</Button>
|
||||
</Upload>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -50,9 +50,7 @@ const props = {
|
||||
|
||||
ReactDOM.render(
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Upload</Button>
|
||||
</Upload>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -19,9 +19,7 @@ import { UploadOutlined } from '@ant-design/icons';
|
||||
|
||||
ReactDOM.render(
|
||||
<Upload action="https://www.mocky.io/v2/5cc8019d300000980a055e76" directory>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload Directory
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Upload Directory</Button>
|
||||
</Upload>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -116,11 +116,11 @@ class PicturesWall extends React.Component {
|
||||
const uploadButton = (
|
||||
<div>
|
||||
<PlusOutlined />
|
||||
<div className="ant-upload-text">Upload</div>
|
||||
<div style={{ marginTop: 8 }}>Upload</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="clearfix">
|
||||
<>
|
||||
<Upload
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
listType="picture-card"
|
||||
@ -134,23 +134,10 @@ class PicturesWall extends React.Component {
|
||||
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
|
||||
<img alt="example" style={{ width: '100%' }} src={previewImage} />
|
||||
</Modal>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<PicturesWall />, mountNode);
|
||||
```
|
||||
|
||||
```css
|
||||
/* you can make up upload button and sample style by using stylesheets */
|
||||
.ant-upload-select-picture-card i {
|
||||
color: #999;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.ant-upload-select-picture-card .ant-upload-text {
|
||||
margin-top: 8px;
|
||||
color: #666;
|
||||
}
|
||||
```
|
||||
|
@ -64,9 +64,7 @@ class MyUpload extends React.Component {
|
||||
};
|
||||
return (
|
||||
<Upload {...props} fileList={this.state.fileList}>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Upload</Button>
|
||||
</Upload>
|
||||
);
|
||||
}
|
||||
|
@ -56,6 +56,13 @@ class PicturesWall extends React.Component {
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
{
|
||||
uid: '-xxx',
|
||||
percent: 50,
|
||||
name: 'image.png',
|
||||
status: 'uploading',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
{
|
||||
uid: '-5',
|
||||
name: 'image.png',
|
||||
@ -85,11 +92,11 @@ class PicturesWall extends React.Component {
|
||||
const uploadButton = (
|
||||
<div>
|
||||
<PlusOutlined />
|
||||
<div className="ant-upload-text">Upload</div>
|
||||
<div style={{ marginTop: 8 }}>Upload</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="clearfix">
|
||||
<>
|
||||
<Upload
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
listType="picture-card"
|
||||
@ -107,23 +114,10 @@ class PicturesWall extends React.Component {
|
||||
>
|
||||
<img alt="example" style={{ width: '100%' }} src={previewImage} />
|
||||
</Modal>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<PicturesWall />, mountNode);
|
||||
```
|
||||
|
||||
```css
|
||||
/* you can make up upload button and sample style by using stylesheets */
|
||||
.ant-upload-select-picture-card i {
|
||||
color: #999;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.ant-upload-select-picture-card .ant-upload-text {
|
||||
margin-top: 8px;
|
||||
color: #666;
|
||||
}
|
||||
```
|
||||
|
@ -32,32 +32,24 @@ const fileList = [
|
||||
},
|
||||
];
|
||||
|
||||
const props = {
|
||||
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
|
||||
listType: 'picture',
|
||||
defaultFileList: [...fileList],
|
||||
};
|
||||
|
||||
const props2 = {
|
||||
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
|
||||
listType: 'picture',
|
||||
defaultFileList: [...fileList],
|
||||
className: 'upload-list-inline',
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload
|
||||
</Button>
|
||||
<Upload
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
listType="picture"
|
||||
defaultFileList={[...fileList]}
|
||||
>
|
||||
<Button icon={<UploadOutlined />}>Upload</Button>
|
||||
</Upload>
|
||||
<br />
|
||||
<br />
|
||||
<Upload {...props2}>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload
|
||||
</Button>
|
||||
<Upload
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
listType="picture"
|
||||
defaultFileList={[...fileList]}
|
||||
className="upload-list-inline"
|
||||
>
|
||||
<Button icon={<UploadOutlined />}>Upload</Button>
|
||||
</Upload>
|
||||
</>,
|
||||
mountNode,
|
||||
@ -75,10 +67,4 @@ ReactDOM.render(
|
||||
.upload-list-inline [class*='-upload-list-rtl'] .ant-upload-list-item {
|
||||
float: right;
|
||||
}
|
||||
.upload-list-inline .ant-upload-animate-enter {
|
||||
animation-name: uploadAnimateInlineIn;
|
||||
}
|
||||
.upload-list-inline .ant-upload-animate-leave {
|
||||
animation-name: uploadAnimateInlineOut;
|
||||
}
|
||||
```
|
||||
|
@ -34,9 +34,7 @@ const props = {
|
||||
|
||||
ReactDOM.render(
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Upload</Button>
|
||||
</Upload>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -43,9 +43,7 @@ const props = {
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Upload</Button>
|
||||
</Upload>
|
||||
</>,
|
||||
mountNode,
|
||||
|
@ -56,9 +56,7 @@ const props = {
|
||||
|
||||
ReactDOM.render(
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Upload</Button>
|
||||
</Upload>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -82,9 +82,7 @@ class Demo extends React.Component {
|
||||
return (
|
||||
<>
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<UploadOutlined /> Select File
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Select File</Button>
|
||||
</Upload>
|
||||
<Button
|
||||
type="primary"
|
||||
|
@ -36,9 +36,7 @@ const Uploader = () => {
|
||||
};
|
||||
return (
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload png only
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Upload png only</Button>
|
||||
</Upload>
|
||||
);
|
||||
};
|
||||
|
@ -114,9 +114,7 @@ class AliyunOSSUpload extends React.Component {
|
||||
};
|
||||
return (
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<UploadOutlined /> Click to Upload
|
||||
</Button>
|
||||
<Button icon={<UploadOutlined />}>Click to Upload</Button>
|
||||
</Upload>
|
||||
);
|
||||
}
|
||||
|
@ -56,6 +56,8 @@ export interface ShowUploadListInterface {
|
||||
showRemoveIcon?: boolean;
|
||||
showPreviewIcon?: boolean;
|
||||
showDownloadIcon?: boolean;
|
||||
removeIcon?: React.ReactNode;
|
||||
downloadIcon?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface UploadLocale {
|
||||
@ -133,4 +135,5 @@ export interface UploadListProps<T = any> {
|
||||
previewFile?: PreviewFileHandler;
|
||||
iconRender?: (file: UploadFile<T>, listType?: UploadListType) => React.ReactNode;
|
||||
isImageUrl?: (file: UploadFile) => boolean;
|
||||
appendAction?: React.ReactNode;
|
||||
}
|
||||
|
@ -34,8 +34,6 @@
|
||||
}
|
||||
|
||||
&&-select-picture-card {
|
||||
display: table;
|
||||
float: left;
|
||||
width: @upload-picture-card-size;
|
||||
height: @upload-picture-card-size;
|
||||
margin-right: 8px;
|
||||
@ -46,15 +44,14 @@
|
||||
border: @border-width-base dashed @border-color-base;
|
||||
border-radius: @border-radius-base;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s ease;
|
||||
transition: border-color 0.3s;
|
||||
|
||||
> .@{upload-prefix-cls} {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: @padding-xs;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@ -408,17 +405,16 @@
|
||||
}
|
||||
|
||||
&-container {
|
||||
float: left;
|
||||
display: inline-block;
|
||||
width: @upload-picture-card-size;
|
||||
height: @upload-picture-card-size;
|
||||
margin: 0 @margin-xs @margin-xs 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.@{upload-item} {
|
||||
float: left;
|
||||
width: @upload-picture-card-size;
|
||||
height: @upload-picture-card-size;
|
||||
margin: 0 @margin-xs @margin-xs 0;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.@{upload-item}-info {
|
||||
@ -515,6 +511,7 @@
|
||||
|
||||
.@{upload-item}-progress {
|
||||
bottom: 32px;
|
||||
width: calc(100% - 14px);
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ title: 结果页
|
||||
|
||||
结果页是用一个页面反馈操作结果,是反馈模式中最强的一种。
|
||||
|
||||
## 设何时使用
|
||||
## 何时使用
|
||||
|
||||
当完成一个流程操作后,需给与用户明确的结果反馈时,例如分步表单的最后一步。<br/> 当有大量的信息需要在结果页展示时。
|
||||
|
||||
|
@ -90,7 +90,12 @@ module.exports = {
|
||||
config.resolve.alias = { ...config.resolve.alias, react: require.resolve('react') };
|
||||
} else if (process.env.ESBUILD) {
|
||||
// use esbuild
|
||||
config.optimization.minimizer = [new EsbuildPlugin(), new CssMinimizerPlugin()];
|
||||
config.optimization.minimizer = [
|
||||
new EsbuildPlugin({
|
||||
target: 'chrome49',
|
||||
}),
|
||||
new CssMinimizerPlugin(),
|
||||
];
|
||||
}
|
||||
|
||||
alertBabelConfig(config.module.rules);
|
||||
|
@ -99,7 +99,9 @@ if (process.env.RUN_ENV === 'PRODUCTION') {
|
||||
config.optimization.usedExports = true;
|
||||
// use esbuild
|
||||
if (process.env.ESBUILD || process.env.CSB_REPO) {
|
||||
config.optimization.minimizer[0] = new EsbuildPlugin();
|
||||
config.optimization.minimizer[0] = new EsbuildPlugin({
|
||||
target: 'chrome49',
|
||||
});
|
||||
}
|
||||
|
||||
config.plugins.push(
|
||||
|
Loading…
Reference in New Issue
Block a user