calculateNodeHeight.ts 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // Reference to https://github.com/andreypopp/react-textarea-autosize/
  2. let hiddenTextarea: any = null;
  3. const HIDDEN_TEXTAREA_STYLE = {
  4. 'min-height': '0',
  5. 'max-height': 'none',
  6. height: '0',
  7. visibility: 'hidden',
  8. overflow: 'hidden',
  9. position: 'absolute',
  10. 'z-index': '-1000',
  11. top: '0',
  12. right: '0',
  13. };
  14. const forceHiddenStyles = (node: any) => {
  15. Object.keys(HIDDEN_TEXTAREA_STYLE).forEach(key => {
  16. node.style.setProperty(
  17. key,
  18. HIDDEN_TEXTAREA_STYLE[key],
  19. 'important'
  20. );
  21. });
  22. };
  23. const getContentHeight = (
  24. node: any,
  25. sizingData: any
  26. ) => {
  27. const height = node.scrollHeight;
  28. if (sizingData.sizingStyle.boxSizing === 'border-box') {
  29. // border-box: add border, since height = content + padding + border
  30. return height + sizingData.borderSize;
  31. }
  32. // remove padding, since height = content
  33. return height - sizingData.paddingSize;
  34. };
  35. export default function calculateNodeHeight(
  36. sizingData: any,
  37. value: string,
  38. minRows = 1,
  39. maxRows = Infinity
  40. ) {
  41. if (!hiddenTextarea) {
  42. hiddenTextarea = document.createElement('textarea');
  43. hiddenTextarea.setAttribute('tab-index', '-1');
  44. hiddenTextarea.setAttribute('aria-hidden', 'true');
  45. forceHiddenStyles(hiddenTextarea);
  46. }
  47. if (hiddenTextarea.parentNode === null) {
  48. document.body.appendChild(hiddenTextarea);
  49. }
  50. const { paddingSize, borderSize, sizingStyle } = sizingData;
  51. const { boxSizing } = sizingStyle;
  52. Object.keys(sizingStyle).forEach(key => {
  53. hiddenTextarea.style[key] = sizingStyle[key];
  54. });
  55. forceHiddenStyles(hiddenTextarea);
  56. hiddenTextarea.value = value;
  57. let height = getContentHeight(hiddenTextarea, sizingData);
  58. // measure height of a textarea with a single row
  59. hiddenTextarea.value = 'x';
  60. // calc single row need to remove padding and border to avoid duplicated calc
  61. const rowHeight = getContentHeight(hiddenTextarea, sizingData) - paddingSize - borderSize;
  62. let minHeight = rowHeight * minRows;
  63. if (boxSizing === 'border-box') {
  64. minHeight = minHeight + paddingSize + borderSize;
  65. }
  66. height = Math.max(minHeight, height);
  67. let maxHeight = rowHeight * maxRows;
  68. if (boxSizing === 'border-box') {
  69. maxHeight = maxHeight + paddingSize + borderSize;
  70. }
  71. height = Math.min(maxHeight, height);
  72. return height;
  73. }