Merge branch 'feature' into master-to-merge-feature

This commit is contained in:
afc163 2021-01-14 14:26:09 +08:00
commit ea9c71a94e
15 changed files with 280 additions and 57 deletions

View File

@ -156,6 +156,55 @@ exports[`renders ./components/image/demo/placeholder.md correctly 1`] = `
</div> </div>
`; `;
exports[`renders ./components/image/demo/preview-mask.md correctly 1`] = `
<div
class="ant-image"
style="width:96px"
>
<img
class="ant-image-img"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
<div
class="ant-image-mask customize-mask"
>
<div
class="ant-space ant-space-vertical ant-space-align-center"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<span
aria-label="zoom-in"
class="anticon anticon-zoom-in"
role="img"
>
<svg
aria-hidden="true"
data-icon="zoom-in"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M637 443H519V309c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v134H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h118v134c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V519h118c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
/>
</svg>
</span>
</div>
<div
class="ant-space-item"
>
示例
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/image/demo/previewGroup.md correctly 1`] = ` exports[`renders ./components/image/demo/previewGroup.md correctly 1`] = `
Array [ Array [
<div <div

View File

@ -0,0 +1,51 @@
---
order: 99
title:
zh-CN: 自定义预览文本
en-US: Custom preview mask
debug: true
---
## zh-CN
自定义预览文本。
## en-US
Custom preview mask.
```tsx
import React from 'react';
import { Image, Space } from 'antd';
import { ZoomInOutlined } from '@ant-design/icons';
function ImageDemo() {
return (
<Image
width={96}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
preview={{
maskClassName: 'customize-mask',
mask: (
<Space direction="vertical" align="center">
<ZoomInOutlined />
示例
</Space>
),
}}
/>
);
}
ReactDOM.render(<ImageDemo />, mountNode);
```
```css
.customize-mask {
opacity: 1;
font-size: 20px;
}
.customize-mask .anticon {
font-size: 32px;
}
```

View File

@ -33,6 +33,8 @@ Previewable image.
onVisibleChange?: (visible, prevVisible) => void; onVisibleChange?: (visible, prevVisible) => void;
getContainer?: string | HTMLElement | (() => HTMLElement); // V4.8.0 getContainer?: string | HTMLElement | (() => HTMLElement); // V4.8.0
src?: string; // V4.10.0 src?: string; // V4.10.0
mask?: ReactNode; // V4.9.0
maskClassName?: string; // V4.11.0
} }
``` ```

View File

@ -34,6 +34,8 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/D1dXz9PZqa/image.svg
onVisibleChange?: (visible, prevVisible) => void; onVisibleChange?: (visible, prevVisible) => void;
getContainer?: string | HTMLElement | (() => HTMLElement); // V4.8.0 getContainer?: string | HTMLElement | (() => HTMLElement); // V4.8.0
src?: string; // V4.10.0 src?: string; // V4.10.0
mask?: ReactNode; // V4.9.0
maskClassName?: string; // V4.11.0
} }
``` ```

View File

@ -9,6 +9,8 @@ title:
通过 `filterDropdown` 自定义的列筛选功能,并实现一个搜索列的示例。 通过 `filterDropdown` 自定义的列筛选功能,并实现一个搜索列的示例。
给函数 `confirm` 添加 `boolean` 类型参数 `closeDropdown`,是否关闭筛选菜单,默认为 `true`
## en-US ## en-US
Implement a customized column search example via `filterDropdown`. Implement a customized column search example via `filterDropdown`.
@ -77,6 +79,19 @@ class App extends React.Component {
<Button onClick={() => this.handleReset(clearFilters)} size="small" style={{ width: 90 }}> <Button onClick={() => this.handleReset(clearFilters)} size="small" style={{ width: 90 }}>
Reset Reset
</Button> </Button>
<Button
type="link"
size="small"
onClick={() => {
confirm({ closeDropdown: false });
this.setState({
searchText: selectedKeys[0],
searchedColumn: dataIndex,
});
}}
>
Filter
</Button>
</Space> </Space>
</div> </div>
), ),

View File

@ -8,7 +8,14 @@ import Checkbox from '../../../checkbox';
import Radio from '../../../radio'; import Radio from '../../../radio';
import Dropdown from '../../../dropdown'; import Dropdown from '../../../dropdown';
import Empty from '../../../empty'; import Empty from '../../../empty';
import { ColumnType, ColumnFilterItem, Key, TableLocale, GetPopupContainer } from '../../interface'; import {
ColumnType,
ColumnFilterItem,
Key,
TableLocale,
GetPopupContainer,
FilterConfirmProps,
} from '../../interface';
import FilterDropdownMenuWrapper from './FilterWrapper'; import FilterDropdownMenuWrapper from './FilterWrapper';
import { FilterState } from '.'; import { FilterState } from '.';
import useSyncState from '../../../_util/hooks/useSyncState'; import useSyncState from '../../../_util/hooks/useSyncState';
@ -160,8 +167,6 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
// ======================= Submit ======================== // ======================= Submit ========================
const internalTriggerFilter = (keys: Key[] | undefined | null) => { const internalTriggerFilter = (keys: Key[] | undefined | null) => {
triggerVisible(false);
const mergedKeys = keys && keys.length ? keys : null; const mergedKeys = keys && keys.length ? keys : null;
if (mergedKeys === null && (!filterState || !filterState.filteredKeys)) { if (mergedKeys === null && (!filterState || !filterState.filteredKeys)) {
return null; return null;
@ -179,14 +184,21 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
}; };
const onConfirm = () => { const onConfirm = () => {
triggerVisible(false);
internalTriggerFilter(getFilteredKeysSync()); internalTriggerFilter(getFilteredKeysSync());
}; };
const onReset = () => { const onReset = () => {
setFilteredKeysSync([]); setFilteredKeysSync([]);
triggerVisible(false);
internalTriggerFilter([]); internalTriggerFilter([]);
}; };
const doFilter = (param: FilterConfirmProps = { closeDropdown: true }) => {
triggerVisible(!param.closeDropdown);
internalTriggerFilter(getFilteredKeysSync());
};
const onVisibleChange = (newVisible: boolean) => { const onVisibleChange = (newVisible: boolean) => {
if (newVisible && propFilteredKeys !== undefined) { if (newVisible && propFilteredKeys !== undefined) {
// Sync filteredKeys on appear in controlled mode (propFilteredKeys !== undefiend) // Sync filteredKeys on appear in controlled mode (propFilteredKeys !== undefiend)
@ -213,7 +225,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
prefixCls: `${dropdownPrefixCls}-custom`, prefixCls: `${dropdownPrefixCls}-custom`,
setSelectedKeys: (selectedKeys: Key[]) => onSelectKeys({ selectedKeys }), setSelectedKeys: (selectedKeys: Key[]) => onSelectKeys({ selectedKeys }),
selectedKeys: getFilteredKeysSync(), selectedKeys: getFilteredKeysSync(),
confirm: onConfirm, confirm: doFilter,
clearFilters: onReset, clearFilters: onReset,
filters: column.filters, filters: column.filters,
visible: mergedVisible, visible: mergedVisible,

View File

@ -67,11 +67,15 @@ export type ColumnTitle<RecordType> =
| React.ReactNode | React.ReactNode
| ((props: ColumnTitleProps<RecordType>) => React.ReactNode); | ((props: ColumnTitleProps<RecordType>) => React.ReactNode);
export interface FilterConfirmProps {
closeDropdown: boolean;
}
export interface FilterDropdownProps { export interface FilterDropdownProps {
prefixCls: string; prefixCls: string;
setSelectedKeys: (selectedKeys: React.Key[]) => void; setSelectedKeys: (selectedKeys: React.Key[]) => void;
selectedKeys: React.Key[]; selectedKeys: React.Key[];
confirm: () => void; confirm: (param: FilterConfirmProps) => void;
clearFilters?: () => void; clearFilters?: () => void;
filters?: ColumnFilterItem[]; filters?: ColumnFilterItem[];
visible: boolean; visible: boolean;

View File

@ -41,13 +41,14 @@ interface EditConfig {
autoSize?: boolean | AutoSizeType; autoSize?: boolean | AutoSizeType;
} }
interface EllipsisConfig { export interface EllipsisConfig {
rows?: number; rows?: number;
expandable?: boolean; expandable?: boolean;
suffix?: string; suffix?: string;
symbol?: React.ReactNode; symbol?: React.ReactNode;
onExpand?: React.MouseEventHandler<HTMLElement>; onExpand?: React.MouseEventHandler<HTMLElement>;
onEllipsis?: (ellipsis: boolean) => void; onEllipsis?: (ellipsis: boolean) => void;
tooltip?: React.ReactNode;
} }
export interface BlockProps extends TypographyProps { export interface BlockProps extends TypographyProps {
@ -287,9 +288,9 @@ class Base extends React.Component<InternalBlockProps, BaseState> {
canUseCSSEllipsis(): boolean { canUseCSSEllipsis(): boolean {
const { clientRendered } = this.state; const { clientRendered } = this.state;
const { editable, copyable } = this.props; const { editable, copyable } = this.props;
const { rows, expandable, suffix, onEllipsis } = this.getEllipsis(); const { rows, expandable, suffix, onEllipsis, tooltip } = this.getEllipsis();
if (suffix) return false; if (suffix || tooltip) return false;
// Can't use css ellipsis since we need to provide the place for button // Can't use css ellipsis since we need to provide the place for button
if (editable || copyable || expandable || !clientRendered || onEllipsis) { if (editable || copyable || expandable || !clientRendered || onEllipsis) {
return false; return false;
@ -441,7 +442,7 @@ class Base extends React.Component<InternalBlockProps, BaseState> {
const { ellipsisContent, isEllipsis, expanded } = this.state; const { ellipsisContent, isEllipsis, expanded } = this.state;
const { component, children, className, type, disabled, style, ...restProps } = this.props; const { component, children, className, type, disabled, style, ...restProps } = this.props;
const { direction } = this.context; const { direction } = this.context;
const { rows, suffix } = this.getEllipsis(); const { rows, suffix, tooltip } = this.getEllipsis();
const prefixCls = this.getPrefixCls(); const prefixCls = this.getPrefixCls();
@ -486,6 +487,15 @@ class Base extends React.Component<InternalBlockProps, BaseState> {
{suffix} {suffix}
</> </>
); );
// If provided tooltip, we need wrap with span to let Tooltip inject events
if (tooltip) {
textNode = (
<Tooltip title={tooltip === true ? children : tooltip}>
<span>{textNode}</span>
</Tooltip>
);
}
} else { } else {
textNode = ( textNode = (
<> <>

View File

@ -1,18 +1,30 @@
import * as React from 'react'; import * as React from 'react';
import omit from 'omit.js';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import Base, { BlockProps } from './Base'; import Base, { BlockProps, EllipsisConfig } from './Base';
export interface TextProps extends BlockProps { export interface TextProps extends BlockProps {
ellipsis?: boolean; ellipsis?: boolean | Omit<EllipsisConfig, 'expandable' | 'rows' | 'onExpand'>;
} }
const Text: React.FC<TextProps> = ({ ellipsis, ...restProps }) => { const Text: React.FC<TextProps> = ({ ellipsis, ...restProps }) => {
const mergedEllipsis = React.useMemo(() => {
if (ellipsis && typeof ellipsis === 'object') {
return omit(ellipsis, ['expandable', 'rows']);
}
return ellipsis;
}, [ellipsis]);
devWarning( devWarning(
typeof ellipsis !== 'object', typeof ellipsis !== 'object' ||
!ellipsis ||
(!('expandable' in ellipsis) && !('rows' in ellipsis)),
'Typography.Text', 'Typography.Text',
'`ellipsis` only supports boolean value.', '`ellipsis` do not support `expandable` or `rows` props.',
); );
return <Base {...restProps} ellipsis={!!ellipsis} component="span" />;
return <Base {...restProps} ellipsis={mergedEllipsis} component="span" />;
}; };
export default Text; export default Text;

View File

@ -217,6 +217,19 @@ exports[`renders ./components/typography/demo/basic.md correctly 1`] = `
exports[`renders ./components/typography/demo/ellipsis.md correctly 1`] = ` exports[`renders ./components/typography/demo/ellipsis.md correctly 1`] = `
Array [ Array [
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
/>
</button>,
<div <div
class="ant-typography ant-typography-ellipsis" class="ant-typography ant-typography-ellipsis"
> >
@ -227,6 +240,12 @@ Array [
> >
Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Design, a design language for background applications, is refined by Ant UED Team.
</div>, </div>,
<span
class="ant-typography ant-typography-ellipsis"
style="width:100px"
>
Ant Design, a design language for background applications, is refined by Ant UED Team.
</span>,
] ]
`; `;

View File

@ -233,6 +233,32 @@ describe('Typography', () => {
const wrapper = mount(<Base ellipsis component="p" />); const wrapper = mount(<Base ellipsis component="p" />);
expect(wrapper.find('.ant-typography-ellipsis-single-line').length).toBeTruthy(); expect(wrapper.find('.ant-typography-ellipsis-single-line').length).toBeTruthy();
}); });
describe('should tooltip support', () => {
function getWrapper(tooltip) {
return mount(
<Base ellipsis={{ tooltip }} component="p">
{fullStr}
</Base>,
);
}
it('boolean', async () => {
const wrapper = getWrapper(true);
await sleep(20);
wrapper.update();
expect(wrapper.find('Tooltip').prop('title')).toEqual(fullStr);
});
it('customize', async () => {
const wrapper = getWrapper('Bamboo is Light');
await sleep(20);
wrapper.update();
expect(wrapper.find('Tooltip').prop('title')).toEqual('Bamboo is Light');
});
});
}); });
describe('copyable', () => { describe('copyable', () => {

View File

@ -7,37 +7,56 @@ title:
## zh-CN ## zh-CN
多行文本省略。 多行文本省略。你可以通过 `tooltip` 属性配置省略展示内容,大量文本时推荐优先使用 `expandable`
## en-US ## en-US
Multiple line ellipsis support. Multiple line ellipsis support. You can use `tooltip` to config ellipsis tooltip. Recommend `expandable` when have lots of content.
```jsx ```tsx
import { Typography } from 'antd'; import { Typography, Switch } from 'antd';
const { Paragraph } = Typography; const { Paragraph, Text } = Typography;
ReactDOM.render( const Demo = () => {
const [ellipsis, setEllipsis] = React.useState(true);
return (
<> <>
<Paragraph ellipsis> <Switch
checked={ellipsis}
onChange={() => {
setEllipsis(!ellipsis);
}}
/>
<Paragraph ellipsis={ellipsis}>
Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Ant Design, a design language for background applications, is refined by Ant UED Team. Ant
Design, a design language for background applications, is refined by Ant UED Team. Ant Design, Design, a design language for background applications, is refined by Ant UED Team. Ant
a design language for background applications, is refined by Ant UED Team. Ant Design, a Design, a design language for background applications, is refined by Ant UED Team. Ant
design language for background applications, is refined by Ant UED Team. Ant Design, a design Design, a design language for background applications, is refined by Ant UED Team. Ant
language for background applications, is refined by Ant UED Team. Ant Design, a design Design, a design language for background applications, is refined by Ant UED Team. Ant
language for background applications, is refined by Ant UED Team. Design, a design language for background applications, is refined by Ant UED Team.
</Paragraph> </Paragraph>
<Paragraph ellipsis={{ rows: 2, expandable: true, symbol: 'more' }}> <Paragraph ellipsis={ellipsis ? { rows: 2, expandable: true, symbol: 'more' } : false}>
Ant Design, a design language for background applications, is refined by Ant UED Team. Ant Ant Design, a design language for background applications, is refined by Ant UED Team. Ant
Design, a design language for background applications, is refined by Ant UED Team. Ant Design, Design, a design language for background applications, is refined by Ant UED Team. Ant
a design language for background applications, is refined by Ant UED Team. Ant Design, a Design, a design language for background applications, is refined by Ant UED Team. Ant
design language for background applications, is refined by Ant UED Team. Ant Design, a design Design, a design language for background applications, is refined by Ant UED Team. Ant
language for background applications, is refined by Ant UED Team. Ant Design, a design Design, a design language for background applications, is refined by Ant UED Team. Ant
language for background applications, is refined by Ant UED Team. Design, a design language for background applications, is refined by Ant UED Team.
</Paragraph> </Paragraph>
</>,
mountNode, <Text
); style={ellipsis ? { width: 100 } : undefined}
ellipsis={ellipsis ? { tooltip: 'I am ellipsis now!' } : false}
>
Ant Design, a design language for background applications, is refined by Ant UED Team.
</Text>
</>
);
};
ReactDOM.render(<Demo />, mountNode);
``` ```

View File

@ -114,11 +114,12 @@ Basic text writing, including headings, body text, lists, and more.
} }
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| ---------- | ----------------------------------------- | ------------------ | ------- | ------- | | --- | --- | --- | --- | --- |
| expandable | Whether to be expandable | boolean | - | | | expandable | Whether to be expandable | boolean | - | |
| rows | Max rows of content | number | - | | | rows | Max rows of content | number | - | |
| suffix | Suffix of ellipsis content | ReactNode | - | | | suffix | Suffix of ellipsis content | ReactNode | - | |
| symbol | Custom `...` symbol of ellipsis | ReactNode | `...` | | | symbol | Custom `...` symbol of ellipsis | ReactNode | `...` | |
| tooltip | Show tooltip when ellipsis | boolean \| ReactNode | - | 4.11.0 |
| onEllipsis | Called when enter or leave ellipsis state | function(ellipsis) | - | 4.2.0 | | onEllipsis | Called when enter or leave ellipsis state | function(ellipsis) | - | 4.2.0 |
| onExpand | Called when expand content | function(event) | - | | | onExpand | Called when expand content | function(event) | - | |

View File

@ -114,11 +114,12 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg
} }
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| ---------- | ------------------ | ------------------ | ------ | ----- | | ---------- | -------------------- | -------------------- | ------ | ------ |
| expandable | 是否可展开 | boolean | - | | | expandable | 是否可展开 | boolean | - | |
| rows | 最多显示的行数 | number | - | | | rows | 最多显示的行数 | number | - | |
| suffix | 自定义省略内容后缀 | ReactNode | - | | | suffix | 自定义省略内容后缀 | ReactNode | - | |
| symbol | 自定义省略符号 | ReactNode | `...` | | | symbol | 自定义省略符号 | ReactNode | `...` | |
| tooltip | 省略时,展示提示信息 | boolean \| ReactNode | - | 4.11.0 |
| onEllipsis | 触发省略时的回调 | function(ellipsis) | - | 4.2.0 | | onEllipsis | 触发省略时的回调 | function(ellipsis) | - | 4.2.0 |
| onExpand | 点击展开时的回调 | function(event) | - | | | onExpand | 点击展开时的回调 | function(event) | - | |

View File

@ -123,7 +123,7 @@
"rc-drawer": "~4.2.0", "rc-drawer": "~4.2.0",
"rc-dropdown": "~3.2.0", "rc-dropdown": "~3.2.0",
"rc-field-form": "~1.17.3", "rc-field-form": "~1.17.3",
"rc-image": "~5.0.2", "rc-image": "~5.1.1",
"rc-input-number": "~6.1.0", "rc-input-number": "~6.1.0",
"rc-mentions": "~1.5.0", "rc-mentions": "~1.5.0",
"rc-menu": "~8.10.0", "rc-menu": "~8.10.0",