PLT-4404/PLT-4578/PLT-4541/PLT-4542 Replaced third party autosizing textarea with a custom one (#4442)
* PLT-4578 Replaced third party autosizing textarea with a custom one * Fix Textbox.handleHeightChange not being called * Removed unused CSS * PLT-4541 Force EditPostModal to resize upon opening * Removed usage of jquery from AutosizeTextarea * Reverted changes made for PLT-4580 as they're no longer needed
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
7403bbce69
Коммит
e63e80dee0
31
NOTICE.txt
31
NOTICE.txt
@@ -1284,37 +1284,6 @@ limitations under the License.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
This product contains a modified portion of 'react-autosize-textarea', a light replacement for built-in textarea component which automaticaly adjusts its height to match the content.
|
|
||||||
|
|
||||||
* HOMEPAGE:
|
|
||||||
* https://github.com/buildo/react-autosize-textarea
|
|
||||||
|
|
||||||
* LICENSE:
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2016 buildo s.r.l.s.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
This product contains a modified portion of 'compass-mixins', which pulls SASS style sheets on Bower, and enjoys the compass mixins by using libsass for faster compilation, by Christopher M. Eppstein.
|
This product contains a modified portion of 'compass-mixins', which pulls SASS style sheets on Bower, and enjoys the compass mixins by using libsass for faster compilation, by Christopher M. Eppstein.
|
||||||
|
|
||||||
* HOMEPAGE:
|
* HOMEPAGE:
|
||||||
|
|||||||
90
webapp/components/autosize_textarea.jsx
Обычный файл
90
webapp/components/autosize_textarea.jsx
Обычный файл
@@ -0,0 +1,90 @@
|
|||||||
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default class AutosizeTextarea extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
value: React.PropTypes.string,
|
||||||
|
placeholder: React.PropTypes.string,
|
||||||
|
onHeightChange: React.PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.refs.textarea.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(value) {
|
||||||
|
this.refs.textarea.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this.recalculateSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
recalculateSize() {
|
||||||
|
const height = this.refs.reference.scrollHeight;
|
||||||
|
|
||||||
|
if (height > 0 && height !== this.height) {
|
||||||
|
const textarea = this.refs.textarea;
|
||||||
|
|
||||||
|
const style = getComputedStyle(textarea);
|
||||||
|
const borderWidth = parseInt(style.borderTopWidth, 10) + parseInt(style.borderBottomWidth, 10);
|
||||||
|
|
||||||
|
// Directly change the height to avoid circular rerenders
|
||||||
|
textarea.style.height = String(height + borderWidth) + 'px';
|
||||||
|
|
||||||
|
this.height = height;
|
||||||
|
|
||||||
|
if (this.props.onHeightChange) {
|
||||||
|
this.props.onHeightChange(height, parseInt(style.maxHeight, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDOMNode() {
|
||||||
|
return this.refs.textarea;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
window.forceUpdate = this.forceUpdate.bind(this);
|
||||||
|
const {
|
||||||
|
value,
|
||||||
|
placeholder,
|
||||||
|
...otherProps
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const heightProps = {};
|
||||||
|
if (this.height <= 0) {
|
||||||
|
// Set an initial number of rows so that the textarea doesn't appear too large when its first rendered
|
||||||
|
heightProps.rows = 1;
|
||||||
|
} else {
|
||||||
|
heightProps.height = this.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<textarea
|
||||||
|
ref='textarea'
|
||||||
|
{...heightProps}
|
||||||
|
{...this.props}
|
||||||
|
/>
|
||||||
|
<div style={{height: 0, overflow: 'hidden'}}>
|
||||||
|
<textarea
|
||||||
|
ref='reference'
|
||||||
|
style={{height: 'auto', width: '100%'}}
|
||||||
|
value={value || placeholder}
|
||||||
|
rows='1'
|
||||||
|
{...otherProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -156,6 +156,8 @@ export default class EditPostModal extends React.Component {
|
|||||||
|
|
||||||
onModalShown() {
|
onModalShown() {
|
||||||
this.refs.editbox.focus();
|
this.refs.editbox.focus();
|
||||||
|
|
||||||
|
this.refs.editbox.recalculateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
onModalHide() {
|
onModalHide() {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import * as GlobalActions from 'actions/global_actions.jsx';
|
|||||||
import SuggestionStore from 'stores/suggestion_store.jsx';
|
import SuggestionStore from 'stores/suggestion_store.jsx';
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.jsx';
|
||||||
|
|
||||||
import TextareaAutosize from 'react-autosize-textarea';
|
import AutosizeTextarea from 'components/autosize_textarea.jsx';
|
||||||
|
|
||||||
const KeyCodes = Constants.KeyCodes;
|
const KeyCodes = Constants.KeyCodes;
|
||||||
|
|
||||||
@@ -46,14 +46,17 @@ export default class SuggestionBox extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTextbox() {
|
getTextbox() {
|
||||||
// this is to support old code that looks at the input/textarea DOM nodes
|
if (this.props.type === 'textarea') {
|
||||||
let textbox = this.refs.textbox;
|
return this.refs.textbox.getDOMNode();
|
||||||
|
|
||||||
if (!(textbox instanceof HTMLElement)) {
|
|
||||||
textbox = ReactDOM.findDOMNode(textbox);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return textbox;
|
return this.refs.textbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
recalculateSize() {
|
||||||
|
if (this.props.type === 'textarea') {
|
||||||
|
this.refs.textbox.recalculateSize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDocumentClick(e) {
|
handleDocumentClick(e) {
|
||||||
@@ -71,7 +74,7 @@ export default class SuggestionBox extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleChange(e) {
|
handleChange(e) {
|
||||||
const textbox = ReactDOM.findDOMNode(this.refs.textbox);
|
const textbox = this.getTextbox();
|
||||||
const caret = Utils.getCaretPosition(textbox);
|
const caret = Utils.getCaretPosition(textbox);
|
||||||
const pretext = textbox.value.substring(0, caret);
|
const pretext = textbox.value.substring(0, caret);
|
||||||
|
|
||||||
@@ -83,7 +86,7 @@ export default class SuggestionBox extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleCompleteWord(term, matchedPretext) {
|
handleCompleteWord(term, matchedPretext) {
|
||||||
const textbox = ReactDOM.findDOMNode(this.refs.textbox);
|
const textbox = this.getTextbox();
|
||||||
const caret = Utils.getCaretPosition(textbox);
|
const caret = Utils.getCaretPosition(textbox);
|
||||||
const text = this.props.value;
|
const text = this.props.value;
|
||||||
const pretext = text.substring(0, caret);
|
const pretext = text.substring(0, caret);
|
||||||
@@ -150,48 +153,57 @@ export default class SuggestionBox extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const {
|
||||||
|
type,
|
||||||
|
listComponent,
|
||||||
|
listStyle,
|
||||||
|
renderDividers,
|
||||||
|
...props
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
let textbox = null;
|
let textbox = null;
|
||||||
if (this.props.type === 'input') {
|
if (type === 'input') {
|
||||||
textbox = (
|
textbox = (
|
||||||
<input
|
<input
|
||||||
ref='textbox'
|
ref='textbox'
|
||||||
type='text'
|
type='text'
|
||||||
{...this.props}
|
{...props}
|
||||||
onChange={this.handleChange}
|
onInput={this.handleChange}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (this.props.type === 'search') {
|
} else if (type === 'search') {
|
||||||
textbox = (
|
textbox = (
|
||||||
<input
|
<input
|
||||||
ref='textbox'
|
ref='textbox'
|
||||||
type='search'
|
type='search'
|
||||||
{...this.props}
|
{...props}
|
||||||
onChange={this.handleChange}
|
onInput={this.handleChange}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (this.props.type === 'textarea') {
|
} else if (type === 'textarea') {
|
||||||
textbox = (
|
textbox = (
|
||||||
<TextareaAutosize
|
<AutosizeTextarea
|
||||||
id={this.suggestionId}
|
id={this.suggestionId}
|
||||||
ref='textbox'
|
ref='textbox'
|
||||||
{...this.props}
|
{...props}
|
||||||
onChange={this.handleChange}
|
onInput={this.handleChange}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const SuggestionListComponent = this.props.listComponent;
|
// This needs to be upper case so React doesn't think it's an html tag
|
||||||
|
const SuggestionListComponent = listComponent;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{textbox}
|
{textbox}
|
||||||
<SuggestionListComponent
|
<SuggestionListComponent
|
||||||
suggestionId={this.suggestionId}
|
suggestionId={this.suggestionId}
|
||||||
location={this.props.listStyle}
|
location={listStyle}
|
||||||
renderDividers={this.props.renderDividers}
|
renderDividers={renderDividers}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export default class Textbox extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.focus = this.focus.bind(this);
|
this.focus = this.focus.bind(this);
|
||||||
|
this.recalculateSize = this.recalculateSize.bind(this);
|
||||||
this.getStateFromStores = this.getStateFromStores.bind(this);
|
this.getStateFromStores = this.getStateFromStores.bind(this);
|
||||||
this.onRecievedError = this.onRecievedError.bind(this);
|
this.onRecievedError = this.onRecievedError.bind(this);
|
||||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||||
@@ -91,13 +92,10 @@ export default class Textbox extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHeightChange(height) {
|
handleHeightChange(height, maxHeight) {
|
||||||
const textbox = $(this.refs.message.getTextbox());
|
|
||||||
const wrapper = $(this.refs.wrapper);
|
const wrapper = $(this.refs.wrapper);
|
||||||
|
|
||||||
const maxHeight = parseInt(textbox.css('max-height'), 10);
|
// Move over attachment icon to compensate for the scrollbar
|
||||||
|
|
||||||
// move over attachment icon to compensate for the scrollbar
|
|
||||||
if (height > maxHeight) {
|
if (height > maxHeight) {
|
||||||
wrapper.closest('.post-body__cell').addClass('scroll');
|
wrapper.closest('.post-body__cell').addClass('scroll');
|
||||||
} else {
|
} else {
|
||||||
@@ -106,7 +104,14 @@ export default class Textbox extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
this.refs.message.getTextbox().focus();
|
const textbox = this.refs.message.getTextbox();
|
||||||
|
|
||||||
|
textbox.focus();
|
||||||
|
Utils.placeCaretAtEnd(textbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
recalculateSize() {
|
||||||
|
this.refs.message.recalculateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
showPreview(e) {
|
showPreview(e) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
"perfect-scrollbar": "0.6.12",
|
"perfect-scrollbar": "0.6.12",
|
||||||
"react": "15.3.2",
|
"react": "15.3.2",
|
||||||
"react-addons-pure-render-mixin": "15.3.2",
|
"react-addons-pure-render-mixin": "15.3.2",
|
||||||
"react-autosize-textarea": "0.3.3",
|
|
||||||
"react-bootstrap": "0.30.3",
|
"react-bootstrap": "0.30.3",
|
||||||
"react-custom-scrollbars": "4.0.0",
|
"react-custom-scrollbars": "4.0.0",
|
||||||
"react-dom": "15.3.2",
|
"react-dom": "15.3.2",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
.custom-textarea {
|
.custom-textarea {
|
||||||
max-height: 60vh;
|
max-height: 60vh;
|
||||||
min-height: 20em;
|
min-height: 8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.suggestion-list {
|
.suggestion-list {
|
||||||
|
|||||||
@@ -22,31 +22,6 @@
|
|||||||
color: #d04444 !important;
|
color: #d04444 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea-div {
|
|
||||||
border: 0;
|
|
||||||
color: rgba(0,0,0,0);
|
|
||||||
height: auto;
|
|
||||||
left: 1px;
|
|
||||||
line-height: 20px;
|
|
||||||
min-height: 36px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-word;
|
|
||||||
word-wrap: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.ios {
|
|
||||||
.textarea-div {
|
|
||||||
-webkit-overflow-scrolling: auto;
|
|
||||||
padding: 7px 17px 7px 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.textarea-div::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textarea-wrapper {
|
.textarea-wrapper {
|
||||||
min-height: 36px;
|
min-height: 36px;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -390,7 +365,6 @@ body.ios {
|
|||||||
|
|
||||||
.custom-textarea {
|
.custom-textarea {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
line-height: 1.5;
|
|
||||||
max-height: 162px !important;
|
max-height: 162px !important;
|
||||||
padding-right: 28px;
|
padding-right: 28px;
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user