[FIX] Autogrow not working properly for many message boxes (#14163)
* Embed autogrow into messageBox template * Move messageBox modules * Delete obsolete messageDropdown template * Refactor imports in messagePopup modules * Destroy event listeners for autogrowpull/14253/head^2
parent
12741f2648
commit
e03bb48921
@ -1,17 +1,7 @@ |
||||
import './message.html'; |
||||
import './messageDropdown.html'; |
||||
import './popup/messagePopup.html'; |
||||
import './popup/messagePopupChannel.html'; |
||||
import './popup/messagePopupConfig.html'; |
||||
import './popup/messagePopupEmoji.html'; |
||||
import './popup/messagePopupSlashCommand.html'; |
||||
import './popup/messagePopupSlashCommandPreview.html'; |
||||
import './popup/messagePopupSlashCommandPreview'; |
||||
import './popup/messagePopupUser.html'; |
||||
import './message'; |
||||
import './messageBox'; |
||||
import './messageBox/messageBox'; |
||||
import './popup/messagePopup'; |
||||
import './popup/messagePopupChannel'; |
||||
import './popup/messagePopupConfig'; |
||||
import './popup/messagePopupEmoji'; |
||||
import './startup'; |
||||
import './popup/messagePopupSlashCommandPreview'; |
||||
|
||||
@ -0,0 +1,93 @@ |
||||
import _ from 'underscore'; |
||||
|
||||
const replaceWhitespaces = (whitespaces: string) => `${ ' '.repeat(whitespaces.length - 1) } `; |
||||
|
||||
export const setupAutogrow = (textarea: HTMLTextAreaElement, shadow: HTMLDivElement, callback: () => void) => { |
||||
const width = textarea.clientWidth; |
||||
const height = textarea.clientHeight; |
||||
const { font, lineHeight, maxHeight: maxHeightPx } = window.getComputedStyle(textarea); |
||||
|
||||
shadow.style.position = 'fixed'; |
||||
shadow.style.top = '-10000px'; |
||||
shadow.style.left = '-10000px'; |
||||
shadow.style.width = `${ width }px`; |
||||
shadow.style.font = font; |
||||
shadow.style.lineHeight = lineHeight; |
||||
shadow.style.resize = 'none'; |
||||
shadow.style.wordWrap = 'break-word'; |
||||
|
||||
const minHeight = height; |
||||
const maxHeight = parseInt(maxHeightPx, 10); |
||||
|
||||
let lastWidth = width; |
||||
let lastHeight = minHeight; |
||||
let textLenght = 0; |
||||
|
||||
const update = () => { |
||||
const { clientWidth: width, value: text } = textarea; |
||||
|
||||
const isMaximumHeightReached = lastHeight >= maxHeight; |
||||
const isTextLengthOutdated = textLenght && textLenght < text.length; |
||||
const wasWidthChanged = width !== lastWidth; |
||||
|
||||
if (isMaximumHeightReached && isTextLengthOutdated && !wasWidthChanged) { |
||||
return true; |
||||
} |
||||
|
||||
const shadowText = ( |
||||
text.replace(/</g, '<') |
||||
.replace(/>/g, '>') |
||||
.replace(/&/g, '&') |
||||
.replace(/\n$/, '<br/> ') |
||||
.replace(/\n/g, '<br/>') |
||||
.replace(/ {2,}/g, replaceWhitespaces) |
||||
); |
||||
|
||||
if (wasWidthChanged) { |
||||
shadow.style.width = `${ width }px`; |
||||
lastWidth = width; |
||||
} |
||||
|
||||
shadow.innerHTML = shadowText; |
||||
|
||||
const shadowHeight = Math.max(shadow.clientHeight + 1, minHeight) + 1; |
||||
const height = Math.min(shadowHeight, maxHeight); |
||||
|
||||
if (height === lastHeight) { |
||||
return true; |
||||
} |
||||
|
||||
lastHeight = height; |
||||
|
||||
const overflow = (height === maxHeight) ? 'hidden' : ''; |
||||
|
||||
if (height < maxHeight) { |
||||
textLenght = text.length; |
||||
} |
||||
|
||||
textarea.style.overflow = overflow; |
||||
textarea.style.height = `${ height }px`; |
||||
|
||||
callback && callback(); |
||||
}; |
||||
|
||||
const updateThrottled = _.throttle(update, 300); |
||||
|
||||
const $textarea = $(textarea); |
||||
$textarea.on('focus', update); |
||||
$textarea.on('change input', updateThrottled); |
||||
window.addEventListener('resize', updateThrottled, false); |
||||
|
||||
const destroy = () => { |
||||
$textarea.off('focus', update); |
||||
$textarea.off('change input', updateThrottled); |
||||
window.removeEventListener('resize', updateThrottled); |
||||
}; |
||||
|
||||
update(); |
||||
|
||||
return { |
||||
update, |
||||
destroy, |
||||
}; |
||||
}; |
||||
@ -1,6 +1,6 @@ |
||||
import { katex } from '../../katex/client'; |
||||
import { Markdown } from '../../markdown/client'; |
||||
import { settings } from '../../settings'; |
||||
import { katex } from '../../../katex/client'; |
||||
import { Markdown } from '../../../markdown/client'; |
||||
import { settings } from '../../../settings'; |
||||
|
||||
|
||||
export const formattingButtons = [ |
||||
@ -1,11 +1,9 @@ |
||||
import { Meteor } from 'meteor/meteor'; |
||||
import { Session } from 'meteor/session'; |
||||
import { Template } from 'meteor/templating'; |
||||
import { settings } from '../../settings'; |
||||
import { call, RoomManager, RoomHistoryManager } from '../../ui-utils'; |
||||
import { roomTypes } from '../../utils'; |
||||
|
||||
import { hasAllPermission } from '../../authorization'; |
||||
import { settings } from '../../../settings'; |
||||
import { call, roomTypes, RoomManager, RoomHistoryManager } from '../../../ui-utils'; |
||||
import { hasAllPermission } from '../../../authorization'; |
||||
import './messageBoxNotSubscribed.html'; |
||||
|
||||
|
||||
@ -1,6 +1,6 @@ |
||||
import { Template } from 'meteor/templating'; |
||||
import { MsgTyping } from '../../ui'; |
||||
import { t } from '../../utils'; |
||||
import { MsgTyping } from '../../../ui'; |
||||
import { t } from '../../../utils'; |
||||
import './messageBoxTyping.html'; |
||||
|
||||
|
||||
@ -1,12 +0,0 @@ |
||||
<template name="messageDropdown"> |
||||
<div class="message-dropdown content-background-color"> |
||||
<ul> |
||||
<li class="message-dropdown-close secondary-background-color border-component-color"><i class=" icon-angle-left" aria-label="{{_ "Close"}}"></i></li> |
||||
{{#if actions.length}} |
||||
{{#each actions}} |
||||
<li class="{{id}} {{classes}} message-action" title="{{_ i18nLabel}}" data-id="{{id}}"><i class="{{icon}}" aria-label="{{_ i18nLabel}}"></i></li> |
||||
{{/each}} |
||||
{{/if}} |
||||
</ul> |
||||
</div> |
||||
</template> |
||||
@ -1 +0,0 @@ |
||||
import './messageBoxActions'; |
||||
@ -1,101 +0,0 @@ |
||||
import _ from 'underscore'; |
||||
|
||||
const replaceWhitespaces = (whitespaces) => `${ ' '.repeat(whitespaces.length - 1) } `; |
||||
|
||||
const getShadow = () => { |
||||
let shadow = document.getElementById('autogrow-shadow'); |
||||
|
||||
if (!shadow) { |
||||
shadow = document.createElement('div'); |
||||
shadow.setAttribute('id', 'autogrow-shadow'); |
||||
document.body.appendChild(shadow); |
||||
} |
||||
|
||||
return shadow; |
||||
}; |
||||
|
||||
$.fn.autogrow = function({ postGrowCallback } = {}) { |
||||
const shadow = getShadow(); |
||||
|
||||
return this.filter('textarea').each((i, textarea) => { |
||||
const $textarea = $(textarea); |
||||
|
||||
const trigger = _.debounce(() => $textarea.trigger('autogrow', []), 500); |
||||
|
||||
const width = $textarea.width(); |
||||
const minHeight = $textarea.height(); |
||||
const maxHeight = window.getComputedStyle(textarea)['max-height'].replace('px', ''); |
||||
|
||||
let lastWidth = width; |
||||
let lastHeight = minHeight; |
||||
let length = 0; |
||||
|
||||
shadow.style.position = 'absolute'; |
||||
shadow.style.top = '-10000px'; |
||||
shadow.style.left = '-10000px'; |
||||
shadow.style.width = `${ width }px`; |
||||
shadow.style.fontSize = $textarea.css('fontSize'); |
||||
shadow.style.fontFamily = $textarea.css('fontFamily'); |
||||
shadow.style.fontWeight = $textarea.css('fontWeight'); |
||||
shadow.style.lineHeight = `${ $textarea.css('lineHeight') }px`; |
||||
shadow.style.resize = 'none'; |
||||
shadow.style.wordWrap = 'break-word'; |
||||
|
||||
const update = (event) => { |
||||
const width = $textarea.width(); |
||||
if (lastHeight >= maxHeight && length && length < textarea.value.length && width === lastWidth) { |
||||
return true; |
||||
} |
||||
|
||||
let val = textarea.value.replace(/</g, '<') |
||||
.replace(/>/g, '>') |
||||
.replace(/&/g, '&') |
||||
.replace(/\n$/, '<br/> ') |
||||
.replace(/\n/g, '<br/>') |
||||
.replace(/ {2,}/g, replaceWhitespaces); |
||||
|
||||
// Did enter get pressed? Resize in this keydown event so that the flicker doesn't occur.
|
||||
if (event && event.data && event.data.event === 'keydown' && event.keyCode === 13 && (event.shiftKey || event.ctrlKey || event.altKey)) { |
||||
val += '<br/>'; |
||||
} |
||||
|
||||
if (width !== lastWidth) { |
||||
shadow.style.width = `${ width }px`; |
||||
lastWidth = width; |
||||
} |
||||
|
||||
shadow.innerHTML = val; |
||||
|
||||
let newHeight = Math.max(shadow.clientHeight + 1, minHeight) + 1; |
||||
|
||||
let overflow = 'hidden'; |
||||
|
||||
if (newHeight >= maxHeight) { |
||||
newHeight = maxHeight; |
||||
overflow = ''; |
||||
} else { |
||||
length = textarea.value.length; |
||||
} |
||||
|
||||
if (newHeight === lastHeight) { |
||||
return true; |
||||
} |
||||
|
||||
lastHeight = newHeight; |
||||
|
||||
$textarea.css({ overflow, height: newHeight }); |
||||
|
||||
trigger(); |
||||
|
||||
postGrowCallback && postGrowCallback($textarea); |
||||
}; |
||||
|
||||
const updateThrottled = _.throttle(update, 300); |
||||
|
||||
$textarea.on('change input', updateThrottled); |
||||
$textarea.on('focus', update); |
||||
$(window).resize(updateThrottled); |
||||
update(); |
||||
textarea.updateAutogrow = update; |
||||
}); |
||||
}; |
||||
Loading…
Reference in new issue