TextEllipsis.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. var __defProp = Object.defineProperty;
  2. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  3. var __getOwnPropNames = Object.getOwnPropertyNames;
  4. var __hasOwnProp = Object.prototype.hasOwnProperty;
  5. var __export = (target, all) => {
  6. for (var name2 in all)
  7. __defProp(target, name2, { get: all[name2], enumerable: true });
  8. };
  9. var __copyProps = (to, from, except, desc) => {
  10. if (from && typeof from === "object" || typeof from === "function") {
  11. for (let key of __getOwnPropNames(from))
  12. if (!__hasOwnProp.call(to, key) && key !== except)
  13. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  14. }
  15. return to;
  16. };
  17. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  18. var stdin_exports = {};
  19. __export(stdin_exports, {
  20. default: () => stdin_default,
  21. textEllipsisProps: () => textEllipsisProps
  22. });
  23. module.exports = __toCommonJS(stdin_exports);
  24. var import_vue = require("vue");
  25. var import_utils = require("../utils");
  26. var import_use_expose = require("../composables/use-expose");
  27. const [name, bem] = (0, import_utils.createNamespace)("text-ellipsis");
  28. const textEllipsisProps = {
  29. rows: (0, import_utils.makeNumericProp)(1),
  30. dots: (0, import_utils.makeStringProp)("..."),
  31. content: (0, import_utils.makeStringProp)(""),
  32. expandText: (0, import_utils.makeStringProp)(""),
  33. collapseText: (0, import_utils.makeStringProp)(""),
  34. position: (0, import_utils.makeStringProp)("end")
  35. };
  36. var stdin_default = (0, import_vue.defineComponent)({
  37. name,
  38. props: textEllipsisProps,
  39. emits: ["clickAction"],
  40. setup(props, {
  41. emit,
  42. slots
  43. }) {
  44. const text = (0, import_vue.ref)(props.content);
  45. const expanded = (0, import_vue.ref)(false);
  46. const hasAction = (0, import_vue.ref)(false);
  47. const root = (0, import_vue.ref)();
  48. const actionRef = (0, import_vue.ref)();
  49. let needRecalculate = false;
  50. const actionText = (0, import_vue.computed)(() => expanded.value ? props.collapseText : props.expandText);
  51. const pxToNum = (value) => {
  52. if (!value) return 0;
  53. const match = value.match(/^\d*(\.\d*)?/);
  54. return match ? Number(match[0]) : 0;
  55. };
  56. const cloneContainer = () => {
  57. if (!root.value || !root.value.isConnected) return;
  58. const originStyle = window.getComputedStyle(root.value);
  59. const container = document.createElement("div");
  60. const styleNames = Array.prototype.slice.apply(originStyle);
  61. styleNames.forEach((name2) => {
  62. container.style.setProperty(name2, originStyle.getPropertyValue(name2));
  63. });
  64. container.style.position = "fixed";
  65. container.style.zIndex = "-9999";
  66. container.style.top = "-9999px";
  67. container.style.height = "auto";
  68. container.style.minHeight = "auto";
  69. container.style.maxHeight = "auto";
  70. container.innerText = props.content;
  71. document.body.appendChild(container);
  72. return container;
  73. };
  74. const calcEllipsisText = (container, maxHeight) => {
  75. var _a, _b;
  76. const {
  77. content,
  78. position,
  79. dots
  80. } = props;
  81. const end = content.length;
  82. const middle = 0 + end >> 1;
  83. const actionHTML = slots.action ? (_b = (_a = actionRef.value) == null ? void 0 : _a.outerHTML) != null ? _b : "" : props.expandText;
  84. const calcEllipse = () => {
  85. const tail = (left, right) => {
  86. if (right - left <= 1) {
  87. if (position === "end") {
  88. return content.slice(0, left) + dots;
  89. }
  90. return dots + content.slice(right, end);
  91. }
  92. const middle2 = Math.round((left + right) / 2);
  93. if (position === "end") {
  94. container.innerText = content.slice(0, middle2) + dots;
  95. } else {
  96. container.innerText = dots + content.slice(middle2, end);
  97. }
  98. container.innerHTML += actionHTML;
  99. if (container.offsetHeight > maxHeight) {
  100. if (position === "end") {
  101. return tail(left, middle2);
  102. }
  103. return tail(middle2, right);
  104. }
  105. if (position === "end") {
  106. return tail(middle2, right);
  107. }
  108. return tail(left, middle2);
  109. };
  110. return tail(0, end);
  111. };
  112. const middleTail = (leftPart, rightPart) => {
  113. if (leftPart[1] - leftPart[0] <= 1 && rightPart[1] - rightPart[0] <= 1) {
  114. return content.slice(0, leftPart[0]) + dots + content.slice(rightPart[1], end);
  115. }
  116. const leftMiddle = Math.floor((leftPart[0] + leftPart[1]) / 2);
  117. const rightMiddle = Math.ceil((rightPart[0] + rightPart[1]) / 2);
  118. container.innerText = props.content.slice(0, leftMiddle) + props.dots + props.content.slice(rightMiddle, end);
  119. container.innerHTML += actionHTML;
  120. if (container.offsetHeight >= maxHeight) {
  121. return middleTail([leftPart[0], leftMiddle], [rightMiddle, rightPart[1]]);
  122. }
  123. return middleTail([leftMiddle, leftPart[1]], [rightPart[0], rightMiddle]);
  124. };
  125. return props.position === "middle" ? middleTail([0, middle], [middle, end]) : calcEllipse();
  126. };
  127. const calcEllipsised = () => {
  128. const container = cloneContainer();
  129. if (!container) {
  130. needRecalculate = true;
  131. return;
  132. }
  133. const {
  134. paddingBottom,
  135. paddingTop,
  136. lineHeight
  137. } = container.style;
  138. const maxHeight = Math.ceil((Number(props.rows) + 0.5) * pxToNum(lineHeight) + pxToNum(paddingTop) + pxToNum(paddingBottom));
  139. if (maxHeight < container.offsetHeight) {
  140. hasAction.value = true;
  141. text.value = calcEllipsisText(container, maxHeight);
  142. } else {
  143. hasAction.value = false;
  144. text.value = props.content;
  145. }
  146. document.body.removeChild(container);
  147. };
  148. const toggle = (isExpanded = !expanded.value) => {
  149. expanded.value = isExpanded;
  150. };
  151. const onClickAction = (event) => {
  152. toggle();
  153. emit("clickAction", event);
  154. };
  155. const renderAction = () => {
  156. const action = slots.action ? slots.action({
  157. expanded: expanded.value
  158. }) : actionText.value;
  159. return (0, import_vue.createVNode)("span", {
  160. "ref": actionRef,
  161. "class": bem("action"),
  162. "onClick": onClickAction
  163. }, [action]);
  164. };
  165. (0, import_vue.onMounted)(() => {
  166. calcEllipsised();
  167. if (slots.action) {
  168. (0, import_vue.nextTick)(calcEllipsised);
  169. }
  170. });
  171. (0, import_vue.onActivated)(() => {
  172. if (needRecalculate) {
  173. needRecalculate = false;
  174. calcEllipsised();
  175. }
  176. });
  177. (0, import_vue.watch)([import_utils.windowWidth, () => [props.content, props.rows, props.position]], calcEllipsised);
  178. (0, import_use_expose.useExpose)({
  179. toggle
  180. });
  181. return () => (0, import_vue.createVNode)("div", {
  182. "ref": root,
  183. "class": bem()
  184. }, [expanded.value ? props.content : text.value, hasAction.value ? renderAction() : null]);
  185. }
  186. });