* WIP - initial exploration into post mentioon autocomplete * replace the autosize textarea with mention autocomplete, adjusting height * add cypress tests for mentioning in a post * fix resizing of text areas * refactor text area resize hook * refactoring * update cypress tests * remove mergeRefs from utils, extract UserListItemContent * allow useTextAreaAutoResize elements to have height greater than minimum, apply styles to container * Trigger Travis CI * Travis plz * small refactors * add comments and constants as per PR feedback Co-authored-by: Nick Taylor <nick@dev.to>
105 lines
2.9 KiB
JavaScript
105 lines
2.9 KiB
JavaScript
// These styles are applied to the hidden element we use to measure the height.
|
|
// !important styles are used to ensure no matter what style properties are attached to the given textarea, the hidden textarea will never become visible or cause layout jumps
|
|
const HIDDEN_TEXTAREA_STYLE = `
|
|
min-height:0 !important;
|
|
max-height:none !important;
|
|
height:0 !important;
|
|
visibility:hidden !important;
|
|
overflow:hidden !important;
|
|
position:absolute !important;
|
|
z-index:-1000 !important;
|
|
top:0 !important;
|
|
right:0 !important
|
|
`;
|
|
|
|
const SIZING_STYLE = [
|
|
'letter-spacing',
|
|
'line-height',
|
|
'padding-top',
|
|
'padding-bottom',
|
|
'font-family',
|
|
'font-weight',
|
|
'font-size',
|
|
'text-rendering',
|
|
'text-transform',
|
|
'width',
|
|
'text-indent',
|
|
'padding-left',
|
|
'padding-right',
|
|
'border-width',
|
|
'box-sizing',
|
|
];
|
|
|
|
let hiddenTextarea;
|
|
|
|
/**
|
|
* Helper function to get the height of the textarea based on the current text content
|
|
*
|
|
* @param {HTMLElement} uiTextNode The textarea to measure height of
|
|
*
|
|
* @returns {{height: number}} Object with the calculated height
|
|
*/
|
|
export const calculateTextAreaHeight = (uiTextNode) => {
|
|
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
|
|
const {
|
|
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.placeholder || 'x';
|
|
|
|
const baseHeight = hiddenTextarea.scrollHeight;
|
|
|
|
if (boxSizing === 'border-box') {
|
|
// border-box: add border, since height = content + padding + border
|
|
return { height: baseHeight + borderSize };
|
|
} else if (boxSizing === 'content-box') {
|
|
// remove padding, since height = content
|
|
return { height: baseHeight - paddingSize };
|
|
}
|
|
|
|
return { height: baseHeight };
|
|
};
|
|
|
|
const calculateNodeStyling = (node) => {
|
|
const style = window.getComputedStyle(node);
|
|
|
|
const boxSizing =
|
|
style.getPropertyValue('box-sizing') ||
|
|
style.getPropertyValue('-moz-box-sizing') ||
|
|
style.getPropertyValue('-webkit-box-sizing');
|
|
|
|
const paddingSize =
|
|
parseFloat(style.getPropertyValue('padding-bottom')) +
|
|
parseFloat(style.getPropertyValue('padding-top'));
|
|
|
|
const borderSize =
|
|
parseFloat(style.getPropertyValue('border-bottom-width')) +
|
|
parseFloat(style.getPropertyValue('border-top-width'));
|
|
|
|
const sizingStyle = SIZING_STYLE.map(
|
|
(name) => `${name}:${style.getPropertyValue(name)}`,
|
|
).join(';');
|
|
|
|
return {
|
|
sizingStyle,
|
|
paddingSize,
|
|
borderSize,
|
|
boxSizing,
|
|
};
|
|
};
|