├── .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 | --------------------------------------------------------------------------------