动态菜单和动态路由的 antd pro

index.js 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import React, { Component } from 'react';
  2. import { Tooltip } from 'antd';
  3. import classNames from 'classnames';
  4. import styles from './index.less';
  5. /* eslint react/no-did-mount-set-state: 0 */
  6. /* eslint no-param-reassign: 0 */
  7. const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined;
  8. const EllipsisText = ({ text, length, tooltip, ...other }) => {
  9. if (typeof text !== 'string') {
  10. throw new Error('Ellipsis children must be string.');
  11. }
  12. if (text.length <= length || length < 0) {
  13. return <span {...other}>{text}</span>;
  14. }
  15. const tail = '...';
  16. let displayText;
  17. if (length - tail.length <= 0) {
  18. displayText = '';
  19. } else {
  20. displayText = text.slice(0, length - tail.length);
  21. }
  22. if (tooltip) {
  23. return (
  24. <Tooltip overlayStyle={{ wordBreak: 'break-all' }} title={text}>
  25. <span>
  26. {displayText}
  27. {tail}
  28. </span>
  29. </Tooltip>
  30. );
  31. }
  32. return (
  33. <span {...other}>
  34. {displayText}
  35. {tail}
  36. </span>
  37. );
  38. };
  39. export default class Ellipsis extends Component {
  40. state = {
  41. text: '',
  42. targetCount: 0,
  43. };
  44. componentDidMount() {
  45. if (this.node) {
  46. this.computeLine();
  47. }
  48. }
  49. componentWillReceiveProps(nextProps) {
  50. if (this.props.lines !== nextProps.lines) {
  51. this.computeLine();
  52. }
  53. }
  54. computeLine = () => {
  55. const { lines } = this.props;
  56. if (lines && !isSupportLineClamp) {
  57. const text = this.shadowChildren.innerText;
  58. const lineHeight = parseInt(getComputedStyle(this.root).lineHeight, 10);
  59. const targetHeight = lines * lineHeight;
  60. this.content.style.height = `${targetHeight}px`;
  61. const totalHeight = this.shadowChildren.offsetHeight;
  62. const shadowNode = this.shadow.firstChild;
  63. if (totalHeight <= targetHeight) {
  64. this.setState({
  65. text,
  66. targetCount: text.length,
  67. });
  68. return;
  69. }
  70. // bisection
  71. const len = text.length;
  72. const mid = Math.floor(len / 2);
  73. const count = this.bisection(targetHeight, mid, 0, len, text, shadowNode);
  74. this.setState({
  75. text,
  76. targetCount: count,
  77. });
  78. }
  79. };
  80. bisection = (th, m, b, e, text, shadowNode) => {
  81. const suffix = '...';
  82. let mid = m;
  83. let end = e;
  84. let begin = b;
  85. shadowNode.innerHTML = text.substring(0, mid) + suffix;
  86. let sh = shadowNode.offsetHeight;
  87. if (sh <= th) {
  88. shadowNode.innerHTML = text.substring(0, mid + 1) + suffix;
  89. sh = shadowNode.offsetHeight;
  90. if (sh > th) {
  91. return mid;
  92. } else {
  93. begin = mid;
  94. mid = Math.floor((end - begin) / 2) + begin;
  95. return this.bisection(th, mid, begin, end, text, shadowNode);
  96. }
  97. } else {
  98. if (mid - 1 < 0) {
  99. return mid;
  100. }
  101. shadowNode.innerHTML = text.substring(0, mid - 1) + suffix;
  102. sh = shadowNode.offsetHeight;
  103. if (sh <= th) {
  104. return mid - 1;
  105. } else {
  106. end = mid;
  107. mid = Math.floor((end - begin) / 2) + begin;
  108. return this.bisection(th, mid, begin, end, text, shadowNode);
  109. }
  110. }
  111. };
  112. handleRoot = n => {
  113. this.root = n;
  114. };
  115. handleContent = n => {
  116. this.content = n;
  117. };
  118. handleNode = n => {
  119. this.node = n;
  120. };
  121. handleShadow = n => {
  122. this.shadow = n;
  123. };
  124. handleShadowChildren = n => {
  125. this.shadowChildren = n;
  126. };
  127. render() {
  128. const { text, targetCount } = this.state;
  129. const { children, lines, length, className, tooltip, ...restProps } = this.props;
  130. const cls = classNames(styles.ellipsis, className, {
  131. [styles.lines]: lines && !isSupportLineClamp,
  132. [styles.lineClamp]: lines && isSupportLineClamp,
  133. });
  134. if (!lines && !length) {
  135. return (
  136. <span className={cls} {...restProps}>
  137. {children}
  138. </span>
  139. );
  140. }
  141. // length
  142. if (!lines) {
  143. return (
  144. <EllipsisText
  145. className={cls}
  146. length={length}
  147. text={children || ''}
  148. tooltip={tooltip}
  149. {...restProps}
  150. />
  151. );
  152. }
  153. const id = `antd-pro-ellipsis-${`${new Date().getTime()}${Math.floor(Math.random() * 100)}`}`;
  154. // support document.body.style.webkitLineClamp
  155. if (isSupportLineClamp) {
  156. const style = `#${id}{-webkit-line-clamp:${lines};-webkit-box-orient: vertical;}`;
  157. return (
  158. <div id={id} className={cls} {...restProps}>
  159. <style>{style}</style>
  160. {tooltip ? (
  161. <Tooltip overlayStyle={{ wordBreak: 'break-all' }} title={children}>
  162. {children}
  163. </Tooltip>
  164. ) : (
  165. children
  166. )}
  167. </div>
  168. );
  169. }
  170. const childNode = (
  171. <span ref={this.handleNode}>
  172. {targetCount > 0 && text.substring(0, targetCount)}
  173. {targetCount > 0 && targetCount < text.length && '...'}
  174. </span>
  175. );
  176. return (
  177. <div {...restProps} ref={this.handleRoot} className={cls}>
  178. <div ref={this.handleContent}>
  179. {tooltip ? (
  180. <Tooltip overlayStyle={{ wordBreak: 'break-all' }} title={text}>
  181. {childNode}
  182. </Tooltip>
  183. ) : (
  184. childNode
  185. )}
  186. <div className={styles.shadow} ref={this.handleShadowChildren}>
  187. {children}
  188. </div>
  189. <div className={styles.shadow} ref={this.handleShadow}>
  190. <span>{text}</span>
  191. </div>
  192. </div>
  193. </div>
  194. );
  195. }
  196. }