New add channel modal using react-bootstrap.
Этот коммит содержится в:
10
Makefile
10
Makefile
@@ -78,9 +78,10 @@ travis:
|
||||
|
||||
mv $(DIST_PATH)/web/static/js/bundle.min.js $(DIST_PATH)/web/static/js/bundle-$(BUILD_NUMBER).min.js
|
||||
|
||||
@sed -i'.bak' 's|react-with-addons-0.13.1.js|react-with-addons-0.13.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-with-addons-0.13.3.js|react-with-addons-0.13.3.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|jquery-1.11.1.js|jquery-1.11.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|bootstrap-3.3.1.js|bootstrap-3.3.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|bootstrap-3.3.5.js|bootstrap-3.3.5.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-bootstrap-0.25.1.js|react-bootstrap-0.25.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|perfect-scrollbar.js|perfect-scrollbar.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|bundle.js|bundle-$(BUILD_NUMBER).min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
rm $(DIST_PATH)/web/templates/*.bak
|
||||
@@ -231,9 +232,10 @@ dist: install
|
||||
|
||||
mv $(DIST_PATH)/web/static/js/bundle.min.js $(DIST_PATH)/web/static/js/bundle-$(BUILD_NUMBER).min.js
|
||||
|
||||
@sed -i'.bak' 's|react-with-addons-0.13.1.js|react-with-addons-0.13.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-with-addons-0.13.3.js|react-with-addons-0.13.3.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|jquery-1.11.1.js|jquery-1.11.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|bootstrap-3.3.1.js|bootstrap-3.3.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|bootstrap-3.3.5.js|bootstrap-3.3.5.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-bootstrap-0.25.1.js|react-bootstrap-0.25.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|perfect-scrollbar.js|perfect-scrollbar.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|bundle.js|bundle-$(BUILD_NUMBER).min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
rm $(DIST_PATH)/web/templates/*.bak
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
"es6": true
|
||||
},
|
||||
"globals": {
|
||||
"React": false
|
||||
"React": false,
|
||||
"ReactBootstrap": false
|
||||
},
|
||||
"rules": {
|
||||
"comma-dangle": [2, "never"],
|
||||
|
||||
177
web/react/components/change_url_modal.jsx
Обычный файл
177
web/react/components/change_url_modal.jsx
Обычный файл
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var Modal = ReactBootstrap.Modal;
|
||||
var Utils = require('../utils/utils.jsx');
|
||||
|
||||
export default class ChangeUrlModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.onURLChanged = this.onURLChanged.bind(this);
|
||||
this.doSubmit = this.doSubmit.bind(this);
|
||||
this.doCancel = this.doCancel.bind(this);
|
||||
|
||||
this.state = {
|
||||
currentURL: props.currentURL,
|
||||
urlError: '',
|
||||
userEdit: false
|
||||
};
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// This check prevents the url being deleted when we re-render
|
||||
// because of user status check
|
||||
if (!this.state.userEdit) {
|
||||
this.setState({
|
||||
currentURL: nextProps.currentURL
|
||||
});
|
||||
}
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.show === true && prevProps.show === false) {
|
||||
React.findDOMNode(this.refs.urlinput).select();
|
||||
}
|
||||
}
|
||||
onURLChanged(e) {
|
||||
const url = e.target.value.trim();
|
||||
this.setState({currentURL: url.replace(/[^A-Za-z0-9-_]/g, '').toLowerCase(), userEdit: true});
|
||||
}
|
||||
getURLError(url) {
|
||||
let error = []; //eslint-disable-line prefer-const
|
||||
if (url.length < 2) {
|
||||
error.push(<span key='error1'>{'Must be longer than two characters'}<br/></span>);
|
||||
}
|
||||
if (url.charAt(0) === '-' || url.charAt(0) === '_') {
|
||||
error.push(<span key='error2'>{'Must start with a letter or number'}<br/></span>);
|
||||
}
|
||||
if (url.length > 1 && (url.charAt(url.length - 1) === '-' || url.charAt(url.length - 1) === '_')) {
|
||||
error.push(<span key='error3'>{'Must end with a letter or number'}<br/></span>);
|
||||
}
|
||||
if (url.indexOf('__') > -1) {
|
||||
error.push(<span key='error4'>{'Can not contain two underscores in a row.'}<br/></span>);
|
||||
}
|
||||
|
||||
// In case of error we don't detect
|
||||
if (error.length === 0) {
|
||||
error.push(<span key='errorlast'>{'Invalid URL'}<br/></span>);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
doSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const url = React.findDOMNode(this.refs.urlinput).value;
|
||||
const cleanedURL = Utils.cleanUpUrlable(url);
|
||||
if (cleanedURL !== url || url.length < 2 || url.indexOf('__') > -1) {
|
||||
this.setState({urlError: this.getURLError(url)});
|
||||
return;
|
||||
}
|
||||
this.setState({urlError: '', userEdit: false});
|
||||
this.props.onModalSubmit(url);
|
||||
}
|
||||
doCancel() {
|
||||
this.setState({urlError: '', userEdit: false});
|
||||
this.props.onModalDismissed();
|
||||
}
|
||||
render() {
|
||||
let urlClass = 'input-group input-group--limit';
|
||||
let urlError = null;
|
||||
let serverError = null;
|
||||
|
||||
if (this.state.urlError) {
|
||||
urlClass += ' has-error';
|
||||
urlError = (<p className='input__help error'>{this.state.urlError}</p>);
|
||||
}
|
||||
|
||||
if (this.props.serverError) {
|
||||
serverError = <div className='form-group has-error'><p className='input__help error'>{this.props.serverError}</p></div>;
|
||||
}
|
||||
|
||||
const fullTeamUrl = Utils.getTeamURLFromAddressBar();
|
||||
const teamURL = Utils.getShortenedTeamURL();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
show={this.props.show}
|
||||
onHide={this.doCancel}
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title>{this.props.title}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<form
|
||||
role='form'
|
||||
className='form-horizontal'
|
||||
>
|
||||
<Modal.Body>
|
||||
<div className='modal-intro'>{this.props.description}</div>
|
||||
<div className='form-group'>
|
||||
<label className='col-sm-2 form__label control-label'>{this.props.urlLabel}</label>
|
||||
<div className='col-sm-10'>
|
||||
<div className={urlClass}>
|
||||
<span
|
||||
data-toggle='tooltip'
|
||||
title={fullTeamUrl}
|
||||
className='input-group-addon'
|
||||
>
|
||||
{teamURL}
|
||||
</span>
|
||||
<input
|
||||
type='text'
|
||||
ref='urlinput'
|
||||
className='form-control'
|
||||
maxLength='22'
|
||||
onChange={this.onURLChanged}
|
||||
value={this.state.currentURL}
|
||||
autoFocus={true}
|
||||
tabIndex='1'
|
||||
/>
|
||||
</div>
|
||||
{urlError}
|
||||
{serverError}
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-default'
|
||||
onClick={this.doCancel}
|
||||
>
|
||||
{'Close'}
|
||||
</button>
|
||||
<button
|
||||
onClick={this.doSubmit}
|
||||
type='submit'
|
||||
className='btn btn-primary'
|
||||
tabIndex='2'
|
||||
>
|
||||
{this.props.submitButtonText}
|
||||
</button>
|
||||
</Modal.Footer>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ChangeUrlModal.defaultProps = {
|
||||
show: false,
|
||||
title: 'Change URL',
|
||||
desciption: '',
|
||||
urlLabel: 'URL',
|
||||
submitButtonText: 'Submit',
|
||||
currentURL: '',
|
||||
serverError: ''
|
||||
};
|
||||
|
||||
ChangeUrlModal.propTypes = {
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
title: React.PropTypes.string,
|
||||
description: React.PropTypes.string,
|
||||
urlLabel: React.PropTypes.string,
|
||||
submitButtonText: React.PropTypes.string,
|
||||
currentURL: React.PropTypes.string,
|
||||
serverError: React.PropTypes.string,
|
||||
onModalSubmit: React.PropTypes.func.isRequired,
|
||||
onModalDismissed: React.PropTypes.func.isRequired
|
||||
};
|
||||
@@ -53,7 +53,7 @@ export default class FileUpload extends React.Component {
|
||||
}
|
||||
|
||||
// generate a unique id that can be used by other components to refer back to this upload
|
||||
var clientId = utils.generateId();
|
||||
let clientId = utils.generateId();
|
||||
|
||||
// prepare data to be uploaded
|
||||
var formData = new FormData();
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var utils = require('../utils/utils.jsx');
|
||||
var client = require('../utils/client.jsx');
|
||||
var asyncClient = require('../utils/async_client.jsx');
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
|
||||
export default class NewChannelModal extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.displayNameKeyUp = this.displayNameKeyUp.bind(this);
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
|
||||
this.state = {channelType: ''};
|
||||
}
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var channel = {};
|
||||
var state = {serverError: ''};
|
||||
|
||||
channel.display_name = React.findDOMNode(this.refs.display_name).value.trim();
|
||||
if (!channel.display_name) {
|
||||
state.displayNameError = 'This field is required';
|
||||
state.inValid = true;
|
||||
} else if (channel.display_name.length > 22) {
|
||||
state.displayNameError = 'This field must be less than 22 characters';
|
||||
state.inValid = true;
|
||||
} else {
|
||||
state.displayNameError = '';
|
||||
}
|
||||
|
||||
channel.name = React.findDOMNode(this.refs.channel_name).value.trim();
|
||||
if (!channel.name) {
|
||||
state.nameError = 'This field is required';
|
||||
state.inValid = true;
|
||||
} else if (channel.name.length > 22) {
|
||||
state.nameError = 'This field must be less than 22 characters';
|
||||
state.inValid = true;
|
||||
} else {
|
||||
var cleanedName = utils.cleanUpUrlable(channel.name);
|
||||
if (cleanedName !== channel.name) {
|
||||
state.nameError = "Must be lowercase alphanumeric characters, allowing '-' but not starting or ending with '-'";
|
||||
state.inValid = true;
|
||||
} else {
|
||||
state.nameError = '';
|
||||
}
|
||||
}
|
||||
|
||||
this.setState(state);
|
||||
|
||||
if (state.inValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cu = UserStore.getCurrentUser();
|
||||
channel.team_id = cu.team_id;
|
||||
|
||||
channel.description = React.findDOMNode(this.refs.channel_desc).value.trim();
|
||||
channel.type = this.state.channelType;
|
||||
|
||||
client.createChannel(channel,
|
||||
function success(data) {
|
||||
$(React.findDOMNode(this.refs.modal)).modal('hide');
|
||||
|
||||
asyncClient.getChannel(data.id);
|
||||
utils.switchChannel(data);
|
||||
|
||||
React.findDOMNode(this.refs.display_name).value = '';
|
||||
React.findDOMNode(this.refs.channel_name).value = '';
|
||||
React.findDOMNode(this.refs.channel_desc).value = '';
|
||||
}.bind(this),
|
||||
function error(err) {
|
||||
state.serverError = err.message;
|
||||
state.inValid = true;
|
||||
this.setState(state);
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
displayNameKeyUp() {
|
||||
var displayName = React.findDOMNode(this.refs.display_name).value.trim();
|
||||
var channelName = utils.cleanUpUrlable(displayName);
|
||||
React.findDOMNode(this.refs.channel_name).value = channelName;
|
||||
}
|
||||
componentDidMount() {
|
||||
var self = this;
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function onModalShow(e) {
|
||||
var button = e.relatedTarget;
|
||||
self.setState({channelType: $(button).attr('data-channeltype')});
|
||||
});
|
||||
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
$(React.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
handleClose() {
|
||||
$(React.findDOMNode(this)).find('.form-control').each(function clearForms() {
|
||||
this.value = '';
|
||||
});
|
||||
|
||||
this.setState({channelType: '', displayNameError: '', nameError: '', serverError: '', inValid: false});
|
||||
}
|
||||
render() {
|
||||
var displayNameError = null;
|
||||
var nameError = null;
|
||||
var serverError = null;
|
||||
var displayNameClass = 'form-group';
|
||||
var nameClass = 'form-group';
|
||||
|
||||
if (this.state.displayNameError) {
|
||||
displayNameError = <label className='control-label'>{this.state.displayNameError}</label>;
|
||||
displayNameClass += ' has-error';
|
||||
}
|
||||
if (this.state.nameError) {
|
||||
nameError = <label className='control-label'>{this.state.nameError}</label>;
|
||||
nameClass += ' has-error';
|
||||
}
|
||||
if (this.state.serverError) {
|
||||
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
|
||||
}
|
||||
|
||||
var channelTerm = 'Channel';
|
||||
if (this.state.channelType === 'P') {
|
||||
channelTerm = 'Group';
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='modal fade'
|
||||
id='new_channel'
|
||||
ref='modal'
|
||||
tabIndex='-1'
|
||||
role='dialog'
|
||||
aria-hidden='true'
|
||||
>
|
||||
<div className='modal-dialog'>
|
||||
<div className='modal-content'>
|
||||
<div className='modal-header'>
|
||||
<button
|
||||
type='button'
|
||||
className='close'
|
||||
data-dismiss='modal'
|
||||
>
|
||||
<span aria-hidden='true'>×</span>
|
||||
<span className='sr-only'>Cancel</span>
|
||||
</button>
|
||||
<h4 className='modal-title'>New {channelTerm}</h4>
|
||||
</div>
|
||||
<form role='form'>
|
||||
<div className='modal-body'>
|
||||
<div className={displayNameClass}>
|
||||
<label className='control-label'>Display Name</label>
|
||||
<input
|
||||
onKeyUp={this.displayNameKeyUp}
|
||||
type='text'
|
||||
ref='display_name'
|
||||
className='form-control'
|
||||
placeholder='Enter display name'
|
||||
maxLength='22'
|
||||
/>
|
||||
{displayNameError}
|
||||
</div>
|
||||
<div className={nameClass}>
|
||||
<label className='control-label'>Handle</label>
|
||||
<input
|
||||
type='text'
|
||||
className='form-control'
|
||||
ref='channel_name'
|
||||
placeholder="lowercase alphanumeric's only"
|
||||
maxLength='22'
|
||||
/>
|
||||
{nameError}
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label className='control-label'>Description</label>
|
||||
<textarea
|
||||
className='form-control no-resize'
|
||||
ref='channel_desc'
|
||||
rows='3'
|
||||
placeholder='Description'
|
||||
maxLength='1024'
|
||||
/>
|
||||
</div>
|
||||
{serverError}
|
||||
</div>
|
||||
<div className='modal-footer'>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-default'
|
||||
data-dismiss='modal'
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={this.handleSubmit}
|
||||
type='submit'
|
||||
className='btn btn-primary'
|
||||
>
|
||||
Create New {channelTerm}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
206
web/react/components/new_channel_flow.jsx
Обычный файл
206
web/react/components/new_channel_flow.jsx
Обычный файл
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var Utils = require('../utils/utils.jsx');
|
||||
var AsyncClient = require('../utils/async_client.jsx');
|
||||
var Client = require('../utils/client.jsx');
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
|
||||
var NewChannelModal = require('./new_channel_modal.jsx');
|
||||
var ChangeURLModal = require('./change_url_modal.jsx');
|
||||
|
||||
const SHOW_NEW_CHANNEL = 1;
|
||||
const SHOW_EDIT_URL = 2;
|
||||
const SHOW_EDIT_URL_THEN_COMPLETE = 3;
|
||||
|
||||
export default class NewChannelFlow extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.doSubmit = this.doSubmit.bind(this);
|
||||
this.typeSwitched = this.typeSwitched.bind(this);
|
||||
this.urlChangeRequested = this.urlChangeRequested.bind(this);
|
||||
this.urlChangeSubmitted = this.urlChangeSubmitted.bind(this);
|
||||
this.urlChangeDismissed = this.urlChangeDismissed.bind(this);
|
||||
this.channelDataChanged = this.channelDataChanged.bind(this);
|
||||
|
||||
this.state = {
|
||||
serverError: '',
|
||||
channelType: 'O',
|
||||
flowState: SHOW_NEW_CHANNEL,
|
||||
channelDisplayName: '',
|
||||
channelName: '',
|
||||
channelDescription: '',
|
||||
nameModified: false
|
||||
};
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// If we are being shown, grab channel type from props and clear
|
||||
if (nextProps.show === true && this.props.show === false) {
|
||||
this.setState({
|
||||
serverError: '',
|
||||
channelType: nextProps.channelType,
|
||||
flowState: SHOW_NEW_CHANNEL,
|
||||
channelDisplayName: '',
|
||||
channelName: '',
|
||||
channelDescription: '',
|
||||
nameModified: false
|
||||
});
|
||||
}
|
||||
}
|
||||
doSubmit() {
|
||||
var channel = {};
|
||||
|
||||
channel.display_name = this.state.channelDisplayName;
|
||||
if (!channel.display_name) {
|
||||
this.setState({serverError: 'Invalid Channel Name'});
|
||||
return;
|
||||
}
|
||||
|
||||
channel.name = this.state.channelName;
|
||||
if (channel.name.length < 2) {
|
||||
this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
|
||||
return;
|
||||
}
|
||||
|
||||
const cu = UserStore.getCurrentUser();
|
||||
channel.team_id = cu.team_id;
|
||||
channel.description = this.state.channelDescription;
|
||||
channel.type = this.state.channelType;
|
||||
|
||||
Client.createChannel(channel,
|
||||
(data) => {
|
||||
this.props.onModalDismissed();
|
||||
AsyncClient.getChannel(data.id);
|
||||
Utils.switchChannel(data);
|
||||
},
|
||||
(err) => {
|
||||
if (err.message === 'Name must be 2 or more lowercase alphanumeric characters') {
|
||||
this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
|
||||
}
|
||||
if (err.message === 'A channel with that handle already exists') {
|
||||
this.setState({serverError: 'A channel with that URL already exists'});
|
||||
return;
|
||||
}
|
||||
this.setState({serverError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
typeSwitched() {
|
||||
if (this.state.channelType === 'P') {
|
||||
this.setState({channelType: 'O'});
|
||||
} else {
|
||||
this.setState({channelType: 'P'});
|
||||
}
|
||||
}
|
||||
urlChangeRequested() {
|
||||
this.setState({flowState: SHOW_EDIT_URL});
|
||||
}
|
||||
urlChangeSubmitted(newURL) {
|
||||
if (this.state.flowState === SHOW_EDIT_URL_THEN_COMPLETE) {
|
||||
this.setState({channelName: newURL, nameModified: true}, this.doSubmit);
|
||||
} else {
|
||||
this.setState({flowState: SHOW_NEW_CHANNEL, serverError: '', channelName: newURL, nameModified: true});
|
||||
}
|
||||
}
|
||||
urlChangeDismissed() {
|
||||
this.setState({flowState: SHOW_NEW_CHANNEL});
|
||||
}
|
||||
channelDataChanged(data) {
|
||||
this.setState({
|
||||
channelDisplayName: data.displayName,
|
||||
channelDescription: data.description
|
||||
});
|
||||
if (!this.state.nameModified) {
|
||||
this.setState({channelName: Utils.cleanUpUrlable(data.displayName.trim())});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const channelData = {
|
||||
name: this.state.channelName,
|
||||
displayName: this.state.channelDisplayName,
|
||||
description: this.state.channelDescription
|
||||
};
|
||||
|
||||
let showChannelModal = false;
|
||||
let showGroupModal = false;
|
||||
let showChangeURLModal = false;
|
||||
|
||||
let changeURLTitle = '';
|
||||
let changeURLSubmitButtonText = '';
|
||||
let channelTerm = '';
|
||||
|
||||
// Only listen to flow state if we are being shown
|
||||
if (this.props.show) {
|
||||
switch (this.state.flowState) {
|
||||
case SHOW_NEW_CHANNEL:
|
||||
if (this.state.channelType === 'O') {
|
||||
showChannelModal = true;
|
||||
channelTerm = 'Channel';
|
||||
} else {
|
||||
showGroupModal = true;
|
||||
channelTerm = 'Group';
|
||||
}
|
||||
break;
|
||||
case SHOW_EDIT_URL:
|
||||
showChangeURLModal = true;
|
||||
changeURLTitle = 'Change ' + channelTerm + ' URL';
|
||||
changeURLSubmitButtonText = 'Change ' + channelTerm + ' URL';
|
||||
break;
|
||||
case SHOW_EDIT_URL_THEN_COMPLETE:
|
||||
showChangeURLModal = true;
|
||||
changeURLTitle = 'Set ' + channelTerm + ' URL';
|
||||
changeURLSubmitButtonText = 'Create ' + channelTerm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
<NewChannelModal
|
||||
show={showChannelModal}
|
||||
channelType={'O'}
|
||||
channelData={channelData}
|
||||
serverError={this.state.serverError}
|
||||
onSubmitChannel={this.doSubmit}
|
||||
onModalDismissed={this.props.onModalDismissed}
|
||||
onTypeSwitched={this.typeSwitched}
|
||||
onChangeURLPressed={this.urlChangeRequested}
|
||||
onDataChanged={this.channelDataChanged}
|
||||
/>
|
||||
<NewChannelModal
|
||||
show={showGroupModal}
|
||||
channelType={'P'}
|
||||
channelData={channelData}
|
||||
serverError={this.state.serverError}
|
||||
onSubmitChannel={this.doSubmit}
|
||||
onModalDismissed={this.props.onModalDismissed}
|
||||
onTypeSwitched={this.typeSwitched}
|
||||
onChangeURLPressed={this.urlChangeRequested}
|
||||
onDataChanged={this.channelDataChanged}
|
||||
/>
|
||||
<ChangeURLModal
|
||||
show={showChangeURLModal}
|
||||
title={changeURLTitle}
|
||||
description={'Some characters are not allowed in URLs and may be removed.'}
|
||||
urlLabel={channelTerm + ' URL'}
|
||||
submitButtonText={changeURLSubmitButtonText}
|
||||
currentURL={this.state.channelName}
|
||||
serverError={this.state.serverError}
|
||||
onModalSubmit={this.urlChangeSubmitted}
|
||||
onModalDismissed={this.urlChangeDismissed}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NewChannelFlow.defaultProps = {
|
||||
show: false,
|
||||
channelType: 'O'
|
||||
};
|
||||
|
||||
NewChannelFlow.propTypes = {
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
channelType: React.PropTypes.string.isRequired,
|
||||
onModalDismissed: React.PropTypes.func.isRequired
|
||||
};
|
||||
198
web/react/components/new_channel_modal.jsx
Обычный файл
198
web/react/components/new_channel_modal.jsx
Обычный файл
@@ -0,0 +1,198 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
const Utils = require('../utils/utils.jsx');
|
||||
var Modal = ReactBootstrap.Modal;
|
||||
|
||||
export default class NewChannelModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
displayNameError: ''
|
||||
};
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.show === true && this.props.show === false) {
|
||||
this.setState({
|
||||
displayNameError: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const displayName = React.findDOMNode(this.refs.display_name).value.trim();
|
||||
if (displayName.length < 1) {
|
||||
this.setState({displayNameError: 'This field is required'});
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onSubmitChannel();
|
||||
}
|
||||
handleChange() {
|
||||
const newData = {
|
||||
displayName: React.findDOMNode(this.refs.display_name).value,
|
||||
description: React.findDOMNode(this.refs.channel_desc).value
|
||||
};
|
||||
this.props.onDataChanged(newData);
|
||||
}
|
||||
render() {
|
||||
var displayNameError = null;
|
||||
var serverError = null;
|
||||
var displayNameClass = 'form-group';
|
||||
|
||||
if (this.state.displayNameError) {
|
||||
displayNameError = <p className='input__help error'>{this.state.displayNameError}</p>;
|
||||
displayNameClass += ' has-error';
|
||||
}
|
||||
|
||||
if (this.props.serverError) {
|
||||
serverError = <div className='form-group has-error'><p className='input__help error'>{this.props.serverError}</p></div>;
|
||||
}
|
||||
|
||||
var channelTerm = '';
|
||||
var channelSwitchText = '';
|
||||
switch (this.props.channelType) {
|
||||
case 'P':
|
||||
channelTerm = 'Group';
|
||||
channelSwitchText = (
|
||||
<div className='modal-intro'>
|
||||
{'Create a new private group with restricted membership. '}
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.props.onTypeSwitched}
|
||||
>
|
||||
{'Create a public channel'}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'O':
|
||||
channelTerm = 'Channel';
|
||||
channelSwitchText = (
|
||||
<div className='modal-intro'>
|
||||
{'Create a new public channel anyone can join. '}
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.props.onTypeSwitched}
|
||||
>
|
||||
{'Create a private group'}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
const prettyTeamURL = Utils.getShortenedTeamURL();
|
||||
|
||||
return (
|
||||
<span>
|
||||
<Modal
|
||||
show={this.props.show}
|
||||
onHide={this.props.onModalDismissed}
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title>{'New ' + channelTerm}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<form
|
||||
role='form'
|
||||
className='form-horizontal'
|
||||
>
|
||||
<Modal.Body>
|
||||
<div>
|
||||
{channelSwitchText}
|
||||
</div>
|
||||
<div className={displayNameClass}>
|
||||
<label className='col-sm-2 form__label control-label'>{'Name'}</label>
|
||||
<div className='col-sm-10'>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
type='text'
|
||||
ref='display_name'
|
||||
className='form-control'
|
||||
placeholder='Ex: "Bugs", "Marketing", "办公室恋情"'
|
||||
maxLength='22'
|
||||
value={this.props.channelData.displayName}
|
||||
autoFocus={true}
|
||||
tabIndex='1'
|
||||
/>
|
||||
{displayNameError}
|
||||
<p className='input__help'>
|
||||
{'Channel URL: ' + prettyTeamURL + this.props.channelData.name + ' ('}
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.props.onChangeURLPressed}
|
||||
>
|
||||
{'change this URL'}
|
||||
</a>
|
||||
{')'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='form-group less'>
|
||||
<div className='col-sm-2'>
|
||||
<label className='form__label control-label'>{'Description'}</label>
|
||||
<label className='form__label light'>{'(optional)'}</label>
|
||||
</div>
|
||||
<div className='col-sm-10'>
|
||||
<textarea
|
||||
className='form-control no-resize'
|
||||
ref='channel_desc'
|
||||
rows='4'
|
||||
placeholder='Description'
|
||||
maxLength='1024'
|
||||
value={this.props.channelData.description}
|
||||
onChange={this.handleChange}
|
||||
tabIndex='2'
|
||||
/>
|
||||
<p className='input__help'>
|
||||
{'The purpose of your channel. To help others decide whether to join.'}
|
||||
</p>
|
||||
{serverError}
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-default'
|
||||
onClick={this.props.onModalDismissed}
|
||||
>
|
||||
{'Cancel'}
|
||||
</button>
|
||||
<button
|
||||
onClick={this.handleSubmit}
|
||||
type='submit'
|
||||
className='btn btn-primary'
|
||||
tabIndex='3'
|
||||
>
|
||||
{'Create New ' + channelTerm}
|
||||
</button>
|
||||
</Modal.Footer>
|
||||
</form>
|
||||
</Modal>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NewChannelModal.defaultProps = {
|
||||
show: false,
|
||||
channelType: 'O',
|
||||
serverError: ''
|
||||
};
|
||||
NewChannelModal.propTypes = {
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
channelType: React.PropTypes.string.isRequired,
|
||||
channelData: React.PropTypes.object.isRequired,
|
||||
serverError: React.PropTypes.string,
|
||||
onSubmitChannel: React.PropTypes.func.isRequired,
|
||||
onModalDismissed: React.PropTypes.func.isRequired,
|
||||
onTypeSwitched: React.PropTypes.func.isRequired,
|
||||
onChangeURLPressed: React.PropTypes.func.isRequired,
|
||||
onDataChanged: React.PropTypes.func.isRequired
|
||||
};
|
||||
@@ -12,6 +12,7 @@ var Utils = require('../utils/utils.jsx');
|
||||
var SidebarHeader = require('./sidebar_header.jsx');
|
||||
var SearchBox = require('./search_bar.jsx');
|
||||
var Constants = require('../utils/constants.jsx');
|
||||
var NewChannelFlow = require('./new_channel_flow.jsx');
|
||||
|
||||
export default class Sidebar extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -28,6 +29,7 @@ export default class Sidebar extends React.Component {
|
||||
this.createChannelElement = this.createChannelElement.bind(this);
|
||||
|
||||
this.state = this.getStateFromStores();
|
||||
this.state.modal = '';
|
||||
this.state.loadingDMChannel = -1;
|
||||
}
|
||||
getStateFromStores() {
|
||||
@@ -473,8 +475,18 @@ export default class Sidebar extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let showChannelModal = false;
|
||||
if (this.state.modal !== '') {
|
||||
showChannelModal = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<NewChannelFlow
|
||||
show={showChannelModal}
|
||||
channelType={this.state.modal}
|
||||
onModalDismissed={() => this.setState({modal: ''})}
|
||||
/>
|
||||
<SidebarHeader
|
||||
teamDisplayName={this.props.teamDisplayName}
|
||||
teamType={this.props.teamType}
|
||||
@@ -508,11 +520,9 @@ export default class Sidebar extends React.Component {
|
||||
<a
|
||||
className='add-channel-btn'
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#new_channel'
|
||||
data-channeltype='O'
|
||||
onClick={() => this.setState({modal: 'O'})}
|
||||
>
|
||||
+
|
||||
{'+'}
|
||||
</a>
|
||||
</h4>
|
||||
</li>
|
||||
@@ -537,11 +547,9 @@ export default class Sidebar extends React.Component {
|
||||
<a
|
||||
className='add-channel-btn'
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#new_channel'
|
||||
data-channeltype='P'
|
||||
onClick={() => this.setState({modal: 'P'})}
|
||||
>
|
||||
+
|
||||
{'+'}
|
||||
</a>
|
||||
</h4>
|
||||
</li>
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
"flux": "2.1.1",
|
||||
"keymirror": "0.1.1",
|
||||
"object-assign": "3.0.0",
|
||||
"react": "0.13.3",
|
||||
"react-zeroclipboard-mixin": "0.1.0",
|
||||
"twemoji": "1.4.1"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,6 @@ var RenameChannelModal = require('../components/rename_channel_modal.jsx');
|
||||
var EditPostModal = require('../components/edit_post_modal.jsx');
|
||||
var DeletePostModal = require('../components/delete_post_modal.jsx');
|
||||
var MoreChannelsModal = require('../components/more_channels.jsx');
|
||||
var NewChannelModal = require('../components/new_channel.jsx');
|
||||
var PostDeletedModal = require('../components/post_deleted_modal.jsx');
|
||||
var ChannelNotificationsModal = require('../components/channel_notifications.jsx');
|
||||
var UserSettingsModal = require('../components/user_settings_modal.jsx');
|
||||
@@ -153,11 +152,6 @@ function setupChannelPage(teamName, teamType, teamId, channelName, channelId) {
|
||||
document.getElementById('direct_channel_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
<NewChannelModal />,
|
||||
document.getElementById('new_channel_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
<PostListContainer />,
|
||||
document.getElementById('post-list')
|
||||
|
||||
@@ -1127,3 +1127,14 @@ export function importSlack(file, success, error) {
|
||||
|
||||
client.importSlack(formData, success, error);
|
||||
}
|
||||
|
||||
export function getTeamURLFromAddressBar() {
|
||||
return window.location.href.split('/channels')[0];
|
||||
}
|
||||
|
||||
export function getShortenedTeamURL() {
|
||||
const teamURL = getTeamURLFromAddressBar();
|
||||
if (teamURL.length > 24) {
|
||||
return teamURL.substring(0, 10) + '...' + teamURL.substring(teamURL.length - 12, teamURL.length - 1) + '/';
|
||||
}
|
||||
}
|
||||
|
||||
33
web/sass-files/sass/partials/_forms.scss
Обычный файл
33
web/sass-files/sass/partials/_forms.scss
Обычный файл
@@ -0,0 +1,33 @@
|
||||
.form-horizontal {
|
||||
.modal-intro {
|
||||
margin: -10px 0 30px;
|
||||
}
|
||||
.form__label {
|
||||
text-align: left;
|
||||
padding-right: 3px;
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
&.light {
|
||||
color: #999;
|
||||
font-size: 1.05em;
|
||||
font-style: italic;
|
||||
padding-top: 2px;
|
||||
}
|
||||
}
|
||||
.input__help {
|
||||
color: #777;
|
||||
margin: 10px 0 0 10px;
|
||||
&.error {
|
||||
color: #a94442;
|
||||
}
|
||||
}
|
||||
.form-control {
|
||||
font-weight: normal;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 25px;
|
||||
&.less {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
@import "partials/videos";
|
||||
@import "partials/settings";
|
||||
@import "partials/modal";
|
||||
@import "partials/forms";
|
||||
@import "partials/mentions";
|
||||
@import "partials/command-box";
|
||||
@import "partials/error";
|
||||
|
||||
5
web/static/css/bootstrap-3.3.1.min.css
поставляемый
5
web/static/css/bootstrap-3.3.1.min.css
поставляемый
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
5
web/static/css/bootstrap-3.3.5.min.css
поставляемый
Обычный файл
5
web/static/css/bootstrap-3.3.5.min.css
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
7
web/static/js/bootstrap-3.3.1.min.js
поставляемый
7
web/static/js/bootstrap-3.3.1.min.js
поставляемый
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
471
web/static/js/bootstrap-3.3.1.js → web/static/js/bootstrap-3.3.5.js
поставляемый
471
web/static/js/bootstrap-3.3.1.js → web/static/js/bootstrap-3.3.5.js
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
7
web/static/js/bootstrap-3.3.5.min.js
поставляемый
Обычный файл
7
web/static/js/bootstrap-3.3.5.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
1024
web/static/js/jasny-bootstrap.js
Обычный файл
1024
web/static/js/jasny-bootstrap.js
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
6
web/static/js/jasny-bootstrap.min.js
поставляемый
Обычный файл
6
web/static/js/jasny-bootstrap.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
15678
web/static/js/react-bootstrap-0.25.1.js
Обычный файл
15678
web/static/js/react-bootstrap-0.25.1.js
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
14
web/static/js/react-bootstrap-0.25.1.min.js
поставляемый
Обычный файл
14
web/static/js/react-bootstrap-0.25.1.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
16
web/static/js/react-with-addons-0.13.1.min.js
поставляемый
16
web/static/js/react-with-addons-0.13.1.min.js
поставляемый
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* React (with addons) v0.13.1
|
||||
* React (with addons) v0.13.3
|
||||
*/
|
||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.React = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
|
||||
/**
|
||||
@@ -710,7 +710,9 @@ var isUnitlessNumber = {
|
||||
columnCount: true,
|
||||
flex: true,
|
||||
flexGrow: true,
|
||||
flexPositive: true,
|
||||
flexShrink: true,
|
||||
flexNegative: true,
|
||||
fontWeight: true,
|
||||
lineClamp: true,
|
||||
lineHeight: true,
|
||||
@@ -723,7 +725,9 @@ var isUnitlessNumber = {
|
||||
|
||||
// SVG-related properties
|
||||
fillOpacity: true,
|
||||
strokeOpacity: true
|
||||
strokeDashoffset: true,
|
||||
strokeOpacity: true,
|
||||
strokeWidth: true
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3788,6 +3792,7 @@ var HTMLDOMPropertyConfig = {
|
||||
headers: null,
|
||||
height: MUST_USE_ATTRIBUTE,
|
||||
hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
||||
high: null,
|
||||
href: null,
|
||||
hrefLang: null,
|
||||
htmlFor: null,
|
||||
@@ -3798,6 +3803,7 @@ var HTMLDOMPropertyConfig = {
|
||||
lang: null,
|
||||
list: MUST_USE_ATTRIBUTE,
|
||||
loop: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
||||
low: null,
|
||||
manifest: MUST_USE_ATTRIBUTE,
|
||||
marginHeight: null,
|
||||
marginWidth: null,
|
||||
@@ -3812,6 +3818,7 @@ var HTMLDOMPropertyConfig = {
|
||||
name: null,
|
||||
noValidate: HAS_BOOLEAN_VALUE,
|
||||
open: HAS_BOOLEAN_VALUE,
|
||||
optimum: null,
|
||||
pattern: null,
|
||||
placeholder: null,
|
||||
poster: null,
|
||||
@@ -3825,6 +3832,7 @@ var HTMLDOMPropertyConfig = {
|
||||
rowSpan: null,
|
||||
sandbox: null,
|
||||
scope: null,
|
||||
scoped: HAS_BOOLEAN_VALUE,
|
||||
scrolling: null,
|
||||
seamless: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
||||
selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
||||
@@ -3866,7 +3874,9 @@ var HTMLDOMPropertyConfig = {
|
||||
itemID: MUST_USE_ATTRIBUTE,
|
||||
itemRef: MUST_USE_ATTRIBUTE,
|
||||
// property is supported for OpenGraph in meta tags.
|
||||
property: null
|
||||
property: null,
|
||||
// IE-only attribute that controls focus behavior
|
||||
unselectable: MUST_USE_ATTRIBUTE
|
||||
},
|
||||
DOMAttributeNames: {
|
||||
acceptCharset: 'accept-charset',
|
||||
@@ -4475,7 +4485,7 @@ if ("production" !== "development") {
|
||||
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
|
||||
console.debug(
|
||||
'Download the React DevTools for a better development experience: ' +
|
||||
'http://fb.me/react-devtools'
|
||||
'https://fb.me/react-devtools'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4502,7 +4512,7 @@ if ("production" !== "development") {
|
||||
if (!expectedFeatures[i]) {
|
||||
console.error(
|
||||
'One or more ES5 shim/shams expected by React are not available: ' +
|
||||
'http://fb.me/react-warning-polyfills'
|
||||
'https://fb.me/react-warning-polyfills'
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -4510,7 +4520,7 @@ if ("production" !== "development") {
|
||||
}
|
||||
}
|
||||
|
||||
React.version = '0.13.1';
|
||||
React.version = '0.13.3';
|
||||
|
||||
module.exports = React;
|
||||
|
||||
@@ -6229,7 +6239,7 @@ var ReactClass = {
|
||||
("production" !== "development" ? warning(
|
||||
this instanceof Constructor,
|
||||
'Something is calling a React component directly. Use a factory or ' +
|
||||
'JSX instead. See: http://fb.me/react-legacyfactory'
|
||||
'JSX instead. See: https://fb.me/react-legacyfactory'
|
||||
) : null);
|
||||
}
|
||||
|
||||
@@ -6439,20 +6449,38 @@ ReactComponent.prototype.forceUpdate = function(callback) {
|
||||
*/
|
||||
if ("production" !== "development") {
|
||||
var deprecatedAPIs = {
|
||||
getDOMNode: 'getDOMNode',
|
||||
isMounted: 'isMounted',
|
||||
replaceProps: 'replaceProps',
|
||||
replaceState: 'replaceState',
|
||||
setProps: 'setProps'
|
||||
getDOMNode: [
|
||||
'getDOMNode',
|
||||
'Use React.findDOMNode(component) instead.'
|
||||
],
|
||||
isMounted: [
|
||||
'isMounted',
|
||||
'Instead, make sure to clean up subscriptions and pending requests in ' +
|
||||
'componentWillUnmount to prevent memory leaks.'
|
||||
],
|
||||
replaceProps: [
|
||||
'replaceProps',
|
||||
'Instead call React.render again at the top level.'
|
||||
],
|
||||
replaceState: [
|
||||
'replaceState',
|
||||
'Refactor your code to use setState instead (see ' +
|
||||
'https://github.com/facebook/react/issues/3236).'
|
||||
],
|
||||
setProps: [
|
||||
'setProps',
|
||||
'Instead call React.render again at the top level.'
|
||||
]
|
||||
};
|
||||
var defineDeprecationWarning = function(methodName, displayName) {
|
||||
var defineDeprecationWarning = function(methodName, info) {
|
||||
try {
|
||||
Object.defineProperty(ReactComponent.prototype, methodName, {
|
||||
get: function() {
|
||||
("production" !== "development" ? warning(
|
||||
false,
|
||||
'%s(...) is deprecated in plain JavaScript React classes.',
|
||||
displayName
|
||||
'%s(...) is deprecated in plain JavaScript React classes. %s',
|
||||
info[0],
|
||||
info[1]
|
||||
) : null);
|
||||
return undefined;
|
||||
}
|
||||
@@ -6802,6 +6830,14 @@ var ReactCompositeComponentMixin = {
|
||||
'Did you mean to define a state property instead?',
|
||||
this.getName() || 'a component'
|
||||
) : null);
|
||||
("production" !== "development" ? warning(
|
||||
!inst.getDefaultProps ||
|
||||
inst.getDefaultProps.isReactClassApproved,
|
||||
'getDefaultProps was defined on %s, a plain JavaScript class. ' +
|
||||
'This is only supported for classes created using React.createClass. ' +
|
||||
'Use a static property to define defaultProps instead.',
|
||||
this.getName() || 'a component'
|
||||
) : null);
|
||||
("production" !== "development" ? warning(
|
||||
!inst.propTypes,
|
||||
'propTypes was defined as an instance property on %s. Use a static ' +
|
||||
@@ -6838,6 +6874,7 @@ var ReactCompositeComponentMixin = {
|
||||
this._pendingReplaceState = false;
|
||||
this._pendingForceUpdate = false;
|
||||
|
||||
var childContext;
|
||||
var renderedElement;
|
||||
|
||||
var previouslyMounting = ReactLifeCycle.currentlyMountingInstance;
|
||||
@@ -6852,7 +6889,8 @@ var ReactCompositeComponentMixin = {
|
||||
}
|
||||
}
|
||||
|
||||
renderedElement = this._renderValidatedComponent();
|
||||
childContext = this._getValidatedChildContext(context);
|
||||
renderedElement = this._renderValidatedComponent(childContext);
|
||||
} finally {
|
||||
ReactLifeCycle.currentlyMountingInstance = previouslyMounting;
|
||||
}
|
||||
@@ -6866,7 +6904,7 @@ var ReactCompositeComponentMixin = {
|
||||
this._renderedComponent,
|
||||
rootID,
|
||||
transaction,
|
||||
this._processChildContext(context)
|
||||
this._mergeChildContext(context, childContext)
|
||||
);
|
||||
if (inst.componentDidMount) {
|
||||
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
|
||||
@@ -6996,7 +7034,7 @@ var ReactCompositeComponentMixin = {
|
||||
* @return {object}
|
||||
* @private
|
||||
*/
|
||||
_processChildContext: function(currentContext) {
|
||||
_getValidatedChildContext: function(currentContext) {
|
||||
var inst = this._instance;
|
||||
var childContext = inst.getChildContext && inst.getChildContext();
|
||||
if (childContext) {
|
||||
@@ -7021,6 +7059,13 @@ var ReactCompositeComponentMixin = {
|
||||
name
|
||||
) : invariant(name in inst.constructor.childContextTypes));
|
||||
}
|
||||
return childContext;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_mergeChildContext: function(currentContext, childContext) {
|
||||
if (childContext) {
|
||||
return assign({}, currentContext, childContext);
|
||||
}
|
||||
return currentContext;
|
||||
@@ -7280,6 +7325,10 @@ var ReactCompositeComponentMixin = {
|
||||
return inst.state;
|
||||
}
|
||||
|
||||
if (replace && queue.length === 1) {
|
||||
return queue[0];
|
||||
}
|
||||
|
||||
var nextState = assign({}, replace ? queue[0] : inst.state);
|
||||
for (var i = replace ? 1 : 0; i < queue.length; i++) {
|
||||
var partial = queue[i];
|
||||
@@ -7349,13 +7398,14 @@ var ReactCompositeComponentMixin = {
|
||||
_updateRenderedComponent: function(transaction, context) {
|
||||
var prevComponentInstance = this._renderedComponent;
|
||||
var prevRenderedElement = prevComponentInstance._currentElement;
|
||||
var nextRenderedElement = this._renderValidatedComponent();
|
||||
var childContext = this._getValidatedChildContext();
|
||||
var nextRenderedElement = this._renderValidatedComponent(childContext);
|
||||
if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
|
||||
ReactReconciler.receiveComponent(
|
||||
prevComponentInstance,
|
||||
nextRenderedElement,
|
||||
transaction,
|
||||
this._processChildContext(context)
|
||||
this._mergeChildContext(context, childContext)
|
||||
);
|
||||
} else {
|
||||
// These two IDs are actually the same! But nothing should rely on that.
|
||||
@@ -7371,7 +7421,7 @@ var ReactCompositeComponentMixin = {
|
||||
this._renderedComponent,
|
||||
thisID,
|
||||
transaction,
|
||||
context
|
||||
this._mergeChildContext(context, childContext)
|
||||
);
|
||||
this._replaceNodeWithMarkupByID(prevComponentID, nextMarkup);
|
||||
}
|
||||
@@ -7409,11 +7459,12 @@ var ReactCompositeComponentMixin = {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_renderValidatedComponent: function() {
|
||||
_renderValidatedComponent: function(childContext) {
|
||||
var renderedComponent;
|
||||
var previousContext = ReactContext.current;
|
||||
ReactContext.current = this._processChildContext(
|
||||
this._currentElement._context
|
||||
ReactContext.current = this._mergeChildContext(
|
||||
this._currentElement._context,
|
||||
childContext
|
||||
);
|
||||
ReactCurrentOwner.current = this;
|
||||
try {
|
||||
@@ -7778,6 +7829,7 @@ var ReactDOM = mapObject({
|
||||
|
||||
// SVG
|
||||
circle: 'circle',
|
||||
clipPath: 'clipPath',
|
||||
defs: 'defs',
|
||||
ellipse: 'ellipse',
|
||||
g: 'g',
|
||||
@@ -7927,11 +7979,13 @@ function assertValidProps(props) {
|
||||
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.'
|
||||
) : invariant(props.children == null));
|
||||
("production" !== "development" ? invariant(
|
||||
props.dangerouslySetInnerHTML.__html != null,
|
||||
typeof props.dangerouslySetInnerHTML === 'object' &&
|
||||
'__html' in props.dangerouslySetInnerHTML,
|
||||
'`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
|
||||
'Please visit http://fb.me/react-invariant-dangerously-set-inner-html ' +
|
||||
'Please visit https://fb.me/react-invariant-dangerously-set-inner-html ' +
|
||||
'for more information.'
|
||||
) : invariant(props.dangerouslySetInnerHTML.__html != null));
|
||||
) : invariant(typeof props.dangerouslySetInnerHTML === 'object' &&
|
||||
'__html' in props.dangerouslySetInnerHTML));
|
||||
}
|
||||
if ("production" !== "development") {
|
||||
("production" !== "development" ? warning(
|
||||
@@ -8239,6 +8293,8 @@ ReactDOMComponent.Mixin = {
|
||||
if (propKey === STYLE) {
|
||||
if (nextProp) {
|
||||
nextProp = this._previousStyleCopy = assign({}, nextProp);
|
||||
} else {
|
||||
this._previousStyleCopy = null;
|
||||
}
|
||||
if (lastProp) {
|
||||
// Unset styles on `lastProp` but not on `nextProp`.
|
||||
@@ -10721,7 +10777,7 @@ function warnAndMonitorForKeyUse(message, element, parentType) {
|
||||
|
||||
("production" !== "development" ? warning(
|
||||
false,
|
||||
message + '%s%s See http://fb.me/react-warning-keys for more information.',
|
||||
message + '%s%s See https://fb.me/react-warning-keys for more information.',
|
||||
parentOrOwnerAddendum,
|
||||
childOwnerAddendum
|
||||
) : null);
|
||||
@@ -10845,9 +10901,9 @@ function warnForPropsMutation(propName, element) {
|
||||
|
||||
("production" !== "development" ? warning(
|
||||
false,
|
||||
'Don\'t set .props.%s of the React component%s. ' +
|
||||
'Instead, specify the correct value when ' +
|
||||
'initially creating the element.%s',
|
||||
'Don\'t set .props.%s of the React component%s. Instead, specify the ' +
|
||||
'correct value when initially creating the element or use ' +
|
||||
'React.cloneElement to make a new element with updated props.%s',
|
||||
propName,
|
||||
elementInfo,
|
||||
ownerInfo
|
||||
@@ -15236,6 +15292,7 @@ var ReactUpdates = _dereq_(100);
|
||||
var SyntheticEvent = _dereq_(108);
|
||||
|
||||
var assign = _dereq_(29);
|
||||
var emptyObject = _dereq_(130);
|
||||
|
||||
var topLevelTypes = EventConstants.topLevelTypes;
|
||||
|
||||
@@ -15577,6 +15634,9 @@ assign(
|
||||
);
|
||||
|
||||
ReactShallowRenderer.prototype.render = function(element, context) {
|
||||
if (!context) {
|
||||
context = emptyObject;
|
||||
}
|
||||
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
|
||||
this._render(element, transaction, context);
|
||||
ReactUpdates.ReactReconcileTransaction.release(transaction);
|
||||
@@ -15717,7 +15777,7 @@ for (eventType in topLevelTypes) {
|
||||
|
||||
module.exports = ReactTestUtils;
|
||||
|
||||
},{"100":100,"108":108,"16":16,"18":18,"21":21,"29":29,"31":31,"33":33,"43":43,"63":63,"65":65,"72":72,"73":73,"77":77}],96:[function(_dereq_,module,exports){
|
||||
},{"100":100,"108":108,"130":130,"16":16,"18":18,"21":21,"29":29,"31":31,"33":33,"43":43,"63":63,"65":65,"72":72,"73":73,"77":77}],96:[function(_dereq_,module,exports){
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
@@ -16762,6 +16822,7 @@ var MUST_USE_ATTRIBUTE = DOMProperty.injection.MUST_USE_ATTRIBUTE;
|
||||
|
||||
var SVGDOMPropertyConfig = {
|
||||
Properties: {
|
||||
clipPath: MUST_USE_ATTRIBUTE,
|
||||
cx: MUST_USE_ATTRIBUTE,
|
||||
cy: MUST_USE_ATTRIBUTE,
|
||||
d: MUST_USE_ATTRIBUTE,
|
||||
@@ -16807,6 +16868,7 @@ var SVGDOMPropertyConfig = {
|
||||
y: MUST_USE_ATTRIBUTE
|
||||
},
|
||||
DOMAttributeNames: {
|
||||
clipPath: 'clip-path',
|
||||
fillOpacity: 'fill-opacity',
|
||||
fontFamily: 'font-family',
|
||||
fontSize: 'font-size',
|
||||
@@ -19713,6 +19775,7 @@ var shouldWrap = {
|
||||
// Force wrapping for SVG elements because if they get created inside a <div>,
|
||||
// they will be initialized in the wrong namespace (and will not display).
|
||||
'circle': true,
|
||||
'clipPath': true,
|
||||
'defs': true,
|
||||
'ellipse': true,
|
||||
'g': true,
|
||||
@@ -19755,6 +19818,7 @@ var markupWrap = {
|
||||
'th': trWrap,
|
||||
|
||||
'circle': svgWrap,
|
||||
'clipPath': svgWrap,
|
||||
'defs': svgWrap,
|
||||
'ellipse': svgWrap,
|
||||
'g': svgWrap,
|
||||
@@ -20100,6 +20164,7 @@ assign(
|
||||
function isInternalComponentType(type) {
|
||||
return (
|
||||
typeof type === 'function' &&
|
||||
typeof type.prototype !== 'undefined' &&
|
||||
typeof type.prototype.mountComponent === 'function' &&
|
||||
typeof type.prototype.receiveComponent === 'function'
|
||||
);
|
||||
@@ -21355,11 +21420,14 @@ module.exports = traverseAllChildren;
|
||||
* @providesModule update
|
||||
*/
|
||||
|
||||
/* global hasOwnProperty:true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var assign = _dereq_(29);
|
||||
var keyOf = _dereq_(157);
|
||||
var invariant = _dereq_(150);
|
||||
var hasOwnProperty = {}.hasOwnProperty;
|
||||
|
||||
function shallowCopy(x) {
|
||||
if (Array.isArray(x)) {
|
||||
@@ -21419,7 +21487,7 @@ function update(value, spec) {
|
||||
COMMAND_SET
|
||||
) : invariant(typeof spec === 'object'));
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_SET)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_SET)) {
|
||||
("production" !== "development" ? invariant(
|
||||
Object.keys(spec).length === 1,
|
||||
'Cannot have more than one key in an object with %s',
|
||||
@@ -21431,7 +21499,7 @@ function update(value, spec) {
|
||||
|
||||
var nextValue = shallowCopy(value);
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_MERGE)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_MERGE)) {
|
||||
var mergeObj = spec[COMMAND_MERGE];
|
||||
("production" !== "development" ? invariant(
|
||||
mergeObj && typeof mergeObj === 'object',
|
||||
@@ -21448,21 +21516,21 @@ function update(value, spec) {
|
||||
assign(nextValue, spec[COMMAND_MERGE]);
|
||||
}
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_PUSH)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_PUSH)) {
|
||||
invariantArrayCase(value, spec, COMMAND_PUSH);
|
||||
spec[COMMAND_PUSH].forEach(function(item) {
|
||||
nextValue.push(item);
|
||||
});
|
||||
}
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_UNSHIFT)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_UNSHIFT)) {
|
||||
invariantArrayCase(value, spec, COMMAND_UNSHIFT);
|
||||
spec[COMMAND_UNSHIFT].forEach(function(item) {
|
||||
nextValue.unshift(item);
|
||||
});
|
||||
}
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_SPLICE)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_SPLICE)) {
|
||||
("production" !== "development" ? invariant(
|
||||
Array.isArray(value),
|
||||
'Expected %s target to be an array; got %s',
|
||||
@@ -21488,7 +21556,7 @@ function update(value, spec) {
|
||||
});
|
||||
}
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_APPLY)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_APPLY)) {
|
||||
("production" !== "development" ? invariant(
|
||||
typeof spec[COMMAND_APPLY] === 'function',
|
||||
'update(): expected spec of %s to be a function; got %s.',
|
||||
18
web/static/js/react-with-addons-0.13.3.min.js
поставляемый
Обычный файл
18
web/static/js/react-with-addons-0.13.3.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
@@ -18,12 +18,13 @@
|
||||
<link rel="manifest" href="/static/config/manifest.json">
|
||||
<!-- Android add to homescreen -->
|
||||
|
||||
<link rel="stylesheet" href="/static/css/bootstrap-3.3.1.min.css">
|
||||
<link rel="stylesheet" href="/static/css/bootstrap-3.3.5.min.css">
|
||||
<link rel="stylesheet" href="/static/css/jasny-bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<script src="/static/js/react-with-addons-0.13.1.js"></script>
|
||||
<script src="/static/js/react-with-addons-0.13.3.js"></script>
|
||||
<script src="/static/js/jquery-1.11.1.js"></script>
|
||||
<script src="/static/js/bootstrap-3.3.1.js"></script>
|
||||
<script src="/static/js/bootstrap-3.3.5.js"></script>
|
||||
<script src="/static/js/react-bootstrap-0.25.1.js"></script>
|
||||
|
||||
<link id="favicon" rel="icon" href="/static/images/favicon.ico" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="/static/images/favicon.ico" type="image/x-icon">
|
||||
|
||||
Ссылка в новой задаче
Block a user