calcTextareaHeight.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /**
  2. * fork from element-plus
  3. * https://github.com/element-plus/element-plus/blob/dev/packages/components/input/src/utils.ts
  4. */
  5. import { isFirefox } from "./utils";
  6. let hiddenTextarea: HTMLTextAreaElement | undefined = undefined;
  7. const HIDDEN_STYLE = `
  8. height:0 !important;
  9. visibility:hidden !important;
  10. ${isFirefox() ? "" : "overflow:hidden !important;"}
  11. position:absolute !important;
  12. z-index:-1000 !important;
  13. top:0 !important;
  14. right:0 !important;
  15. `;
  16. const CONTEXT_STYLE = [
  17. "letter-spacing",
  18. "line-height",
  19. "padding-top",
  20. "padding-bottom",
  21. "font-family",
  22. "font-weight",
  23. "font-size",
  24. "text-rendering",
  25. "text-transform",
  26. "width",
  27. "text-indent",
  28. "padding-left",
  29. "padding-right",
  30. "border-width",
  31. "box-sizing",
  32. ];
  33. type NodeStyle = {
  34. contextStyle: string;
  35. boxSizing: string;
  36. paddingSize: number;
  37. borderSize: number;
  38. };
  39. type TextAreaHeight = {
  40. height: string;
  41. minHeight?: string;
  42. };
  43. function calculateNodeStyling(targetElement: Element): NodeStyle {
  44. const style = window.getComputedStyle(targetElement);
  45. const boxSizing = style.getPropertyValue("box-sizing");
  46. const paddingSize =
  47. Number.parseFloat(style.getPropertyValue("padding-bottom")) +
  48. Number.parseFloat(style.getPropertyValue("padding-top"));
  49. const borderSize =
  50. Number.parseFloat(style.getPropertyValue("border-bottom-width")) +
  51. Number.parseFloat(style.getPropertyValue("border-top-width"));
  52. const contextStyle = CONTEXT_STYLE.map(
  53. (name) => `${name}:${style.getPropertyValue(name)}`,
  54. ).join(";");
  55. return { contextStyle, paddingSize, borderSize, boxSizing };
  56. }
  57. export default function calcTextareaHeight(
  58. targetElement: HTMLTextAreaElement,
  59. minRows: number = 2,
  60. maxRows?: number,
  61. ): TextAreaHeight {
  62. if (!hiddenTextarea) {
  63. hiddenTextarea = document.createElement("textarea");
  64. document.body.appendChild(hiddenTextarea);
  65. }
  66. const { paddingSize, borderSize, boxSizing, contextStyle } =
  67. calculateNodeStyling(targetElement);
  68. hiddenTextarea.setAttribute("style", `${contextStyle};${HIDDEN_STYLE}`);
  69. hiddenTextarea.value = targetElement.value || targetElement.placeholder || "";
  70. let height = hiddenTextarea.scrollHeight;
  71. const result = {} as TextAreaHeight;
  72. if (boxSizing === "border-box") {
  73. height = height + borderSize;
  74. } else if (boxSizing === "content-box") {
  75. height = height - paddingSize;
  76. }
  77. hiddenTextarea.value = "";
  78. const singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
  79. if (minRows) {
  80. let minHeight = singleRowHeight * minRows;
  81. if (boxSizing === "border-box") {
  82. minHeight = minHeight + paddingSize + borderSize;
  83. }
  84. height = Math.max(minHeight, height);
  85. result.minHeight = `${minHeight}px`;
  86. }
  87. if (maxRows) {
  88. let maxHeight = singleRowHeight * maxRows;
  89. if (boxSizing === "border-box") {
  90. maxHeight = maxHeight + paddingSize + borderSize;
  91. }
  92. height = Math.min(maxHeight, height);
  93. }
  94. result.height = `${height}px`;
  95. hiddenTextarea.parentNode?.removeChild(hiddenTextarea);
  96. hiddenTextarea = undefined;
  97. return result;
  98. }