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.
|
||||
|
||||
* 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() {
|
||||
this.refs.editbox.focus();
|
||||
|
||||
this.refs.editbox.recalculateSize();
|
||||
}
|
||||
|
||||
onModalHide() {
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import SuggestionStore from 'stores/suggestion_store.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;
|
||||
|
||||
@@ -46,14 +46,17 @@ export default class SuggestionBox extends React.Component {
|
||||
}
|
||||
|
||||
getTextbox() {
|
||||
// this is to support old code that looks at the input/textarea DOM nodes
|
||||
let textbox = this.refs.textbox;
|
||||
|
||||
if (!(textbox instanceof HTMLElement)) {
|
||||
textbox = ReactDOM.findDOMNode(textbox);
|
||||
if (this.props.type === 'textarea') {
|
||||
return this.refs.textbox.getDOMNode();
|
||||
}
|
||||
|
||||
return textbox;
|
||||
return this.refs.textbox;
|
||||
}
|
||||
|
||||
recalculateSize() {
|
||||
if (this.props.type === 'textarea') {
|
||||
this.refs.textbox.recalculateSize();
|
||||
}
|
||||
}
|
||||
|
||||
handleDocumentClick(e) {
|
||||
@@ -71,7 +74,7 @@ export default class SuggestionBox extends React.Component {
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
const textbox = ReactDOM.findDOMNode(this.refs.textbox);
|
||||
const textbox = this.getTextbox();
|
||||
const caret = Utils.getCaretPosition(textbox);
|
||||
const pretext = textbox.value.substring(0, caret);
|
||||
|
||||
@@ -83,7 +86,7 @@ export default class SuggestionBox extends React.Component {
|
||||
}
|
||||
|
||||
handleCompleteWord(term, matchedPretext) {
|
||||
const textbox = ReactDOM.findDOMNode(this.refs.textbox);
|
||||
const textbox = this.getTextbox();
|
||||
const caret = Utils.getCaretPosition(textbox);
|
||||
const text = this.props.value;
|
||||
const pretext = text.substring(0, caret);
|
||||
@@ -150,48 +153,57 @@ export default class SuggestionBox extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
type,
|
||||
listComponent,
|
||||
listStyle,
|
||||
renderDividers,
|
||||
...props
|
||||
} = this.props;
|
||||
|
||||
let textbox = null;
|
||||
if (this.props.type === 'input') {
|
||||
if (type === 'input') {
|
||||
textbox = (
|
||||
<input
|
||||
ref='textbox'
|
||||
type='text'
|
||||
{...this.props}
|
||||
onChange={this.handleChange}
|
||||
{...props}
|
||||
onInput={this.handleChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
/>
|
||||
);
|
||||
} else if (this.props.type === 'search') {
|
||||
} else if (type === 'search') {
|
||||
textbox = (
|
||||
<input
|
||||
ref='textbox'
|
||||
type='search'
|
||||
{...this.props}
|
||||
onChange={this.handleChange}
|
||||
{...props}
|
||||
onInput={this.handleChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
/>
|
||||
);
|
||||
} else if (this.props.type === 'textarea') {
|
||||
} else if (type === 'textarea') {
|
||||
textbox = (
|
||||
<TextareaAutosize
|
||||
<AutosizeTextarea
|
||||
id={this.suggestionId}
|
||||
ref='textbox'
|
||||
{...this.props}
|
||||
onChange={this.handleChange}
|
||||
{...props}
|
||||
onInput={this.handleChange}
|
||||
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 (
|
||||
<div>
|
||||
{textbox}
|
||||
<SuggestionListComponent
|
||||
suggestionId={this.suggestionId}
|
||||
location={this.props.listStyle}
|
||||
renderDividers={this.props.renderDividers}
|
||||
location={listStyle}
|
||||
renderDividers={renderDividers}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@ export default class Textbox extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.focus = this.focus.bind(this);
|
||||
this.recalculateSize = this.recalculateSize.bind(this);
|
||||
this.getStateFromStores = this.getStateFromStores.bind(this);
|
||||
this.onRecievedError = this.onRecievedError.bind(this);
|
||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||
@@ -91,13 +92,10 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleHeightChange(height) {
|
||||
const textbox = $(this.refs.message.getTextbox());
|
||||
handleHeightChange(height, maxHeight) {
|
||||
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) {
|
||||
wrapper.closest('.post-body__cell').addClass('scroll');
|
||||
} else {
|
||||
@@ -106,7 +104,14 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.refs.message.getTextbox().focus();
|
||||
const textbox = this.refs.message.getTextbox();
|
||||
|
||||
textbox.focus();
|
||||
Utils.placeCaretAtEnd(textbox);
|
||||
}
|
||||
|
||||
recalculateSize() {
|
||||
this.refs.message.recalculateSize();
|
||||
}
|
||||
|
||||
showPreview(e) {
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
"perfect-scrollbar": "0.6.12",
|
||||
"react": "15.3.2",
|
||||
"react-addons-pure-render-mixin": "15.3.2",
|
||||
"react-autosize-textarea": "0.3.3",
|
||||
"react-bootstrap": "0.30.3",
|
||||
"react-custom-scrollbars": "4.0.0",
|
||||
"react-dom": "15.3.2",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
.custom-textarea {
|
||||
max-height: 60vh;
|
||||
min-height: 20em;
|
||||
min-height: 8em;
|
||||
}
|
||||
|
||||
.suggestion-list {
|
||||
|
||||
@@ -22,31 +22,6 @@
|
||||
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 {
|
||||
min-height: 36px;
|
||||
position: relative;
|
||||
@@ -390,7 +365,6 @@ body.ios {
|
||||
|
||||
.custom-textarea {
|
||||
bottom: 0;
|
||||
line-height: 1.5;
|
||||
max-height: 162px !important;
|
||||
padding-right: 28px;
|
||||
padding-top: 8px;
|
||||
|
||||
Ссылка в новой задаче
Block a user