初始化 antd-pro
This commit is contained in:
31
admin-web/src/components/Ellipsis/demo/line.md
Normal file
31
admin-web/src/components/Ellipsis/demo/line.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
order: 1
|
||||
title:
|
||||
zh-CN: 按照行数省略
|
||||
en-US: Truncate according to the number of rows
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
通过设置 `lines` 属性指定最大行数,如果超过这个行数的文本会自动截取。但是在这种模式下所有 `children` 将会被转换成纯文本。
|
||||
|
||||
并且注意在这种模式下,外容器需要有指定的宽度(或设置自身宽度)。
|
||||
|
||||
## en-US
|
||||
|
||||
`lines` attribute specifies the maximum number of rows where the text will automatically be truncated when exceeded. In this mode, all children will be converted to plain text.
|
||||
|
||||
Also note that, in this mode, the outer container needs to have a specified width (or set its own width).
|
||||
|
||||
|
||||
````jsx
|
||||
import Ellipsis from 'ant-design-pro/lib/Ellipsis';
|
||||
|
||||
const article = <p>There were injuries alleged in three <a href="#cover">cases in 2015</a>, and a fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.</p>;
|
||||
|
||||
ReactDOM.render(
|
||||
<div style={{ width: 200 }}>
|
||||
<Ellipsis tooltip lines={3}>{article}</Ellipsis>
|
||||
</div>
|
||||
, mountNode);
|
||||
````
|
||||
28
admin-web/src/components/Ellipsis/demo/number.md
Normal file
28
admin-web/src/components/Ellipsis/demo/number.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
order: 0
|
||||
title:
|
||||
zh-CN: 按照字符数省略
|
||||
en-US: Truncate according to the number of character
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
通过设置 `length` 属性指定文本最长长度,如果超过这个长度会自动截取。
|
||||
|
||||
## en-US
|
||||
|
||||
`length` attribute specifies the maximum length where the text will automatically be truncated when exceeded.
|
||||
|
||||
````jsx
|
||||
import Ellipsis from 'ant-design-pro/lib/Ellipsis';
|
||||
|
||||
const article = 'There were injuries alleged in three cases in 2015, and a fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.';
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<Ellipsis length={100}>{article}</Ellipsis>
|
||||
<h4 style={{ marginTop: 24 }}>Show Tooltip</h4>
|
||||
<Ellipsis length={100} tooltip>{article}</Ellipsis>
|
||||
</div>
|
||||
, mountNode);
|
||||
````
|
||||
21
admin-web/src/components/Ellipsis/index.d.ts
vendored
Normal file
21
admin-web/src/components/Ellipsis/index.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import * as React from 'react';
|
||||
import { TooltipProps } from 'antd/lib/tooltip';
|
||||
|
||||
export interface IEllipsisTooltipProps extends TooltipProps {
|
||||
title?: undefined;
|
||||
overlayStyle?: undefined;
|
||||
}
|
||||
|
||||
export interface IEllipsisProps {
|
||||
tooltip?: boolean | IEllipsisTooltipProps;
|
||||
length?: number;
|
||||
lines?: number;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
fullWidthRecognition?: boolean;
|
||||
}
|
||||
|
||||
export function getStrFullLength(str: string): number;
|
||||
export function cutStrByFullLength(str: string, maxLength: number): string;
|
||||
|
||||
export default class Ellipsis extends React.Component<IEllipsisProps, any> {}
|
||||
16
admin-web/src/components/Ellipsis/index.en-US.md
Normal file
16
admin-web/src/components/Ellipsis/index.en-US.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: Ellipsis
|
||||
cols: 1
|
||||
order: 10
|
||||
---
|
||||
|
||||
When the text is too long, the Ellipsis automatically shortens it according to its length or the maximum number of lines.
|
||||
|
||||
## API
|
||||
|
||||
Property | Description | Type | Default
|
||||
----|------|-----|------
|
||||
tooltip | tooltip for showing the full text content when hovering over | boolean | -
|
||||
length | maximum number of characters in the text before being truncated | number | -
|
||||
lines | maximum number of rows in the text before being truncated | number | `1`
|
||||
fullWidthRecognition | whether consider full-width character length as 2 when calculate string length | boolean | -
|
||||
270
admin-web/src/components/Ellipsis/index.js
Normal file
270
admin-web/src/components/Ellipsis/index.js
Normal file
@@ -0,0 +1,270 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Tooltip } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import styles from './index.less';
|
||||
|
||||
/* eslint react/no-did-mount-set-state: 0 */
|
||||
/* eslint no-param-reassign: 0 */
|
||||
|
||||
const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined;
|
||||
|
||||
const TooltipOverlayStyle = {
|
||||
overflowWrap: 'break-word',
|
||||
wordWrap: 'break-word',
|
||||
};
|
||||
|
||||
export const getStrFullLength = (str = '') =>
|
||||
str.split('').reduce((pre, cur) => {
|
||||
const charCode = cur.charCodeAt(0);
|
||||
if (charCode >= 0 && charCode <= 128) {
|
||||
return pre + 1;
|
||||
}
|
||||
return pre + 2;
|
||||
}, 0);
|
||||
|
||||
export const cutStrByFullLength = (str = '', maxLength) => {
|
||||
let showLength = 0;
|
||||
return str.split('').reduce((pre, cur) => {
|
||||
const charCode = cur.charCodeAt(0);
|
||||
if (charCode >= 0 && charCode <= 128) {
|
||||
showLength += 1;
|
||||
} else {
|
||||
showLength += 2;
|
||||
}
|
||||
if (showLength <= maxLength) {
|
||||
return pre + cur;
|
||||
}
|
||||
return pre;
|
||||
}, '');
|
||||
};
|
||||
|
||||
const getTooltip = ({ tooltip, overlayStyle, title, children }) => {
|
||||
if (tooltip) {
|
||||
const props = tooltip === true ? { overlayStyle, title } : { ...tooltip, overlayStyle, title };
|
||||
return <Tooltip {...props}>{children}</Tooltip>;
|
||||
}
|
||||
return children;
|
||||
};
|
||||
|
||||
const EllipsisText = ({ text, length, tooltip, fullWidthRecognition, ...other }) => {
|
||||
if (typeof text !== 'string') {
|
||||
throw new Error('Ellipsis children must be string.');
|
||||
}
|
||||
const textLength = fullWidthRecognition ? getStrFullLength(text) : text.length;
|
||||
if (textLength <= length || length < 0) {
|
||||
return <span {...other}>{text}</span>;
|
||||
}
|
||||
const tail = '...';
|
||||
let displayText;
|
||||
if (length - tail.length <= 0) {
|
||||
displayText = '';
|
||||
} else {
|
||||
displayText = fullWidthRecognition ? cutStrByFullLength(text, length) : text.slice(0, length);
|
||||
}
|
||||
|
||||
const spanAttrs = tooltip ? {} : { ...other };
|
||||
return getTooltip({
|
||||
tooltip,
|
||||
overlayStyle: TooltipOverlayStyle,
|
||||
title: text,
|
||||
children: (
|
||||
<span {...spanAttrs}>
|
||||
{displayText}
|
||||
{tail}
|
||||
</span>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
export default class Ellipsis extends Component {
|
||||
state = {
|
||||
text: '',
|
||||
targetCount: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.node) {
|
||||
this.computeLine();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(perProps) {
|
||||
const { lines } = this.props;
|
||||
if (lines !== perProps.lines) {
|
||||
this.computeLine();
|
||||
}
|
||||
}
|
||||
|
||||
computeLine = () => {
|
||||
const { lines } = this.props;
|
||||
if (lines && !isSupportLineClamp) {
|
||||
const text = this.shadowChildren.innerText || this.shadowChildren.textContent;
|
||||
const lineHeight = parseInt(getComputedStyle(this.root).lineHeight, 10);
|
||||
const targetHeight = lines * lineHeight;
|
||||
this.content.style.height = `${targetHeight}px`;
|
||||
const totalHeight = this.shadowChildren.offsetHeight;
|
||||
const shadowNode = this.shadow.firstChild;
|
||||
|
||||
if (totalHeight <= targetHeight) {
|
||||
this.setState({
|
||||
text,
|
||||
targetCount: text.length,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// bisection
|
||||
const len = text.length;
|
||||
const mid = Math.ceil(len / 2);
|
||||
|
||||
const count = this.bisection(targetHeight, mid, 0, len, text, shadowNode);
|
||||
|
||||
this.setState({
|
||||
text,
|
||||
targetCount: count,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
bisection = (th, m, b, e, text, shadowNode) => {
|
||||
const suffix = '...';
|
||||
let mid = m;
|
||||
let end = e;
|
||||
let begin = b;
|
||||
shadowNode.innerHTML = text.substring(0, mid) + suffix;
|
||||
let sh = shadowNode.offsetHeight;
|
||||
|
||||
if (sh <= th) {
|
||||
shadowNode.innerHTML = text.substring(0, mid + 1) + suffix;
|
||||
sh = shadowNode.offsetHeight;
|
||||
if (sh > th || mid === begin) {
|
||||
return mid;
|
||||
}
|
||||
begin = mid;
|
||||
if (end - begin === 1) {
|
||||
mid = 1 + begin;
|
||||
} else {
|
||||
mid = Math.floor((end - begin) / 2) + begin;
|
||||
}
|
||||
return this.bisection(th, mid, begin, end, text, shadowNode);
|
||||
}
|
||||
if (mid - 1 < 0) {
|
||||
return mid;
|
||||
}
|
||||
shadowNode.innerHTML = text.substring(0, mid - 1) + suffix;
|
||||
sh = shadowNode.offsetHeight;
|
||||
if (sh <= th) {
|
||||
return mid - 1;
|
||||
}
|
||||
end = mid;
|
||||
mid = Math.floor((end - begin) / 2) + begin;
|
||||
return this.bisection(th, mid, begin, end, text, shadowNode);
|
||||
};
|
||||
|
||||
handleRoot = n => {
|
||||
this.root = n;
|
||||
};
|
||||
|
||||
handleContent = n => {
|
||||
this.content = n;
|
||||
};
|
||||
|
||||
handleNode = n => {
|
||||
this.node = n;
|
||||
};
|
||||
|
||||
handleShadow = n => {
|
||||
this.shadow = n;
|
||||
};
|
||||
|
||||
handleShadowChildren = n => {
|
||||
this.shadowChildren = n;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { text, targetCount } = this.state;
|
||||
const {
|
||||
children,
|
||||
lines,
|
||||
length,
|
||||
className,
|
||||
tooltip,
|
||||
fullWidthRecognition,
|
||||
...restProps
|
||||
} = this.props;
|
||||
|
||||
const cls = classNames(styles.ellipsis, className, {
|
||||
[styles.lines]: lines && !isSupportLineClamp,
|
||||
[styles.lineClamp]: lines && isSupportLineClamp,
|
||||
});
|
||||
|
||||
if (!lines && !length) {
|
||||
return (
|
||||
<span className={cls} {...restProps}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// length
|
||||
if (!lines) {
|
||||
return (
|
||||
<EllipsisText
|
||||
className={cls}
|
||||
length={length}
|
||||
text={children || ''}
|
||||
tooltip={tooltip}
|
||||
fullWidthRecognition={fullWidthRecognition}
|
||||
{...restProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const id = `antd-pro-ellipsis-${`${new Date().getTime()}${Math.floor(Math.random() * 100)}`}`;
|
||||
|
||||
// support document.body.style.webkitLineClamp
|
||||
if (isSupportLineClamp) {
|
||||
const style = `#${id}{-webkit-line-clamp:${lines};-webkit-box-orient: vertical;}`;
|
||||
|
||||
const node = (
|
||||
<div id={id} className={cls} {...restProps}>
|
||||
<style>{style}</style>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
return getTooltip({
|
||||
tooltip,
|
||||
overlayStyle: TooltipOverlayStyle,
|
||||
title: children,
|
||||
children: node,
|
||||
});
|
||||
}
|
||||
|
||||
const childNode = (
|
||||
<span ref={this.handleNode}>
|
||||
{targetCount > 0 && text.substring(0, targetCount)}
|
||||
{targetCount > 0 && targetCount < text.length && '...'}
|
||||
</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<div {...restProps} ref={this.handleRoot} className={cls}>
|
||||
<div ref={this.handleContent}>
|
||||
{getTooltip({
|
||||
tooltip,
|
||||
overlayStyle: TooltipOverlayStyle,
|
||||
title: text,
|
||||
children: childNode,
|
||||
})}
|
||||
<div className={styles.shadow} ref={this.handleShadowChildren}>
|
||||
{children}
|
||||
</div>
|
||||
<div className={styles.shadow} ref={this.handleShadow}>
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
24
admin-web/src/components/Ellipsis/index.less
Normal file
24
admin-web/src/components/Ellipsis/index.less
Normal file
@@ -0,0 +1,24 @@
|
||||
.ellipsis {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.lines {
|
||||
position: relative;
|
||||
.shadow {
|
||||
position: absolute;
|
||||
z-index: -999;
|
||||
display: block;
|
||||
color: transparent;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.lineClamp {
|
||||
position: relative;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
13
admin-web/src/components/Ellipsis/index.test.js
Normal file
13
admin-web/src/components/Ellipsis/index.test.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { getStrFullLength, cutStrByFullLength } from './index';
|
||||
|
||||
describe('test calculateShowLength', () => {
|
||||
it('get full length', () => {
|
||||
expect(getStrFullLength('一二,a,')).toEqual(8);
|
||||
});
|
||||
it('cut str by full length', () => {
|
||||
expect(cutStrByFullLength('一二,a,', 7)).toEqual('一二,a');
|
||||
});
|
||||
it('cut str when length small', () => {
|
||||
expect(cutStrByFullLength('一22三', 5)).toEqual('一22');
|
||||
});
|
||||
});
|
||||
17
admin-web/src/components/Ellipsis/index.zh-CN.md
Normal file
17
admin-web/src/components/Ellipsis/index.zh-CN.md
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Ellipsis
|
||||
subtitle: 文本自动省略号
|
||||
cols: 1
|
||||
order: 10
|
||||
---
|
||||
|
||||
文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。
|
||||
|
||||
## API
|
||||
|
||||
参数 | 说明 | 类型 | 默认值
|
||||
----|------|-----|------
|
||||
tooltip | 移动到文本展示完整内容的提示 | boolean | -
|
||||
length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | -
|
||||
lines | 在按照行数截取下最大的行数,超过则截取省略 | number | `1`
|
||||
fullWidthRecognition | 是否将全角字符的长度视为2来计算字符串长度 | boolean | -
|
||||
Reference in New Issue
Block a user