[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 autogrow
pull/14253/head^2
Tasso Evangelista 7 years ago committed by Guilherme Gazzo
parent 12741f2648
commit e03bb48921
  1. 61
      app/theme/client/imports/general/base_old.css
  2. 20
      app/theme/client/imports/general/rtl.css
  3. 14
      app/ui-message/client/index.js
  4. 1
      app/ui-message/client/message.js
  5. 1
      app/ui-message/client/messageBox/messageBox.html
  6. 60
      app/ui-message/client/messageBox/messageBox.js
  7. 0
      app/ui-message/client/messageBox/messageBoxActions.js
  8. 0
      app/ui-message/client/messageBox/messageBoxAudioMessage.html
  9. 10
      app/ui-message/client/messageBox/messageBoxAudioMessage.js
  10. 93
      app/ui-message/client/messageBox/messageBoxAutogrow.js
  11. 6
      app/ui-message/client/messageBox/messageBoxFormatting.js
  12. 0
      app/ui-message/client/messageBox/messageBoxNotSubscribed.html
  13. 8
      app/ui-message/client/messageBox/messageBoxNotSubscribed.js
  14. 0
      app/ui-message/client/messageBox/messageBoxReplyPreview.html
  15. 0
      app/ui-message/client/messageBox/messageBoxReplyPreview.js
  16. 0
      app/ui-message/client/messageBox/messageBoxTyping.html
  17. 4
      app/ui-message/client/messageBox/messageBoxTyping.js
  18. 12
      app/ui-message/client/messageDropdown.html
  19. 3
      app/ui-message/client/popup/messagePopup.js
  20. 1
      app/ui-message/client/popup/messagePopupChannel.js
  21. 5
      app/ui-message/client/popup/messagePopupConfig.js
  22. 1
      app/ui-message/client/popup/messagePopupEmoji.js
  23. 3
      app/ui-message/client/popup/messagePopupSlashCommandPreview.js
  24. 1
      app/ui-message/client/startup/index.js
  25. 1
      app/ui/client/index.js
  26. 101
      app/ui/client/lib/textarea-autogrow.js
  27. 1
      private/server/colors.less

@ -2712,54 +2712,6 @@ rc-old select,
display: block;
}
& .message-dropdown {
position: absolute;
z-index: 1000;
top: -5px;
left: -2px;
display: none;
overflow: hidden;
transition: transform 0.15s ease-in-out, opacity 0.15s ease-in-out;
animation: dropdown-in 0.15s ease-in-out;
border-radius: var(--border-radius);
box-shadow:
0 1px 1px 0 rgba(0, 0, 0, 0.2),
0 2px 10px 0 rgba(0, 0, 0, 0.16);
& ul {
display: flex;
display: -webkit-flex;
padding: 0;
font-size: 14px;
& li {
display: block;
padding: 0 8px;
cursor: pointer;
font-weight: 400;
line-height: 26px;
&:first-child {
padding-left: 6px;
border-width: 0 1px 0 0;
}
&:last-child {
padding-right: 13px;
}
}
}
}
& .user {
display: inline-block;
@ -5247,19 +5199,6 @@ rc-old select,
float: right;
width: auto;
& .message-dropdown {
right: -2px;
left: auto;
& ul {
flex-direction: row-reverse;
& li:first-child i::before {
content: "\d7";
}
}
}
}
.rc-old .form-inline {

@ -370,26 +370,6 @@
padding-right: 70px;
padding-left: 20px;
& .message-dropdown {
right: -2px;
left: auto;
& ul li {
&:first-child {
padding-right: 6px;
padding-left: 8px;
border-right: unset;
border-left: 1px solid #eeeeee;
}
&:last-child {
padding-right: 8px;
padding-left: 13px;
}
}
}
& .user {
margin-right: 0;
margin-left: 5px;

@ -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';

@ -15,6 +15,7 @@ import { callbacks } from '../../callbacks/client';
import { Markdown } from '../../markdown/client';
import { t, roomTypes, getURL } from '../../utils';
import { messageArgs } from '../../ui-utils/client/lib/messageArgs';
import './message.html';
async function renderPdfToCanvas(canvasId, pdfLink) {
const isSafari = /constructor/i.test(window.HTMLElement) ||

@ -24,6 +24,7 @@
{{> icon block="rc-input__icon-svg" icon="emoji"}}
</span>
<textarea name="msg" maxlength="{{maxMessageLength}}" placeholder="{{_ 'Message'}}" rows="1" class="rc-message-box__textarea js-input-message"></textarea>
<div class="js-input-message-shadow"></div>
{{#if isSendIconVisible}}
<span class="rc-message-box__icon rc-message-box__send js-send" data-desktop>
{{> icon block="rc-input__icon-svg" icon="send"}}

@ -4,37 +4,38 @@ import { ReactiveDict } from 'meteor/reactive-dict';
import { Session } from 'meteor/session';
import { Template } from 'meteor/templating';
import { Tracker } from 'meteor/tracker';
import { EmojiPicker } from '../../emoji';
import { Users } from '../../models';
import { settings } from '../../settings';
import { EmojiPicker } from '../../../emoji';
import { Users } from '../../../models';
import { settings } from '../../../settings';
import {
fileUpload,
KonchatNotification,
} from '../../ui';
} from '../../../ui';
import {
messageBox,
popover,
call,
keyCodes,
isRTL,
} from '../../ui-utils';
} from '../../../ui-utils';
import {
t,
roomTypes,
getUserPreference,
} from '../../utils';
} from '../../../utils';
import moment from 'moment';
import { setupAutogrow } from './messageBoxAutogrow';
import {
formattingButtons,
applyFormatting,
} from './messageBoxFormatting';
import './messageBoxActions';
import './messageBoxReplyPreview';
import './messageBoxTyping';
import './messageBoxAudioMessage';
import './messageBoxNotSubscribed';
import './messageBox.html';
Template.messageBox.onCreated(function() {
this.state = new ReactiveDict();
EmojiPicker.init();
@ -54,7 +55,7 @@ Template.messageBox.onCreated(function() {
};
this.insertNewLine = () => {
const { input } = this;
const { input, autogrow } = this;
if (!input) {
return;
}
@ -76,26 +77,27 @@ Template.messageBox.onCreated(function() {
input.blur();
input.focus();
input.updateAutogrow();
autogrow.update();
};
this.send = (event) => {
if (!this.input) {
const { input } = this;
if (!input) {
return;
}
const { rid, tmid, onSend } = this.data;
const { value } = this.input;
const { autogrow, data: { rid, tmid, onSend } } = this;
const { value } = input;
this.set('');
onSend && onSend.call(this.data, event, { rid, tmid, value }, () => {
this.input.updateAutogrow();
this.input.focus();
autogrow.update();
input.focus();
});
};
});
Template.messageBox.onRendered(function() {
this.autorun(() => {
const { rid, subscription } = Template.currentData();
const room = Session.get(`roomData${ rid }`);
@ -143,24 +145,35 @@ Template.messageBox.onRendered(function() {
this.popupConfig.set(null);
}
if (this.autogrow) {
this.autogrow.destroy();
this.autogrow = null;
}
if (!input) {
return;
}
const $input = $(input);
const shadow = this.find('.js-input-message-shadow');
this.autogrow = setupAutogrow(input, shadow, onResize);
const $input = $(input);
$input.on('dataChange', () => {
const messages = $input.data('reply') || [];
this.replyMessageData.set(messages);
});
$input.autogrow().on('autogrow', () => {
onResize && onResize();
});
});
});
});
Template.messageBox.onDestroyed(function() {
if (!this.autogrow) {
return;
}
this.autogrow.destroy();
});
Template.messageBox.helpers({
isAnonymousOrMustJoinWithCode() {
const instance = Template.instance();
@ -337,10 +350,9 @@ Template.messageBox.events({
},
'paste .js-input-message'(event, instance) {
const { rid, tmid } = this;
const { input } = instance;
setTimeout(() => {
typeof input.updateAutogrow === 'function' && input.updateAutogrow();
}, 50);
const { input, autogrow } = instance;
setTimeout(() => autogrow && autogrow.update(), 50);
if (!event.originalEvent.clipboardData) {
return;

@ -2,11 +2,11 @@ import { ReactiveVar } from 'meteor/reactive-var';
import { Session } from 'meteor/session';
import { Tracker } from 'meteor/tracker';
import { Template } from 'meteor/templating';
import { fileUploadHandler } from '../../file-upload';
import { settings } from '../../settings';
import { AudioRecorder } from '../../ui';
import { call } from '../../ui-utils';
import { t } from '../../utils';
import { fileUploadHandler } from '../../../file-upload';
import { settings } from '../../../settings';
import { AudioRecorder } from '../../../ui';
import { call } from '../../../ui-utils';
import { t } from '../../../utils';
import './messageBoxAudioMessage.html';
const startRecording = () => new Promise((resolve, reject) =>

@ -0,0 +1,93 @@
import _ from 'underscore';
const replaceWhitespaces = (whitespaces: string) => `${ '&nbsp;'.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, '&lt;')
.replace(/>/g, '&gt;')
.replace(/&/g, '&amp;')
.replace(/\n$/, '<br/>&nbsp;')
.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,12 +1,13 @@
// This is not supposed to be a complete list
// it is just to improve readability in this file
import _ from 'underscore';
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import { toolbarSearch } from '../../../ui-sidenav';
import _ from 'underscore';
import { lazyloadtick } from '../../../lazy-load';
import './messagePopup.html';
const keys = {
TAB: 9,

@ -1,5 +1,6 @@
import { Template } from 'meteor/templating';
import { roomTypes } from '../../../utils';
import './messagePopupChannel.html';
Template.messagePopupChannel.helpers({
channelIcon() {

@ -1,3 +1,4 @@
import _ from 'underscore';
import { Blaze } from 'meteor/blaze';
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
@ -10,7 +11,9 @@ import { hasAllPermission, hasAtLeastOnePermission } from '../../../authorizatio
import { EmojiPicker, emoji } from '../../../emoji';
import { call } from '../../../ui-utils';
import { t, getUserPreference, slashCommands } from '../../../utils';
import _ from 'underscore';
import './messagePopupConfig.html';
import './messagePopupSlashCommand.html';
import './messagePopupUser.html';
const reloadUsersFromRoomMessages = (rid, template) => {
const user = Meteor.userId() && Meteor.users.findOne(Meteor.userId(), { fields: { username: 1 } });

@ -1,4 +1,5 @@
import { Template } from 'meteor/templating';
import './messagePopupEmoji.html';
Template.messagePopupEmoji.helpers({
value() {

@ -1,10 +1,11 @@
import _ from 'underscore';
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import { slashCommands } from '../../../utils';
import { hasAtLeastOnePermission } from '../../../authorization';
import { toolbarSearch } from '../../../ui-sidenav';
import _ from 'underscore';
import './messagePopupSlashCommandPreview.html';
const keys = {
TAB: 9,

@ -1 +0,0 @@
import './messageBoxActions';

@ -10,7 +10,6 @@ export { MsgTyping } from './lib/msgTyping';
export { KonchatNotification } from './lib/notification';
import './lib/parentTemplate';
export { Login, animationSupport, animeBack, Button, preLoadImgs } from './lib/rocket';
import './lib/textarea-autogrow';
import './lib/codeMirror/codeMirror';
export { AudioRecorder } from './lib/recorderjs/audioRecorder';
export { VideoRecorder } from './lib/recorderjs/videoRecorder';

@ -1,101 +0,0 @@
import _ from 'underscore';
const replaceWhitespaces = (whitespaces) => `${ '&nbsp;'.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, '&lt;')
.replace(/>/g, '&gt;')
.replace(/&/g, '&amp;')
.replace(/\n$/, '<br/>&nbsp;')
.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;
});
};

@ -579,7 +579,6 @@ input:-webkit-autofill {
border-color: @component-color;
}
.message-dropdown,
.options-menu {
color: lighten(@primary-font-color, 13%);

Loading…
Cancel
Save