diff --git a/.eslintrc.json b/.eslintrc.json index f20c3f5..02c846a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -8,6 +8,7 @@ "components": ["Link"], "specialLink": ["to"] }], - "react/prop-types": [0] + "react/prop-types": [0], + "jsx-a11y/click-events-have-key-events": [0] } } diff --git a/src/actions/index.js b/src/actions/index.js index d1faee1..5a95e6a 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -30,3 +30,16 @@ export const fetchSyntaxes = dispatch => ( .then(response => response.json()) .then(json => dispatch(setSyntaxes(json))) ); + +export const postSnippet = snippet => dispatch => ( + fetch('http://api.xsnippet.org/snippets', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(snippet), + }) + .then(response => response.json()) + .then(json => dispatch(setSnippet(json))) +); diff --git a/src/components/NewSnippet.jsx b/src/components/NewSnippet.jsx index 0ea9b6d..3544f40 100644 --- a/src/components/NewSnippet.jsx +++ b/src/components/NewSnippet.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import { connect } from 'react-redux'; import { Controlled as CodeMirror } from 'react-codemirror2'; import 'codemirror/lib/codemirror.css'; @@ -6,44 +7,83 @@ import 'codemirror/lib/codemirror.css'; import Title from './common/Title'; import Input from './common/Input'; import Syntaxes from './Syntaxes'; +import * as actions from '../actions'; import '../styles/NewSnippet.styl'; class NewSnippet extends React.Component { - constructor() { - super(); - this.state = { code: '' }; + constructor(props) { + super(props); + this.state = { + content: '', + title: '', + tags: [], + syntax: '', // eslint-disable-line react/no-unused-state + }; + this.postSnippet = this.postSnippet.bind(this); + this.onSyntaxClick = this.onSyntaxClick.bind(this); + this.onInputChange = this.onInputChange.bind(this); } + + onSyntaxClick(syntax) { + this.setState({ syntax }); // eslint-disable-line react/no-unused-state + } + + onInputChange(e) { + const { name } = e.target; + let { value } = e.target; + + if (name === 'tags') { + value = value.split(',').map(item => item.trim()); + } + + this.setState({ [name]: value }); + } + + postSnippet(e) { + e.preventDefault(); + const { dispatch } = this.props; + dispatch(actions.postSnippet(this.state)); + } + render() { return ( [ , - <div className="new-snippet" key="New Snippet"> + <form className="new-snippet" key="New Snippet" onSubmit={this.postSnippet}> <div className="new-snippet-code-wrapper"> <div className="new-snippet-code-header"> - <Input placeholder="Title" /> - <Input placeholder="Tags (separate tags by comma)" /> + <Input + placeholder="Title" + name="title" + onChangeHandler={this.onInputChange} + value={this.state.title} + /> + <Input + placeholder="Tags (separate tags by comma)" + name="tags" + onChangeHandler={this.onInputChange} + value={this.state.tags.toString()} + /> </div> <div className="new-snippet-code"> <CodeMirror - value={this.state.code} - options={{ lineNumbers: false }} - onBeforeChange={(editor, data, code) => { - this.setState({ code }); - }} + value={this.state.content} + options={{ lineNumbers: true }} + onBeforeChange={(editor, data, content) => { this.setState({ content }); }} /> <div className="new-snippet-code-bottom-bar"> - <button>POST</button> + <input type="submit" value="POST" /> </div> </div> </div> <div className="new-snippet-lang-wrapper"> - <Syntaxes /> + <Syntaxes onClick={this.onSyntaxClick} /> </div> - </div>, + </form>, ] ); } } -export default NewSnippet; +export default connect()(NewSnippet); diff --git a/src/components/Syntaxes.jsx b/src/components/Syntaxes.jsx index c25a866..89cb21b 100644 --- a/src/components/Syntaxes.jsx +++ b/src/components/Syntaxes.jsx @@ -6,23 +6,48 @@ import Input from './common/Input'; import * as actions from '../actions'; class Syntaxes extends React.Component { + constructor(props) { + super(props); + this.state = { activeIndex: -1 }; + this.onClickHandler = this.onClickHandler.bind(this); + } + componentDidMount() { const { dispatch } = this.props; dispatch(actions.fetchSyntaxes); } + onClickHandler(e) { + const { index, syntax } = e.target.dataset; + + this.setState({ activeIndex: Number(index) }); + this.props.onClick(syntax); + } + render() { const { syntaxes } = this.props; return ( [ - <div className="new-snippet-lang-header"> + <div className="new-snippet-lang-header" key="Syntax input"> <Input placeholder="Type to search..." /> </div>, - <div className="new-snippet-lang-list-wrapper"> + <div className="new-snippet-lang-list-wrapper" key="Syntax list"> <Scrollbars> - <ul className="new-snippet-lang-list"> - {syntaxes.map(syntax => <li className="new-snippet-lang-item">{syntax}</li>)} + <ul className="new-snippet-lang-list" onClick={this.onClickHandler} role="presentation"> + {syntaxes.map((syntax, index) => { + const active = this.state.activeIndex === index ? 'active' : ''; + return ( + <li + className={`new-snippet-lang-item ${active}`} + data-syntax={syntax} + data-index={index} + key={syntax} + > + {syntax} + </li> + ); + })} </ul> </Scrollbars> </div>, diff --git a/src/components/common/Input.jsx b/src/components/common/Input.jsx index a168230..6cc2f3d 100644 --- a/src/components/common/Input.jsx +++ b/src/components/common/Input.jsx @@ -3,20 +3,39 @@ import PropTypes from 'prop-types'; import '../../styles/common/Input.styl'; -const Input = ({ additionalClass, placeholder, value }) => ( - <input className={`input ${additionalClass}`} placeholder={placeholder} value={value} /> -); +const Input = (props) => { + const { + name, type, value, placeholder, additionalClass, onChangeHandler, + } = props; + + return ( + <input + className={`input ${additionalClass}`} + placeholder={placeholder} + name={name} + type={type} + value={value} + onChange={onChangeHandler} + /> + ); +}; Input.propTypes = { additionalClass: PropTypes.string, placeholder: PropTypes.string, value: PropTypes.string, + name: PropTypes.string, + type: PropTypes.string, + onChangeHandler: PropTypes.func, }; Input.defaultProps = { additionalClass: '', placeholder: 'Enter text', value: '', + name: 'input', + type: 'text', + onChangeHandler: () => {}, }; export default Input; diff --git a/src/styles/NewSnippet.styl b/src/styles/NewSnippet.styl index 662a9f8..f1c522f 100644 --- a/src/styles/NewSnippet.styl +++ b/src/styles/NewSnippet.styl @@ -21,7 +21,7 @@ lang-bar-width = 230px &-code flex: 1 position: relative - padding: 20px 20px 63px 20px + padding: 0 20px 63px 0 &-wrapper display: flex flex-flow: column nowrap @@ -40,7 +40,7 @@ lang-bar-width = 230px text-align: right padding: 5px border-top: 1px solid border-light - & button + & > input border: none background-color: button-normal color: text-light diff --git a/src/styles/common/overwrite.styl b/src/styles/common/overwrite.styl index ef8c5d7..a0dd0db 100644 --- a/src/styles/common/overwrite.styl +++ b/src/styles/common/overwrite.styl @@ -4,6 +4,9 @@ .CodeMirror height: 100% !important font-family: font-ubuntu-mono !important + +.new-snippet .react-codemirror2, +.new-snippet .CodeMirror color: text-dark !important .CodeMirror-gutter