Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"components": ["Link"],
"specialLink": ["to"]
}],
"react/prop-types": [0]
"react/prop-types": [0],
"jsx-a11y/click-events-have-key-events": [0]
}
}
13 changes: 13 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
);
70 changes: 55 additions & 15 deletions src/components/NewSnippet.jsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,89 @@
import React from 'react';
import { connect } from 'react-redux';
import { Controlled as CodeMirror } from 'react-codemirror2';

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this ignore rule in two places?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because our lint checker creates to error messages for those two lines

}

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 (
[
<Title title="New snippet" key="New Snippet Title" />,
<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);
33 changes: 29 additions & 4 deletions src/components/Syntaxes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
25 changes: 22 additions & 3 deletions src/components/common/Input.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
4 changes: 2 additions & 2 deletions src/styles/NewSnippet.styl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/styles/common/overwrite.styl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down