├── .eslintrc.json
├── .gitignore
├── .npmignore
├── .travis.yml
├── .yo-rc.json
├── HISTORY.md
├── README.md
├── demo
├── CgTextEllipsisDemo.jsx
├── CgTextEllipsisDemo.less
└── index.jsx
├── index.html
├── package.json
├── src
├── CgTextEllipsis.jsx
├── CgTextEllipsis.less
├── ToolTip.jsx
├── clamp.js
└── index.js
└── tests
├── CgTextEllipsis.spec.js
└── index.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "parser": "babel-eslint",
4 | "plugins": [
5 | "react"
6 | ],
7 | "env": {
8 | "browser": true,
9 | "mocha": true
10 | },
11 | "rules": {
12 | "import/no-extraneous-dependencies": "off",
13 | "react/jsx-no-bind": "off",
14 | "jsx-a11y/label-has-for": "off",
15 | "no-plusplus": [
16 | "error",
17 | {
18 | "allowForLoopAfterthoughts": true
19 | }
20 | ],
21 | "react/no-unused-prop-types": "off",
22 | "react/forbid-prop-types": "off",
23 | "jsx-a11y/no-static-element-interactions": "off"
24 | }
25 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.log
3 | .idea/
4 | .ipr
5 | .iws
6 | *~
7 | ~*
8 | *.diff
9 | *.patch
10 | *.bak
11 | .DS_Store
12 | Thumbs.db
13 | .project
14 | .*proj
15 | .svn/
16 | *.swp
17 | *.swo
18 | *.pyc
19 | *.pyo
20 | .build
21 | node_modules
22 | _site
23 | sea-modules
24 | spm_modules
25 | .cache
26 | .happypack
27 | dist
28 | build
29 | coverage
30 | package-lock.json
31 | assets/**/*.css
32 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | *.cfg
3 | node_modules/
4 | nohup.out
5 | *.iml
6 | .idea/
7 | .ipr
8 | .iws
9 | *~
10 | ~*
11 | *.diff
12 | *.log
13 | *.patch
14 | *.bak
15 | .DS_Store
16 | Thumbs.db
17 | .project
18 | .*proj
19 | .svn/
20 | *.swp
21 | out/
22 | .build
23 | node_modules
24 | _site
25 | sea-modules
26 | spm_modules
27 | .cache
28 | .happypack
29 | dist
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | sudo: false
4 |
5 | addons:
6 | apt:
7 | packages:
8 | - xvfb
9 |
10 | notification:
11 | email:
12 | - wsj7552715@hotmail.com
13 |
14 | node_js:
15 | - 6.9.0
16 |
17 | before_install:
18 | - |
19 | if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qve '(\.md$)|(\.html$)'
20 | then
21 | echo "Only docs were updated, stopping build process."
22 | exit
23 | fi
24 | phantomjs --version
25 | install:
26 | - export DISPLAY=':99.0'
27 | - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
28 | - npm install
29 |
30 |
31 | script:
32 | - |
33 | if [ "$TEST_TYPE" = test ]; then
34 | npm test
35 | else
36 | npm run $TEST_TYPE
37 | fi
38 | env:
39 | matrix:
40 | - TEST_TYPE=test
41 | - TEST_TYPE=coverage
42 | - TEST_TYPE=saucelabs
43 |
44 | matrix:
45 | allow_failures:
46 | - env: "TEST_TYPE=saucelabs"
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-uxcore": {
3 | "promptValues": {
4 | "authorName": "DuBilin"
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | # history
2 |
3 | ## 1.0.0
4 |
5 | - `init`: uxcore-cg-text-ellipsis
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## uxcore-cg-text-ellipsis
2 |
3 | React cg text ellipsis
4 |
5 | [![NPM version][npm-image]][npm-url]
6 | [![build status][travis-image]][travis-url]
7 | [![Test Coverage][coveralls-image]][coveralls-url]
8 | [![Dependency Status][dep-image]][dep-url]
9 | [![devDependency Status][devdep-image]][devdep-url]
10 | [![NPM downloads][downloads-image]][npm-url]
11 |
12 | [![Sauce Test Status][sauce-image]][sauce-url]
13 |
14 | [npm-image]: http://img.shields.io/npm/v/uxcore-cg-text-ellipsis.svg?style=flat-square
15 | [npm-url]: http://npmjs.org/package/uxcore-cg-text-ellipsis
16 | [travis-image]: https://img.shields.io/travis/uxcore/uxcore-cg-text-ellipsis.svg?style=flat-square
17 | [travis-url]: https://travis-ci.org/uxcore/uxcore-cg-text-ellipsis
18 | [coveralls-image]: https://img.shields.io/coveralls/uxcore/uxcore-cg-text-ellipsis.svg?style=flat-square
19 | [coveralls-url]: https://coveralls.io/r/uxcore/uxcore-cg-text-ellipsis?branch=master
20 | [dep-image]: http://img.shields.io/david/uxcore/uxcore-cg-text-ellipsis.svg?style=flat-square
21 | [dep-url]: https://david-dm.org/uxcore/uxcore-cg-text-ellipsis
22 | [devdep-image]: http://img.shields.io/david/dev/uxcore/uxcore-cg-text-ellipsis.svg?style=flat-square
23 | [devdep-url]: https://david-dm.org/uxcore/uxcore-cg-text-ellipsis#info=devDependencies
24 | [downloads-image]: https://img.shields.io/npm/dm/uxcore-cg-text-ellipsis.svg
25 | [sauce-image]: https://saucelabs.com/browser-matrix/uxcore-cg-text-ellipsis.svg
26 | [sauce-url]: https://saucelabs.com/u/uxcore-cg-text-ellipsis
27 |
28 |
29 | ### Development
30 |
31 | ```sh
32 | git clone https://github.com/uxcore/uxcore-cg-text-ellipsis
33 | cd uxcore-cg-text-ellipsis
34 | npm install
35 | npm run server
36 | ```
37 |
38 | if you'd like to save your install time,you can use uxcore-tools globally.
39 |
40 | ```sh
41 | npm install uxcore-tools -g
42 | git clone https://github.com/uxcore/uxcore-cg-text-ellipsis
43 | cd uxcore-cg-text-ellipsis
44 | npm install
45 | npm run dep
46 | npm run start
47 | ```
48 |
49 | ### Test Case
50 |
51 | ```sh
52 | npm run test
53 | ```
54 |
55 | ### Coverage
56 |
57 | ```sh
58 | npm run coverage
59 | ```
60 |
61 | ## Demo
62 |
63 | http://uxcore.github.io/components/cg-text-ellipsis
64 |
65 | ## Contribute
66 |
67 | Yes please! See the [CONTRIBUTING](https://github.com/uxcore/uxcore/blob/master/CONTRIBUTING.md) for details.
68 |
69 | ## Usage
70 | ```javascript
71 |
77 | ```
78 |
79 | ## Props
80 |
81 | | Name | Type | Required | Default | Comments |
82 | |---|---|---|---|---|
83 | | line | Number | false | 1 | 最大行数 |
84 | | text | String | true | | 要显示的文本 |
85 | | uniqueKey | Number / String | true | | 元素唯一id |
86 | | ellipsisChar | String | false | '…' | 文本超出句末显示的字符串 |
87 | | isTipAlwaysShow | Boolean | false | false | 是否总是显示Tip |
88 | | maxTipWidth | Number | false | 400 | Tip的最大宽度 |
89 | | tipAlign | String | 'left', 'right', 'top', 'bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight' | 'topRight' | Tip显示的位置 |
90 | | tipTextAlign | String | 'left', 'center', 'right' | 'left' | Tip中文本对其方式 |
91 |
92 | ## Todo
93 | 由于代码实现采用的是js递归截取字符串,当文本内容很大,而期望行数较小时,会导致性能不好,需要引入一些算法去优化。
94 |
95 |
--------------------------------------------------------------------------------
/demo/CgTextEllipsisDemo.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * CgTextEllipsis Component Demo for uxcore
3 | * @author DuBilin
4 | *
5 | * Copyright 2017-2018, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 |
9 | import React from 'react';
10 | import CgTextEllipsis from '../src';
11 |
12 | class Demo extends React.Component {
13 |
14 | constructor(props) {
15 | super(props);
16 | this.state = {
17 | };
18 | }
19 |
20 | render() {
21 | return (
22 |
23 |
多行文本超出显示省略号组件
24 |
1.单行文本超出显示省略号
25 |
26 |
30 |
31 |
2.多行文本超出显示省略号
32 |
33 |
38 |
39 |
3.右边显示Tip
40 |
41 |
47 |
48 |
4.Tip自定义最大宽度
49 |
50 |
56 |
57 |
5.Tip中的文本居中对齐
58 |
59 |
65 |
66 |
6.当文本未超出也显示Tip
67 |
68 |
74 |
75 |
7.自定义文本超出时显示的文案
76 |
77 |
83 |
84 |
85 | );
86 | }
87 | }
88 |
89 | export default Demo;
90 |
--------------------------------------------------------------------------------
/demo/CgTextEllipsisDemo.less:
--------------------------------------------------------------------------------
1 | /**
2 | * CgTextEllipsis Component Demo Style for Uxcore
3 | * @author DuBilin
4 | *
5 | * Copyright 2017-2018, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 |
9 | @import "../node_modules/kuma-base/theme/orange";
10 | @import "../src/CgTextEllipsis.less";
11 |
12 | .box {
13 | margin: 20px 0 0 50px;
14 | .mt20 {
15 | margin-top: 20px;
16 | }
17 | }
--------------------------------------------------------------------------------
/demo/index.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * CgTextEllipsis Component Demo for uxcore
3 | * @author DuBilin
4 | *
5 | * Copyright 2017-2018, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 |
9 | import ReactDOM from 'react-dom';
10 | import React from 'react';
11 | import Demo from './CgTextEllipsisDemo';
12 |
13 | ReactDOM.render(, document.getElementById('UXCoreDemo'));
14 |
15 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | uxcore-cg-text-ellipsis
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "uxcore-cg-text-ellipsis",
3 | "version": "0.1.0",
4 | "description": "uxcore-cg-text-ellipsis component for uxcore.",
5 | "repository": "https://github.com/uxcore/uxcore-cg-text-ellipsis.git",
6 | "author": "DuBilin",
7 | "main": "build/index.js",
8 | "scripts": {
9 | "start": "uxcore-tools run start",
10 | "server": "uxcore-tools run server",
11 | "lint": "uxcore-tools run lint",
12 | "build": "uxcore-tools run build",
13 | "test": "uxcore-tools run electron",
14 | "coverage": "uxcore-tools run electron-coverage",
15 | "pub": "uxcore-tools run pub",
16 | "dep": "uxcore-tools run dep",
17 | "tnpm-dep": "uxcore-tools run tnpm-dep",
18 | "update": "uxcore-tools run update",
19 | "tnpm-update": "uxcore-tools run tnpm-update",
20 | "saucelabs": "uxcore-tools run saucelabs",
21 | "chrome": "uxcore-tools run chrome",
22 | "browsers": "uxcore-tools run browsers"
23 | },
24 | "bugs": {
25 | "url": "http://github.com/uxcore/uxcore-cg-text-ellipsis/issues"
26 | },
27 | "keywords": [
28 | "react",
29 | "react-component",
30 | "uxcore-cg-text-ellipsis",
31 | "CgTextEllipsis",
32 | "uxcore",
33 | "ellipsis"
34 | ],
35 | "devDependencies": {
36 | "console-polyfill": "^0.2.2",
37 | "es5-shim": "^4.5.8",
38 | "react": "15.x",
39 | "react-dom": "15.x",
40 | "react-test-renderer": "15.x",
41 | "expect.js": "~0.3.1",
42 | "uxcore-tools": "0.2.x",
43 | "uxcore-kuma": "*",
44 | "kuma-base": "1.x",
45 | "enzyme": "^3.0.0",
46 | "enzyme-adapter-react-15": "^1.0.0"
47 | },
48 | "dependencies": {
49 | "classnames": "^2.1.2",
50 | "prop-types": "15.x",
51 | "uxcore-tooltip": "^0.4.8"
52 | },
53 | "contributors": [],
54 | "license": "MIT"
55 | }
56 |
--------------------------------------------------------------------------------
/src/CgTextEllipsis.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * CgTextEllipsis Component for uxcore
3 | * @author DuBilin
4 | *
5 | * Copyright 2017-2018, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 | import React, { PropTypes, Component } from 'react';
9 | import ToolTip from './ToolTip';
10 | import Clamp from './clamp';
11 |
12 | class CgTextEllipsis extends React.Component {
13 |
14 | constructor(props) {
15 | super(props);
16 | this.state = {
17 | isOver: false,
18 | clamped: '',
19 | };
20 | }
21 |
22 | componentWillMount() {
23 | }
24 |
25 | componentDidMount() {
26 | const element = document.getElementById(this.props.uniqueKey);
27 | const clampInfo = Clamp(element, {
28 | line: this.props.line,
29 | ellipsisChar: this.props.ellipsisChar,
30 | });
31 | if (this.state.isOver !== clampInfo.isOver) {
32 | this.setState({
33 | isOver: clampInfo.isOver,
34 | clamped: clampInfo.clamped,
35 | });
36 | }
37 | }
38 |
39 | render() {
40 | const { isOver, clamped } = this.state;
41 | const { maxTipWidth, tipAlign, tipTextAlign, isTipAlwaysShow } = this.props;
42 | const displayValue = (
43 | {this.props.text}
44 |
);
45 |
46 | if (!isOver) {
47 | if (isTipAlwaysShow) {
48 | return (
49 |
50 | {this.props.text}
51 |
52 | );
53 | }
54 | return (
55 |
56 | {this.props.text}
57 |
58 | );
59 | }
60 | return (
61 |
62 | {clamped}
63 |
64 | );
65 | }
66 | }
67 |
68 | CgTextEllipsis.displayName = 'CgTextEllipsis';
69 | CgTextEllipsis.propTypes = {
70 | line: PropTypes.number,
71 | ellipsisChar: PropTypes.string,
72 | text: PropTypes.string.isRequired,
73 | uniqueKey: PropTypes.oneOf([PropTypes.number, PropTypes.string]).isRequired,
74 | maxTipWidth: PropTypes.number,
75 | tipAlign: PropTypes.oneOf(['left', 'right', 'top', 'bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight']),
76 | tipTextAlign: PropTypes.oneOf(['left', 'center', 'right']),
77 | isTipAlwaysShow: PropTypes.bool,
78 | };
79 | CgTextEllipsis.defaultProps = {
80 | line: 1,
81 | ellipsisChar: '…',
82 | maxTipWidth: 400,
83 | tipAlign: 'topRight',
84 | tipTextAlign: 'left',
85 | isTipAlwaysShow: false,
86 | };
87 |
88 | export default CgTextEllipsis;
89 |
90 |
--------------------------------------------------------------------------------
/src/CgTextEllipsis.less:
--------------------------------------------------------------------------------
1 | /**
2 | * CgTextEllipsis Component Style for uxcore
3 | * @author DuBilin
4 | *
5 | * Copyright 2017-2018, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 |
9 |
--------------------------------------------------------------------------------
/src/ToolTip.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes, Component } from 'react';
2 | import Tooltip from 'uxcore-tooltip';
3 |
4 | const CgToolTip = ({ displayValue, children, ...otherProps }) => (
5 |
6 | {children}
7 |
8 | );
9 |
10 | CgToolTip.propTypes = {
11 | displayValue: React.PropTypes.oneOfType([
12 | React.PropTypes.string,
13 | React.PropTypes.element
14 | ]),
15 | children: React.PropTypes.element
16 | };
17 |
18 | export default CgToolTip;
--------------------------------------------------------------------------------
/src/clamp.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: DuBilin
3 | * @Email: wb-dbl257323@alibaba-inc.com
4 | * @Date: 2018-01-25 23:20:59
5 | * @Last Modified by: Du Bilin
6 | * @Last Modified time: 2018-01-26 09:38:33
7 | */
8 |
9 | /**
10 | * @param {HTMLElement} element dom元素
11 | * @param {Object} options 参数配置
12 | */
13 | export default function clamp(element, options) {
14 | const originalText = element.innerHTML; // 源文本
15 | let isOver = false; // 是否超出
16 | let operatorChar = ''; // 操作中的字符,每次会去切割
17 |
18 | // 获取行高
19 | function getLineHeight(elem) {
20 | let lh = getComputedStyle(elem).getPropertyValue('line-height');
21 | if (lh === 'normal') {
22 | lh = parseInt(getComputedStyle(elem).getPropertyValue('font-size'), 10) * 1.2;
23 | }
24 | return parseInt(lh, 10);
25 | }
26 |
27 | // 根据行高获取元素最大限制的高度
28 | function getMaxHeight(clmp) {
29 | const lineHeight = getLineHeight(element);
30 | return lineHeight * clmp;
31 | }
32 |
33 | function getLastChild(elem) {
34 | return elem.lastChild;
35 | }
36 |
37 | function truncate(target, maxHeight) {
38 | if (!maxHeight) return '';
39 |
40 | if (!operatorChar) {
41 | operatorChar = target.nodeValue;
42 | if (operatorChar.length === 0) {
43 | return '';
44 | }
45 | }
46 |
47 | if (operatorChar.length > 1) {
48 | operatorChar = operatorChar.substring(0, operatorChar.length - 1);
49 | target.nodeValue = operatorChar + options.ellipsisChar;
50 | }
51 |
52 | if (operatorChar) {
53 | // 当高度符合,则停止递归
54 | if (element.clientHeight <= maxHeight) {
55 | return element.innerHTML;
56 | }
57 | }
58 | // 递归切割最后一个字符
59 | return truncate(target, maxHeight);
60 | }
61 |
62 | let clampedText;
63 | const height = getMaxHeight(options.line);
64 | if (height < element.clientHeight) {
65 | isOver = true;
66 | clampedText = truncate(getLastChild(element), height);
67 | }
68 |
69 | return {
70 | original: originalText,
71 | clamped: clampedText,
72 | isOver,
73 | };
74 | }
75 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * CgTextEllipsis Component for uxcore
3 | * @author DuBilin
4 | *
5 | * Copyright 2017-2018, Uxcore Team, Alinw.
6 | * All rights reserved.
7 | */
8 |
9 | export default from './CgTextEllipsis';
10 |
11 |
--------------------------------------------------------------------------------
/tests/CgTextEllipsis.spec.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect.js';
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import Enzyme, { mount } from 'enzyme';
5 | import Adapter from 'enzyme-adapter-react-15';
6 | import CgTextEllipsis from '../src';
7 |
8 | Enzyme.configure({ adapter: new Adapter() });
9 |
10 | describe('CgTextEllipsis', () => {
11 |
12 | });
--------------------------------------------------------------------------------
/tests/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * only require other specs here
3 | */
4 |
5 | const req = require.context('.', false, /\.spec\.js(x)?$/);
6 | req.keys().forEach(req);
7 |
--------------------------------------------------------------------------------