mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 22:36:31 +08:00
feat: Typography add suffix (#20224)
* feat:Typography add suffix * feat:run test * fix:lint * feat: update suffix example * feat: update test * fix: updateupdateSnapshot * fix: upate test * run lint * feat: measureSuffixText * feat: suffix 不可省略 * fix: title * fix: update node_modules * fix: change title type
This commit is contained in:
parent
4047e2632e
commit
1ebf5fa4b7
@ -37,16 +37,17 @@ interface EditConfig {
|
||||
interface EllipsisConfig {
|
||||
rows?: number;
|
||||
expandable?: boolean;
|
||||
suffix?: string;
|
||||
onExpand?: () => void;
|
||||
}
|
||||
|
||||
export interface BlockProps extends TypographyProps {
|
||||
title?: string;
|
||||
editable?: boolean | EditConfig;
|
||||
copyable?: boolean | CopyConfig;
|
||||
type?: BaseType;
|
||||
disabled?: boolean;
|
||||
ellipsis?: boolean | EllipsisConfig;
|
||||
|
||||
// decorations
|
||||
code?: boolean;
|
||||
mark?: boolean;
|
||||
@ -268,8 +269,9 @@ class Base extends React.Component<InternalBlockProps & ConfigConsumerProps, Bas
|
||||
canUseCSSEllipsis(): boolean {
|
||||
const { clientRendered } = this.state;
|
||||
const { editable, copyable } = this.props;
|
||||
const { rows, expandable } = this.getEllipsis();
|
||||
const { rows, expandable, suffix } = this.getEllipsis();
|
||||
|
||||
if (suffix) return false;
|
||||
// Can't use css ellipsis since we need to provide the place for button
|
||||
if (editable || copyable || expandable || !clientRendered) {
|
||||
return false;
|
||||
@ -284,7 +286,7 @@ class Base extends React.Component<InternalBlockProps & ConfigConsumerProps, Bas
|
||||
|
||||
syncEllipsis() {
|
||||
const { ellipsisText, isEllipsis, expanded } = this.state;
|
||||
const { rows } = this.getEllipsis();
|
||||
const { rows, suffix } = this.getEllipsis();
|
||||
const { children } = this.props;
|
||||
if (!rows || rows < 0 || !this.content || expanded) return;
|
||||
|
||||
@ -299,7 +301,7 @@ class Base extends React.Component<InternalBlockProps & ConfigConsumerProps, Bas
|
||||
|
||||
const { content, text, ellipsis } = measure(
|
||||
findDOMNode(this.content),
|
||||
rows,
|
||||
{ rows, suffix },
|
||||
children,
|
||||
this.renderOperations(true),
|
||||
ELLIPSIS_STR,
|
||||
@ -398,9 +400,10 @@ class Base extends React.Component<InternalBlockProps & ConfigConsumerProps, Bas
|
||||
type,
|
||||
disabled,
|
||||
style,
|
||||
title,
|
||||
...restProps
|
||||
} = this.props;
|
||||
const { rows } = this.getEllipsis();
|
||||
const { rows, suffix } = this.getEllipsis();
|
||||
|
||||
const textProps = omit(restProps, [
|
||||
'prefixCls',
|
||||
@ -421,18 +424,29 @@ class Base extends React.Component<InternalBlockProps & ConfigConsumerProps, Bas
|
||||
const cssLineClamp = rows && rows > 1 && cssEllipsis;
|
||||
|
||||
let textNode: React.ReactNode = children;
|
||||
let ariaLabel: string | null = null;
|
||||
let ariaLabel: string | undefined;
|
||||
|
||||
// Only use js ellipsis when css ellipsis not support
|
||||
if (rows && isEllipsis && !expanded && !cssEllipsis) {
|
||||
ariaLabel = String(children);
|
||||
ariaLabel = title;
|
||||
if (!title && (typeof children === 'string' || typeof children === 'number')) {
|
||||
ariaLabel = String(children);
|
||||
}
|
||||
// We move full content to outer element to avoid repeat read the content by accessibility
|
||||
textNode = (
|
||||
<span title={String(children)} aria-hidden="true">
|
||||
<span title={ariaLabel} aria-hidden="true">
|
||||
{ellipsisContent}
|
||||
{ELLIPSIS_STR}
|
||||
{suffix}
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
textNode = (
|
||||
<>
|
||||
{children}
|
||||
{suffix}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
textNode = wrapperDecorations(this.props, textNode);
|
||||
|
@ -548,6 +548,43 @@ exports[`renders ./components/typography/demo/paragraph-debug.md correctly 1`] =
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/typography/demo/suffix.md correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="ant-slider"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left:0%;right:auto;width:0%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="10"
|
||||
aria-valuemin="1"
|
||||
aria-valuenow="1"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left:0%;right:auto;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-mark"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-typography ant-typography-ellipsis"
|
||||
>
|
||||
To be, or not to be, that is a question: Whether it is nobler in the mind to suffer. The slings and arrows of outrageous fortune--William Shakespeare
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/typography/demo/text.md correctly 1`] = `
|
||||
<div>
|
||||
<span
|
||||
|
@ -96,6 +96,45 @@ describe('Typography', () => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('should middle ellipsis', () => {
|
||||
const suffix = '--suffix';
|
||||
const wrapper = mount(
|
||||
<Base ellipsis={{ rows: 1, suffix }} component="p">
|
||||
{fullStr}
|
||||
</Base>,
|
||||
);
|
||||
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('p').text()).toEqual('Bamboo is...--suffix');
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('should front or middle ellipsis', () => {
|
||||
const suffix = '--The information is very important';
|
||||
const wrapper = mount(
|
||||
<Base ellipsis={{ rows: 1, suffix }} component="p">
|
||||
{fullStr}
|
||||
</Base>,
|
||||
);
|
||||
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('p').text()).toEqual('...--The information is very important');
|
||||
|
||||
wrapper.setProps({ ellipsis: { rows: 2, suffix } });
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('p').text()).toEqual('Ba...--The information is very important');
|
||||
|
||||
wrapper.setProps({ ellipsis: { rows: 99, suffix } });
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper.find('p').text()).toEqual(fullStr + suffix);
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('connect children', () => {
|
||||
const bamboo = 'Bamboo';
|
||||
const is = ' is ';
|
||||
|
53
components/typography/demo/suffix.md
Normal file
53
components/typography/demo/suffix.md
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
order: 100
|
||||
title:
|
||||
zh-CN: 后缀
|
||||
en-US: suffix
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
添加后缀的省略。
|
||||
|
||||
## en-US
|
||||
|
||||
add suffix ellipsis support.
|
||||
|
||||
```jsx
|
||||
import { Typography, Slider } from 'antd';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
class Demo extends React.Component {
|
||||
state = {
|
||||
rows: 1,
|
||||
};
|
||||
|
||||
onChange = rows => {
|
||||
this.setState({ rows });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { rows } = this.state;
|
||||
const article =
|
||||
'To be, or not to be, that is a question: Whether it is nobler in the mind to suffer. The slings and arrows of outrageous fortune';
|
||||
return (
|
||||
<div>
|
||||
<Slider value={rows} min={1} max={10} onChange={this.onChange} />
|
||||
<Paragraph
|
||||
ellipsis={{
|
||||
rows,
|
||||
expandable: true,
|
||||
suffix: '--William Shakespeare',
|
||||
}}
|
||||
title={`${article}--William Shakespeare`}
|
||||
>
|
||||
{article}
|
||||
</Paragraph>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
```
|
@ -55,7 +55,7 @@ Basic text writing, including headings, body text, lists, and more.
|
||||
| delete | Deleted line style | boolean | false | |
|
||||
| disabled | Disabled content | boolean | false | |
|
||||
| editable | Editable. Can control edit state when is object | boolean \| { editing: boolean, onStart: Function, onChange: Function(string) } | false | |
|
||||
| ellipsis | Display ellipsis when text overflows. Can configure rows and expandable by using object | boolean \| { rows: number, expandable: boolean, onExpand: Function } | false | |
|
||||
| ellipsis | Display ellipsis when text overflows. Can configure rows expandable and suffix by using object | boolean \| { rows: number, expandable: boolean suffix: string, onExpand: Function } | false | |
|
||||
| mark | Marked style | boolean | false | |
|
||||
| underline | Underlined style | boolean | false | |
|
||||
| onChange | Trigger when user edits the content | Function(string) | - | |
|
||||
|
@ -52,7 +52,7 @@ cols: 1
|
||||
| delete | 添加删除线样式 | boolean | false | |
|
||||
| disabled | 禁用文本 | boolean | false | |
|
||||
| editable | 是否可编辑,为对象时可对编辑进行控制 | boolean \| { editing: boolean, onStart: Function, onChange: Function(string) } | false | |
|
||||
| ellipsis | 自动溢出省略,为对象时可设置省略行数与是否可展开等 | boolean \| { rows: number, expandable: boolean, onExpand: Function } | false | |
|
||||
| ellipsis | 自动溢出省略,为对象时可设置省略行数、是否可展开、添加后缀等 | boolean \| { rows: number, expandable: boolean, suffix: string, onExpand: Function } | false | |
|
||||
| mark | 添加标记样式 | boolean | false | |
|
||||
| underline | 添加下划线样式 | boolean | false | |
|
||||
| onChange | 当用户提交编辑内容时触发 | Function(string) | - | |
|
||||
|
@ -6,6 +6,10 @@ interface MeasureResult {
|
||||
finished: boolean;
|
||||
reactNode: React.ReactNode;
|
||||
}
|
||||
interface Option {
|
||||
rows: number;
|
||||
suffix?: string;
|
||||
}
|
||||
|
||||
// We only handle element & text node.
|
||||
const ELEMENT_NODE = 1;
|
||||
@ -53,7 +57,7 @@ function mergeChildren(children: React.ReactNode[]): React.ReactNode[] {
|
||||
|
||||
export default (
|
||||
originEle: HTMLElement,
|
||||
rows: number,
|
||||
option: Option,
|
||||
content: React.ReactNode,
|
||||
fixedContent: React.ReactNode[],
|
||||
ellipsisStr: string,
|
||||
@ -64,6 +68,7 @@ export default (
|
||||
document.body.appendChild(ellipsisContainer);
|
||||
}
|
||||
|
||||
const { rows, suffix = '' } = option;
|
||||
// Get origin style
|
||||
const originStyle = window.getComputedStyle(originEle);
|
||||
const originCSS = styleToString(originStyle);
|
||||
@ -92,7 +97,10 @@ export default (
|
||||
const contentList: React.ReactNode[] = mergeChildren(toArray(content));
|
||||
render(
|
||||
<div style={wrapperStyle}>
|
||||
<span style={wrapperStyle}>{contentList}</span>
|
||||
<span style={wrapperStyle}>
|
||||
{contentList}
|
||||
{suffix}
|
||||
</span>
|
||||
<span style={wrapperStyle}>{fixedContent}</span>
|
||||
</div>,
|
||||
ellipsisContainer,
|
||||
@ -125,7 +133,7 @@ export default (
|
||||
// Create origin content holder
|
||||
const ellipsisContentHolder = document.createElement('span');
|
||||
ellipsisContainer.appendChild(ellipsisContentHolder);
|
||||
const ellipsisTextNode = document.createTextNode(ellipsisStr);
|
||||
const ellipsisTextNode = document.createTextNode(ellipsisStr + suffix);
|
||||
ellipsisContentHolder.appendChild(ellipsisTextNode);
|
||||
|
||||
fixedNodes.forEach(childNode => {
|
||||
@ -155,7 +163,7 @@ export default (
|
||||
const currentStepText = fullText.slice(0, step);
|
||||
textNode.textContent = currentStepText;
|
||||
|
||||
if (inRange()) {
|
||||
if (inRange() || !currentStepText) {
|
||||
return step === fullText.length
|
||||
? {
|
||||
finished: false,
|
||||
|
Loading…
Reference in New Issue
Block a user