1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-23 01:39:36 +01:00

toggable textarea

This commit is contained in:
ddejongh 2015-06-09 13:29:22 +02:00
parent b49f3fe9f2
commit 6563bf97b0
11 changed files with 179 additions and 28 deletions

View File

@ -45,7 +45,7 @@ gulp.task('build', function() {
bundle(false); bundle(false);
}); });
gulp.task('serve', ['browser-sync', 'lint:watch', 'sass', 'sass:watch', 'copy'], function() { gulp.task('serve', ['browser-sync', 'sass', 'sass:watch', 'copy'], function() {
bundle(true); bundle(true);
}); });

View File

@ -14,7 +14,7 @@ let ButtonSubmitOrClose = React.createClass({
<div className="modal-footer"> <div className="modal-footer">
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" /> <img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
</div> </div>
) );
} }
return ( return (
<div className="modal-footer"> <div className="modal-footer">

View File

@ -0,0 +1,43 @@
'use strict';
import React from 'react';
import apiUrls from '../../constants/api_urls';
import FormMixin from '../../mixins/form_mixin';
import InputTextAreaToggable from './input_textarea_toggable';
let PersonalNoteForm = React.createClass({
mixins: [FormMixin],
url() {
return apiUrls.note_notes;
},
getFormData() {
return {
bitcoin_id: this.getBitcoinIds().join(),
note: this.refs.personalNote.state.value
};
},
renderForm() {
return (
<form id="personal_note_content" role="form" key="personal_note_content">
<InputTextAreaToggable
ref="personalNote"
className="form-control"
defaultValue={this.props.editions[0].note_from_user}
rows={3}
editable={true}
required=""
onSubmit={this.submit}
/>
</form>
);
}
});
export default PersonalNoteForm;

View File

@ -0,0 +1,72 @@
'use strict';
import React from 'react';
import AlertMixin from '../../mixins/alert_mixin';
import TextareaAutosize from 'react-textarea-autosize';
import Button from 'react-bootstrap/lib/Button';
let InputTextAreaToggable = React.createClass({
mixins: [AlertMixin],
getInitialState() {
return {
value: this.props.defaultValue,
edited: false,
alerts: null // needed in AlertMixin
};
},
handleChange(event) {
this.setState({
value: event.target.value,
edited: true
});
},
reset(){
this.setState(this.getInitialState());
},
submit(){
this.props.onSubmit();
this.setState({edited: false});
},
render() {
let className = 'form-control ascribe-textarea';
let buttons = null;
let textarea = null;
if (this.props.editable && this.state.edited){
buttons = (
<div className="pull-right">
<Button className="ascribe-btn" onClick={this.submit}>Save</Button>
<Button className="ascribe-btn" onClick={this.reset}>Cancel</Button>
</div>
);
}
if (this.props.editable){
className = className + ' ascribe-textarea-editable';
textarea = (
<TextareaAutosize
className={className}
value={this.state.value}
rows={this.props.rows}
required={this.props.required}
onChange={this.handleChange}
placeholder='Write something...' />
);
}
else{
textarea = <pre className="ascribe-pre">{this.state.value}</pre>;
}
let alerts = (this.props.submitted) ? null : this.state.alerts;
return (
<div className="form-group">
{alerts}
{textarea}
{buttons}
</div>
);
}
});
export default InputTextAreaToggable;

View File

@ -8,7 +8,8 @@ import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import TextareaAutosize from 'react-textarea-autosize';
import PersonalNoteForm from './ascribe_forms/form_note_personal';
import EditionActions from '../actions/edition_actions'; import EditionActions from '../actions/edition_actions';
import AclButtonList from './ascribe_buttons/acl_button_list'; import AclButtonList from './ascribe_buttons/acl_button_list';
@ -23,7 +24,7 @@ let Edition = React.createClass({
edition: React.PropTypes.object, edition: React.PropTypes.object,
currentUser: React.PropTypes.object, currentUser: React.PropTypes.object,
deleteEdition: React.PropTypes.func, deleteEdition: React.PropTypes.func,
savePersonalNote: React.PropTypes.func loadEdition: React.PropTypes.func
}, },
render() { render() {
@ -43,6 +44,10 @@ let Edition = React.createClass({
<a target="_blank" href={'https://www.blocktrail.com/BTC/address/' + this.props.edition.hash_as_address}>{this.props.edition.hash_as_address}</a> <a target="_blank" href={'https://www.blocktrail.com/BTC/address/' + this.props.edition.hash_as_address}>{this.props.edition.hash_as_address}</a>
); );
let ownerAddress = (
<a target="_blank" href={'https://www.blocktrail.com/BTC/address/' + this.props.edition.btc_owner_address_noprefix}>{this.props.edition.btc_owner_address_noprefix}</a>
);
return ( return (
<Row> <Row>
<Col md={6}> <Col md={6}>
@ -65,7 +70,8 @@ let Edition = React.createClass({
title="Personal Note" title="Personal Note"
iconName="pencil"> iconName="pencil">
<EditionPersonalNote <EditionPersonalNote
savePersonalNote={this.props.savePersonalNote}/> handleSuccess={this.props.loadEdition}
edition={this.props.edition}/>
</CollapsibleEditionDetails> </CollapsibleEditionDetails>
<CollapsibleEditionDetails <CollapsibleEditionDetails
@ -92,7 +98,7 @@ let Edition = React.createClass({
value={hashOfArtwork} /> value={hashOfArtwork} />
<EditionDetailProperty <EditionDetailProperty
label="Owned by SPOOL address" label="Owned by SPOOL address"
value="MISSING IN /editions/<id> RESOURCE!" /> value={ownerAddress} />
</CollapsibleEditionDetails> </CollapsibleEditionDetails>
<CollapsibleEditionDetails <CollapsibleEditionDetails
@ -267,7 +273,7 @@ let EditionDetailProperty = React.createClass({
<div className="row ascribe-detail-property"> <div className="row ascribe-detail-property">
<div className="row-same-height"> <div className="row-same-height">
<div className={this.props.labelClassName + ' col-xs-height col-bottom'}> <div className={this.props.labelClassName + ' col-xs-height col-bottom'}>
<div>{ this.props.label }{this.props.separator}</div> <div>{ this.props.label + this.props.separator}</div>
</div> </div>
<div className={this.props.valueClassName + ' col-xs-height col-bottom'}> <div className={this.props.valueClassName + ' col-xs-height col-bottom'}>
<div>{ this.props.value }</div> <div>{ this.props.value }</div>
@ -304,7 +310,7 @@ let EditionDetailHistoryIterator = React.createClass({
let EditionPersonalNote = React.createClass({ let EditionPersonalNote = React.createClass({
propTypes: { propTypes: {
savePersonalNote: React.PropTypes.func edition: React.PropTypes.object
}, },
prepareSavePersonalNote() { prepareSavePersonalNote() {
@ -316,14 +322,9 @@ let EditionPersonalNote = React.createClass({
return ( return (
<Row> <Row>
<Col md={12} className="ascribe-edition-personal-note"> <Col md={12} className="ascribe-edition-personal-note">
<TextareaAutosize <PersonalNoteForm
ref="personalNote" handleSuccess={this.props.handleSuccess}
className="form-control" editions={[this.props.edition]} />
rows={3}
placeholder='Write something...' />
<Button
onClick={this.prepareSavePersonalNote}
className="pull-right">Save</Button>
</Col> </Col>
</Row> </Row>
); );

View File

@ -4,6 +4,9 @@ import React from 'react';
import { mergeOptions } from '../utils/general_utils'; import { mergeOptions } from '../utils/general_utils';
import apiUrls from '../constants/api_urls';
import fetch from '../utils/fetch';
import EditionActions from '../actions/edition_actions'; import EditionActions from '../actions/edition_actions';
import EditionStore from '../stores/edition_store'; import EditionStore from '../stores/edition_store';
import UserActions from '../actions/user_actions'; import UserActions from '../actions/user_actions';
@ -40,9 +43,8 @@ let EditionContainer = React.createClass({
// Delete Edition from server // Delete Edition from server
}, },
savePersonalNote(note) { loadEdition() {
console.log(note); EditionActions.fetchOne(this.props.params.editionId);
// Save personalNote to server
}, },
render() { render() {
@ -52,7 +54,7 @@ let EditionContainer = React.createClass({
edition={this.state.edition} edition={this.state.edition}
currentUser={this.state.currentUser} currentUser={this.state.currentUser}
deleteEdition={this.deleteEdition} deleteEdition={this.deleteEdition}
savePersonalNote={this.savePersonalNote}/> loadEdition={this.loadEdition}/>
); );
} else { } else {
return ( return (

View File

@ -13,7 +13,8 @@ let apiUrls = {
'ownership_loans': AppConstants.baseUrl + 'ownership/loans/', 'ownership_loans': AppConstants.baseUrl + 'ownership/loans/',
'ownership_consigns': AppConstants.baseUrl + 'ownership/consigns/', 'ownership_consigns': AppConstants.baseUrl + 'ownership/consigns/',
'ownership_unconsigns': AppConstants.baseUrl + 'ownership/unconsigns/', 'ownership_unconsigns': AppConstants.baseUrl + 'ownership/unconsigns/',
'ownership_unconsigns_request': AppConstants.baseUrl + 'ownership/unconsigns/request/' 'ownership_unconsigns_request': AppConstants.baseUrl + 'ownership/unconsigns/request/',
'note_notes': AppConstants.baseUrl + 'note/notes/'
}; };
export default apiUrls; export default apiUrls;

View File

@ -1,8 +1,8 @@
'use strict'; 'use strict';
let constants = { let constants = {
//'baseUrl': 'http://localhost:8000/api/', 'baseUrl': 'http://localhost:8000/api/',
'baseUrl': 'http://staging.ascribe.io/api/', //'baseUrl': 'http://staging.ascribe.io/api/',
'debugCredentialBase64': 'ZGltaUBtYWlsaW5hdG9yLmNvbTowMDAwMDAwMDAw', // dimi@mailinator:0000000000 'debugCredentialBase64': 'ZGltaUBtYWlsaW5hdG9yLmNvbTowMDAwMDAwMDAw', // dimi@mailinator:0000000000
'aclList': ['edit', 'consign', 'transfer', 'loan', 'share', 'download', 'view', 'delete', 'del_from_collection', 'add_to_collection'] 'aclList': ['edit', 'consign', 'transfer', 'loan', 'share', 'download', 'view', 'delete', 'del_from_collection', 'add_to_collection']
}; };

View File

@ -14,22 +14,32 @@ export const FormMixin = {
}, },
submit(e) { submit(e) {
if (e) {
e.preventDefault(); e.preventDefault();
}
this.setState({submitted: true}); this.setState({submitted: true});
this.clearErrors(); this.clearErrors();
fetch fetch
.post(this.url(), { body: this.getFormData() }) .post(this.url(), { body: this.getFormData() })
.then(() => this.props.handleSuccess()) .then(() => this.handleSuccess() )
.catch(this.handleError); .catch(this.handleError);
}, },
clearErrors(){ clearErrors(){
for (var ref in this.refs){ for (var ref in this.refs){
if ('clearAlerts' in this.refs[ref]){
this.refs[ref].clearAlerts(); this.refs[ref].clearAlerts();
} }
}
this.setState({errors: []}); this.setState({errors: []});
}, },
handleSuccess(){
if ('handleSuccess' in this.props){
this.props.handleSuccess();
}
},
handleError(err){ handleError(err){
if (err.json) { if (err.json) {
for (var input in err.json.errors){ for (var input in err.json.errors){

View File

@ -0,0 +1,21 @@
.ascribe-textarea {
border: none;
box-shadow: none;
margin-bottom: 1em;
}
.ascribe-textarea-editable:hover {
border: 1px solid #AAA;
}
.ascribe-pre{
word-break: break-word;
/* white-space: pre-wrap; */
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
/* word-wrap: break-word; */
font-family: inherit;
text-align: justify;
background-color: white;
}

View File

@ -10,6 +10,7 @@
@import 'ascribe_piece_list_bulk_modal'; @import 'ascribe_piece_list_bulk_modal';
@import 'ascribe_piece_list_toolbar'; @import 'ascribe_piece_list_toolbar';
@import 'ascribe_edition'; @import 'ascribe_edition';
@import 'ascribe_textarea';
@import 'ascribe_media_player'; @import 'ascribe_media_player';
@import 'offset_right'; @import 'offset_right';