Commit 7e1b00fa authored by Alf Eaton's avatar Alf Eaton
Browse files

Add initial set of components

parents
{
"parser": "babel-eslint",
"extends": [
"standard",
"plugin:react/recommended"
],
"plugins": [
"react"
],
"parserOptions": {
"ecmaVersion": 7,
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"es6": true,
"browser": true
}
}
api
logs
node_modules
.env.*
_build
Note: xpub is still _very_ new. This repository contains components for uploading and submitting a manuscript, but is not yet ready for use.
import React from 'react'
import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import { browserHistory } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'
import { configureStore, Root } from 'pubsweet-client'
let store = configureStore(browserHistory, {})
let history = syncHistoryWithStore(browserHistory, store)
import 'pubsweet-fira'
import 'typeface-fira-sans-condensed'
import 'typeface-vollkorn'
import './styles/main.scss'
const rootEl = document.getElementById('root')
ReactDOM.render(
<AppContainer>
<Root store={store} history={history} />
</AppContainer>,
rootEl
)
if (module.hot) {
module.hot.accept('pubsweet-client/src/components/Root', () => {
const NextRoot = require('pubsweet-client/src/components/Root').default
ReactDOM.render(
<AppContainer>
<NextRoot store={store} history={history} />
</AppContainer>,
rootEl
)
})
}
.editor {
position: fixed;
top: 50px;
left: 0;
right: 0;
bottom: 0;
background: white;
overflow: hidden;
}
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import actions from 'pubsweet-client/src/actions'
import SimpleEditor from 'pubsweet-component-wax/src/SimpleEditor'
import './Editor.css'
class Editor extends React.Component {
componentDidMount () {
const { actions, params } = this.props
actions.getCollection({
id: params.project
})
actions.getFragment({
id: params.project
}, {
id: params.snapshot
})
}
render () {
const { project, snapshot, actions, currentUser } = this.props
if (!snapshot || !project) return null
return (
<div className="editor">
<SimpleEditor
book={project}
fileUpload={actions.fileUpload}
fragment={snapshot}
history={history}
onSave={({ source }) => actions.updateFragment(project, { source })}
update={data => actions.updateFragment(project, data)}
user={currentUser}
/>
</div>
)
}
}
Editor.propTypes = {
actions: PropTypes.object.isRequired,
currentUser: PropTypes.object,
params: PropTypes.object.isRequired,
project: PropTypes.object,
snapshot: PropTypes.object
}
export default connect(
(state, ownProps) => ({
currentUser: state.currentUser && state.currentUser.isAuthenticated ? state.currentUser.user : null,
project: state.collections.find(collection => collection.id === ownProps.params.project),
snapshot: state.fragments[ownProps.params.snapshot]
}),
dispatch => ({
actions: bindActionCreators(actions, dispatch)
})
)(Editor)
.navbar-link {
color: #4990E2;
text-decoration: none;
font-weight: 800;
}
import React from 'react'
import PropTypes from 'prop-types'
import { Nav, Navbar, NavbarBrand, NavItem } from 'react-bootstrap'
import UpdateSubscriber from 'pubsweet-client/src/components/UpdateSubscriber'
import { bindActionCreators } from 'redux'
import { withRouter } from 'react-router'
import { connect } from 'react-redux'
import actions from 'pubsweet-client/src/actions'
import './Navigation.css'
const Navigation = ({ actions, currentUser }) => (
<Navbar fluid fixedTop style={{ minHeight: 0 }}>
<Navbar.Header>
<NavbarBrand>
<Navbar.Link href="/projects" className="navbar-link">xpub</Navbar.Link>
</NavbarBrand>
</Navbar.Header>
{currentUser && (
<Nav pullRight>
<NavItem><UpdateSubscriber/></NavItem>
<NavItem>logged in as {currentUser.username}</NavItem>
<NavItem onClick={actions.logoutUser}>logout</NavItem>
</Nav>
)}
</Navbar>
)
Navigation.propTypes = {
actions: PropTypes.object.isRequired,
currentUser: PropTypes.object
}
export default withRouter(connect(
state => ({
currentUser: state.currentUser && state.currentUser.isAuthenticated ? state.currentUser.user : null
}),
dispatch => ({
actions: bindActionCreators(actions, dispatch)
})
)(Navigation))
.project-title {
/*font-style: italic;*/
font-size: 200%;
text-decoration: underline rgb(170, 170, 170);
text-decoration-skip: ink;
text-align: center;
margin-bottom: 60px;
margin-top: 120px;
margin-right: 200px;
line-height: 1.1;
}
.project-role {
font-size: 120%
}
.project-role-title {
color: #4990E2
}
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { browserHistory } from 'react-router'
import { Button } from 'react-bootstrap'
import actions from 'pubsweet-client/src/actions'
import Snapshots from './Snapshots'
import './Project.css'
class Project extends React.Component {
componentDidMount () {
const { params } = this.props
this.fetch(params.project)
}
componentWillReceiveProps (nextProps) {
const { params } = nextProps
if (params.project !== this.props.params.project) {
this.fetch(params.project)
}
}
fetch (id) {
const { actions } = this.props
actions.getCollection({ id })
actions.getFragments({ id })
}
remove = () => {
const { project, actions } = this.props
if (!window.confirm('Delete this submission?')) {
return
}
actions.deleteCollection(project).then(() => {
browserHistory.push('/')
})
}
render () {
const { project } = this.props
if (!project) return null
return (
<div className="content-text main" style={{paddingBottom: 90}}>
<div className="container">
<Button bsSize="small" bsStyle="link" onClick={this.remove} style={{ color: '#eee', background: 'none', position: 'fixed', top: 50, right: 50 }}><span className="fa fa-remove"/></Button>
<div className="project-title">{project.title}</div>
<div style={{ display: 'flex' }}>
<div style={{ flex: 1 }}>
<Snapshots project={project}/>
</div>
<div className="content-metadata" style={{ width: 200 }}>
<div style={{ display: 'table', margin: 10, borderLeft: '1px solid #ddd' }}>
<div style={{ display: 'table-row' }}>
<div style={{ display: 'table-cell', padding: '2px 5px 2px 15px', color: '#4990E2' }}>Owner</div>
<div style={{ display: 'table-cell', padding: '2px 5px' }}>{project.owner}</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
}
Project.propTypes = {
actions: PropTypes.object.isRequired,
params: PropTypes.object.isRequired,
project: PropTypes.object
}
export default connect(
(state, ownProps) => ({
project: state.collections.find(collection => collection.id === ownProps.params.project)
}),
dispatch => ({
actions: bindActionCreators(actions, dispatch)
})
)(Project)
import React from 'react'
import PropTypes from 'prop-types'
import './ProjectDeclarations.css'
class ProjectDeclarationAnswers extends React.Component {
render () {
const { project } = this.props
const { declarations = {} } = project
return (
<div className="answers questions-reset content-metadata">
<div className="question-section">
<div className="question-section-heading">PART I - Questions about ethics</div>
<div className="question" style={{ marginBottom: 20 }}>
<div className="answer-toggle">This research {declarations.human ? 'included' : 'did not include'} study of human participants or human tissue.</div>
{declarations.humanReview && (
<div className="content-text" style={{ whiteSpace: 'pre-wrap' }}>{declarations.humanReview}</div>
)}
</div>
<div className="question" style={{ marginBottom: 20 }}>
<div className="answer-title">Please disclose your funders and the role they played in your manuscript
</div>
<div className="content-text" style={{ whiteSpace: 'pre-wrap' }}>{declarations.financialDisclosure}</div>
</div>
</div>
<div className="question-section">
<div className="question-section-heading">PART II - Questions about new discoveries</div>
<div className="question" style={{ marginBottom: 20 }}>
<div className="answer-toggle">This research {declarations.newTaxon ? 'describes' : 'does not describe'} a new taxon.</div>
</div>
</div>
</div>
)
}
}
ProjectDeclarationAnswers.propTypes = {
project: PropTypes.object.isRequired
}
export default ProjectDeclarationAnswers
.questions-reset {
counter-reset: question;
}
.question-section {
/*counter-increment: section;*/
/*counter-reset: question;*/
margin-bottom: 3em;
margin-top: 1em;
max-width: 40em;
}
.question-section-heading {
font-size: 150%;
color: #888;
}
.question {
margin-top: 2em;
text-align: left;
position: relative;
}
.question,
.sub-question {
margin-bottom: 4em;
}
.sub-question {
margin-top: -3em;
}
.questions .question {
color: #4990E2;
}
.question:before {
counter-increment: question;
/*content: counters(section, ".") counter(question);*/
content: counter(question);
background: #4990E2;
text-align: right;
font-size: 11pt;
display: inline-block;
padding: 1em 0.4em 0.1em 2em;
color: white;
position: absolute;
left: 0;
top: -1em;
font-family: "Fira Sans Condensed", sans-serif;
}
.question,
.sub-question {
padding-left: 60px;
}
.bootstrap .question .checkbox,
.bootstrap .question .radio {
margin-top: 0 !important;
}
.bootstrap .question .checkbox .control-label {
display: none;
}
.question.hide-control-label .control-label {
display: none;
}
.answer-title,
.answer-toggle {
color: #888;
}
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import actions from 'pubsweet-client/src/actions'
import FRC from 'formsy-react-components'
import { Button } from 'react-bootstrap'
import './ProjectDeclarations.css'
const styles = {
textarea: {
paddingLeft: 0,
border: 'none',
boxShadow: 'none',
lineHeight: '21pt',
background: 'linear-gradient(rgba(200, 200, 200, 0.5) 1pt, transparent 1pt) 0px 20pt / 100% 21pt'
}
}
class ProjectDeclarations extends React.Component {
constructor (props) {
super(props)
this.state = {
declarations: {}
}
}
componentDidMount () {
const { project } = this.props
this.setState({ declarations: project.declarations })
}
componentWillReceiveProps (nextProps) {
const { project } = nextProps
if (project.id !== this.props.project.id) {
this.setState({ declarations: project.declarations })
}
}
changed = (declarations) => {
this.setState({ declarations })
}
autoresize = name => () => {
const textarea = this[name].element
textarea.style.height = 'auto'
textarea.style.height = textarea.scrollHeight + 'px'
textarea.scrollTop = textarea.scrollHeight
// window.scrollTo(window.scrollLeft, (textarea.scrollTop + textarea.scrollHeight))
}
save = declarations => {
const { actions, project, submit } = this.props
declarations.saved = true
const id = project.id
actions.updateCollection({ id, declarations }).then(submit)
}
render () {
const { declarations = {} } = this.state
return (
<div className="questions questions-reset content-metadata">
<FRC.Form
onSubmit={this.save}
validateOnSubmit={true}
onChange={this.changed}
layout="vertical">
<div className="question-section">
<div className="question-section-heading">PART I - Questions about ethics</div>
<div className="question hide-control-label">
<FRC.Checkbox
name="human"
label="This research included study of human participants or human tissue"
value={declarations.human}/>
</div>
{declarations.human && (
<div className="sub-question">
<FRC.Textarea
name="humanReview"
label="Please name the Institutional Review Board which approved this research:"
value={declarations.humanReview}
rows={1}
// required
style={styles.textarea}
ref={input => (this.humanReviewTextarea = input)}
onChange={this.autoresize('humanReviewTextarea')}/>
</div>
)}
<div className="question">
<FRC.Textarea
name="financialDisclosure"
label="Please disclose your funders and the role they played in your manuscript:"
value={declarations.financialDisclosure}
rows={1}
// required
style={styles.textarea}
ref={input => (this.financialDisclosureTextarea = input)}
onChange={this.autoresize('financialDisclosureTextarea')}/>
</div>
</div>
<div className="question-section">
<div className="question-section-heading">PART II - Questions about new discoveries</div>
<div className="question hide-control-label">
<FRC.Checkbox
name="newTaxon"
label="Does your paper describe a new taxon?"
value={declarations.newTaxon}/>
</div>
{declarations.newTaxon && (
<div className="sub-question">
<p>Please review <a href="https://submit.elifesciences.org/html/elife_author_instructions.html" target="_blank">our policies on new taxon nomenclature.</a></p>
</div>
)}
</div>
<div style={{ textAlign: 'center' }}>
<FRC.Checkbox
name="accept"
label="By checking this box, I accept the terms and conditions"
value={declarations.accept}/>
</div>
<div style={{textAlign: 'center', marginTop: 40}}>
<Button
type="submit"
bsStyle="primary"
bsSize="large"
style={{textTransform: 'uppercase'}}>Submit</Button>
</div>
</FRC.Form>
</div>
)
}
}
ProjectDeclarations.propTypes = {
actions: PropTypes.object.isRequired,
project: PropTypes.object.isRequired,
submit: PropTypes.func.isRequired
}
export default connect(
null,
dispatch => ({
actions: bindActionCreators(actions, dispatch)
})
)(ProjectDeclarations)
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import actions from 'pubsweet-client/src/actions'
import ProjectListItem from './ProjectListItem'
import Upload from './Upload'
class ProjectList extends React.Component {
componentDidMount () {