Merge pull request #2494 from hmhealey/plt2190
PLT-2190 Switched Textbox to use a TextareaAutosize and removed custom resizing code
Этот коммит содержится в:
@@ -27,8 +27,8 @@ const holders = defineMessages({
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
class EditPostModal extends React.Component {
|
class EditPostModal extends React.Component {
|
||||||
constructor() {
|
constructor(props) {
|
||||||
super();
|
super(props);
|
||||||
|
|
||||||
this.handleEdit = this.handleEdit.bind(this);
|
this.handleEdit = this.handleEdit.bind(this);
|
||||||
this.handleEditInput = this.handleEditInput.bind(this);
|
this.handleEditInput = this.handleEditInput.bind(this);
|
||||||
@@ -56,12 +56,13 @@ class EditPostModal extends React.Component {
|
|||||||
updatedPost.id = this.state.post_id;
|
updatedPost.id = this.state.post_id;
|
||||||
updatedPost.channel_id = this.state.channel_id;
|
updatedPost.channel_id = this.state.channel_id;
|
||||||
|
|
||||||
Client.updatePost(updatedPost,
|
Client.updatePost(
|
||||||
function success() {
|
updatedPost,
|
||||||
|
() => {
|
||||||
AsyncClient.getPosts(updatedPost.channel_id);
|
AsyncClient.getPosts(updatedPost.channel_id);
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
},
|
},
|
||||||
function error(err) {
|
(err) => {
|
||||||
AsyncClient.dispatchError(err, 'updatePost');
|
AsyncClient.dispatchError(err, 'updatePost');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -106,11 +107,11 @@ class EditPostModal extends React.Component {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function onHidden() {
|
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', () => {
|
||||||
self.setState({editText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''});
|
self.setState({editText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function onShow(e) {
|
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => {
|
||||||
var button = e.relatedTarget;
|
var button = e.relatedTarget;
|
||||||
if (!button) {
|
if (!button) {
|
||||||
return;
|
return;
|
||||||
@@ -118,12 +119,11 @@ class EditPostModal extends React.Component {
|
|||||||
self.setState({editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refocusid')});
|
self.setState({editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refocusid')});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', function onShown() {
|
$(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', () => {
|
||||||
self.refs.editbox.resize();
|
self.refs.editbox.focus();
|
||||||
$('#edit_textbox').get(0).focus();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', function onShown() {
|
$(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', () => {
|
||||||
if (self.state.refocusId !== '') {
|
if (self.state.refocusId !== '') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
$(self.state.refocusId).get(0).focus();
|
$(self.state.refocusId).get(0).focus();
|
||||||
@@ -221,4 +221,4 @@ EditPostModal.propTypes = {
|
|||||||
intl: intlShape.isRequired
|
intl: intlShape.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default injectIntl(EditPostModal);
|
export default injectIntl(EditPostModal);
|
||||||
|
|||||||
@@ -3,11 +3,14 @@
|
|||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import Constants from 'utils/constants.jsx';
|
import Constants from 'utils/constants.jsx';
|
||||||
import * as GlobalActions from 'action_creators/global_actions.jsx';
|
import * as GlobalActions from 'action_creators/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-textarea-autosize';
|
||||||
|
|
||||||
const KeyCodes = Constants.KeyCodes;
|
const KeyCodes = Constants.KeyCodes;
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -44,7 +47,13 @@ export default class SuggestionBox extends React.Component {
|
|||||||
|
|
||||||
getTextbox() {
|
getTextbox() {
|
||||||
// this is to support old code that looks at the input/textarea DOM nodes
|
// this is to support old code that looks at the input/textarea DOM nodes
|
||||||
return ReactDOM.findDOMNode(this.refs.textbox);
|
let textbox = this.refs.textbox;
|
||||||
|
|
||||||
|
if (!(textbox instanceof HTMLElement)) {
|
||||||
|
textbox = ReactDOM.findDOMNode(textbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
return textbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDocumentClick(e) {
|
handleDocumentClick(e) {
|
||||||
@@ -132,7 +141,8 @@ export default class SuggestionBox extends React.Component {
|
|||||||
);
|
);
|
||||||
} else if (this.props.type === 'textarea') {
|
} else if (this.props.type === 'textarea') {
|
||||||
textbox = (
|
textbox = (
|
||||||
<textarea
|
<TextareaAutosize
|
||||||
|
id={this.suggestionId}
|
||||||
ref='textbox'
|
ref='textbox'
|
||||||
{...newProps}
|
{...newProps}
|
||||||
/>
|
/>
|
||||||
@@ -163,5 +173,6 @@ SuggestionBox.propTypes = {
|
|||||||
|
|
||||||
// explicitly name any input event handlers we override and need to manually call
|
// explicitly name any input event handlers we override and need to manually call
|
||||||
onChange: React.PropTypes.func,
|
onChange: React.PropTypes.func,
|
||||||
onKeyDown: React.PropTypes.func
|
onKeyDown: React.PropTypes.func,
|
||||||
|
onHeightChange: React.PropTypes.func
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import AtMentionProvider from './suggestion/at_mention_provider.jsx';
|
import AtMentionProvider from './suggestion/at_mention_provider.jsx';
|
||||||
import CommandProvider from './suggestion/command_provider.jsx';
|
import CommandProvider from './suggestion/command_provider.jsx';
|
||||||
import EmoticonProvider from './suggestion/emoticon_provider.jsx';
|
import EmoticonProvider from './suggestion/emoticon_provider.jsx';
|
||||||
@@ -29,7 +28,7 @@ export default class Textbox extends React.Component {
|
|||||||
this.onRecievedError = this.onRecievedError.bind(this);
|
this.onRecievedError = this.onRecievedError.bind(this);
|
||||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
this.resize = this.resize.bind(this);
|
this.handleHeightChange = this.handleHeightChange.bind(this);
|
||||||
this.showPreview = this.showPreview.bind(this);
|
this.showPreview = this.showPreview.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -54,8 +53,6 @@ export default class Textbox extends React.Component {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
ErrorStore.addChangeListener(this.onRecievedError);
|
ErrorStore.addChangeListener(this.onRecievedError);
|
||||||
|
|
||||||
this.resize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@@ -72,10 +69,6 @@ export default class Textbox extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
this.resize();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyPress(e) {
|
handleKeyPress(e) {
|
||||||
this.props.onKeyPress(e);
|
this.props.onKeyPress(e);
|
||||||
}
|
}
|
||||||
@@ -86,50 +79,28 @@ export default class Textbox extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
focus() {
|
handleHeightChange(height) {
|
||||||
this.refs.message.getTextbox().focus();
|
const textbox = $(this.refs.message.getTextbox());
|
||||||
|
const wrapper = $(this.refs.wrapper);
|
||||||
|
|
||||||
|
const maxHeight = parseInt(textbox.css('max-height'), 10);
|
||||||
|
|
||||||
|
// move over attachment icon to compensate for the scrollbar
|
||||||
|
if (height > maxHeight) {
|
||||||
|
wrapper.closest('.post-body__cell').addClass('scroll');
|
||||||
|
} else {
|
||||||
|
wrapper.closest('.post-body__cell').removeClass('scroll');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resize() {
|
focus() {
|
||||||
const textbox = this.refs.message.getTextbox();
|
this.refs.message.getTextbox().focus();
|
||||||
const $textbox = $(textbox);
|
|
||||||
const $wrapper = $(ReactDOM.findDOMNode(this.refs.wrapper));
|
|
||||||
|
|
||||||
const padding = parseInt($textbox.css('padding-bottom'), 10) + parseInt($textbox.css('padding-top'), 10);
|
|
||||||
const borders = parseInt($textbox.css('border-bottom-width'), 10) + parseInt($textbox.css('border-top-width'), 10);
|
|
||||||
const maxHeight = parseInt($textbox.css('max-height'), 10) - borders;
|
|
||||||
|
|
||||||
// set the height to auto and remove the scrollbar so we can get the actual size of the contents
|
|
||||||
$textbox.css('height', 'auto').css('overflow-y', 'hidden');
|
|
||||||
|
|
||||||
let height = textbox.scrollHeight - padding;
|
|
||||||
|
|
||||||
if (height + padding > maxHeight) {
|
|
||||||
height = maxHeight - padding;
|
|
||||||
|
|
||||||
// turn scrollbar on and move over attachment icon to compensate for that
|
|
||||||
$textbox.css('overflow-y', 'scroll');
|
|
||||||
$wrapper.closest('.post-body__cell').addClass('scroll');
|
|
||||||
} else {
|
|
||||||
$wrapper.closest('.post-body__cell').removeClass('scroll');
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the textarea to be the proper height
|
|
||||||
$textbox.height(height);
|
|
||||||
|
|
||||||
// set the wrapper height to match the height of the textbox including padding and borders
|
|
||||||
$wrapper.height(height + padding + borders);
|
|
||||||
|
|
||||||
if (this.state.preview) {
|
|
||||||
$(ReactDOM.findDOMNode(this.refs.preview)).height(height + borders);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showPreview(e) {
|
showPreview(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.target.blur();
|
e.target.blur();
|
||||||
this.setState({preview: !this.state.preview});
|
this.setState({preview: !this.state.preview});
|
||||||
this.resize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -157,7 +128,7 @@ export default class Textbox extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let helpText = (
|
const helpText = (
|
||||||
<div
|
<div
|
||||||
style={{visibility: hasText ? 'visible' : 'hidden', opacity: hasText ? '0.5' : '0'}}
|
style={{visibility: hasText ? 'visible' : 'hidden', opacity: hasText ? '0.5' : '0'}}
|
||||||
className='help_format_text'
|
className='help_format_text'
|
||||||
@@ -174,12 +145,16 @@ export default class Textbox extends React.Component {
|
|||||||
defaultMessage='_italic_'
|
defaultMessage='_italic_'
|
||||||
/>
|
/>
|
||||||
</i>
|
</i>
|
||||||
<span>~~<strike>
|
<span>
|
||||||
<FormattedMessage
|
{'~~'}
|
||||||
id='textbox.strike'
|
<strike>
|
||||||
defaultMessage='strike'
|
<FormattedMessage
|
||||||
/>
|
id='textbox.strike'
|
||||||
</strike>~~ </span>
|
defaultMessage='strike'
|
||||||
|
/>
|
||||||
|
</strike>
|
||||||
|
{'~~ '}
|
||||||
|
</span>
|
||||||
<code>
|
<code>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='textbox.inlinecode'
|
id='textbox.inlinecode'
|
||||||
@@ -214,16 +189,17 @@ export default class Textbox extends React.Component {
|
|||||||
spellCheck='true'
|
spellCheck='true'
|
||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
autoCorrect='off'
|
autoCorrect='off'
|
||||||
rows='1'
|
|
||||||
maxLength={Constants.MAX_POST_LEN}
|
maxLength={Constants.MAX_POST_LEN}
|
||||||
placeholder={this.props.createMessage}
|
placeholder={this.props.createMessage}
|
||||||
value={this.props.messageText}
|
value={this.props.messageText}
|
||||||
onUserInput={this.props.onUserInput}
|
onUserInput={this.props.onUserInput}
|
||||||
onKeyPress={this.handleKeyPress}
|
onKeyPress={this.handleKeyPress}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
|
onHeightChange={this.handleHeightChange}
|
||||||
style={{visibility: this.state.preview ? 'hidden' : 'visible'}}
|
style={{visibility: this.state.preview ? 'hidden' : 'visible'}}
|
||||||
listComponent={SuggestionList}
|
listComponent={SuggestionList}
|
||||||
providers={this.suggestionProviders}
|
providers={this.suggestionProviders}
|
||||||
|
channelId={this.props.channelId}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
ref='preview'
|
ref='preview'
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"react-dom": "0.14.7",
|
"react-dom": "0.14.7",
|
||||||
"react-intl": "2.0.0-rc-1",
|
"react-intl": "2.0.0-rc-1",
|
||||||
"react-router": "2.0.1",
|
"react-router": "2.0.1",
|
||||||
|
"react-textarea-autosize": "3.3.0",
|
||||||
"twemoji": "1.4.1",
|
"twemoji": "1.4.1",
|
||||||
"velocity-animate": "1.2.3"
|
"velocity-animate": "1.2.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
.suggestion-list--top {
|
.suggestion-list--top {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.suggestion-list__content {
|
.suggestion-list__content {
|
||||||
|
|||||||
@@ -7,9 +7,7 @@
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
min-height: 36px;
|
min-height: 36px;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
position: absolute;
|
|
||||||
resize: none;
|
resize: none;
|
||||||
top: 0px;
|
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user