All files / ship-components-textinput/src nodeHeight.js

76.47% Statements 26/34
64.71% Branches 11/17
80% Functions 4/5
76.47% Lines 26/34
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136        1x                   1x                                             8x     8x     8x           8x         8x       8x 112x     8x             8x       8x 1x 1x                   8x         8x 8x   8x           8x     8x           8x 8x   8x                     8x 16x                   8x    
/**
 * Stypes to hide the text input
 * @type {String}
 */
const HIDDEN_TEXTAREA_STYLE = `
  height:0;
  visibility:hidden;
  overflow:hidden;
  position:absolute;
  z-index:-1000;
  top:0;
  right:0
`;
 
const SIZING_STYLE = [
  'letter-spacing',
  'line-height',
  'padding-top',
  'padding-bottom',
  'font-family',
  'font-weight',
  'font-size',
  'text-rendering',
  'text-transform',
  'width',
  'padding-left',
  'padding-right',
  'border-width',
  'box-sizing'
];
 
/**
 * Reference to text area we use to calculate the styles
 */
let hiddenTextarea;
 
function calculateNodeStyling(node) {
  Iif (!node) {
    throw new TypeError('InvalidArgument: missing argument');
  }
  let style = window.getComputedStyle(node);
 
  let boxSizing = (
    style.getPropertyValue('box-sizing') ||
    style.getPropertyValue('-moz-box-sizing') ||
    style.getPropertyValue('-webkit-box-sizing')
  );
 
  let paddingSize = (
    parseFloat(style.getPropertyValue('padding-bottom')) +
    parseFloat(style.getPropertyValue('padding-top'))
  );
 
  let borderSize = (
    parseFloat(style.getPropertyValue('border-bottom-width')) +
    parseFloat(style.getPropertyValue('border-top-width'))
  );
 
  let sizingStyle = SIZING_STYLE
    .map(name => `${name}:${style.getPropertyValue(name)}`)
    .join(';');
 
  let nodeInfo = {
    sizingStyle,
    paddingSize,
    borderSize,
    boxSizing
  };
 
  return nodeInfo;
}
 
export default function nodeHeight(uiTextNode, minRows = null, maxRows = null) {
  if (!hiddenTextarea) {
    hiddenTextarea = document.createElement('textarea');
    document.body.appendChild(hiddenTextarea);
  }
 
  // Copy all CSS properties that have an impact on the height of the content in
  // the textbox
  let {
    paddingSize,
    borderSize,
    boxSizing,
    sizingStyle
  } = calculateNodeStyling(uiTextNode);
 
  // Need to have the overflow attribute to hide the scrollbar otherwise
  // text-lines will not calculated properly as the shadow will technically be
  // narrower for content
  hiddenTextarea.setAttribute('style', sizingStyle + ';' + HIDDEN_TEXTAREA_STYLE);
  hiddenTextarea.value = uiTextNode.value ? uiTextNode.value : '';
 
  let result = {
    minHeight: -Infinity,
    maxHeight: Infinity,
    height: hiddenTextarea.scrollHeight
  }
 
  Iif (boxSizing === 'border-box') {
    // border-box: add border, since height = content + padding + border
    result.height = result.height + borderSize;
  } else Iif (boxSizing === 'content-box') {
    // remove padding, since height = content
    result.height = result.height - paddingSize;
  }
 
  // measure height of a textarea with a single row
  hiddenTextarea.value = '';
  let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
 
  const modifiers = [{
    fn: 'max',
    value: maxRows,
    prop: 'maxHeight'
  },
  {
    fn: 'min',
    value: minRows,
    prop: 'minHeight'
  }];
 
  modifiers
    .filter((modifier) => modifier.value !== null)
    .forEach((mod) => {
      let rowHeight = singleRowHeight * mod.value;
      if (boxSizing === 'border-box') {
        rowHeight = rowHeight + paddingSize + borderSize;
      }
      result.height = Math[mod.fn](rowHeight, result.height);
      result[mod.prop] = rowHeight;
    });
 
  return result;
}