Ivan - Create Ticket Form - Create Basic TextEditor component using draft.js [skip ci]
This commit is contained in:
parent
8b6266c6c8
commit
95290e97f7
|
@ -55,6 +55,7 @@
|
|||
"dependencies": {
|
||||
"app-module-path": "^1.0.3",
|
||||
"classnames": "^2.1.3",
|
||||
"draft-js": "^0.8.1",
|
||||
"jquery": "^2.1.4",
|
||||
"keycode": "^2.1.4",
|
||||
"localStorage": "^1.0.3",
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import React from 'react';
|
||||
import ReCAPTCHA from 'react-google-recaptcha';
|
||||
|
||||
import i18n from 'lib-app/i18n';
|
||||
import API from 'lib-app/api-call';
|
||||
|
||||
import SubmitButton from 'core-components/submit-button';
|
||||
import Message from 'core-components/message';
|
||||
import Form from 'core-components/form';
|
||||
import Input from 'core-components/input';
|
||||
import TextEditor from 'core-components/text-editor';
|
||||
import DropDown from 'core-components/drop-down';
|
||||
import Widget from 'core-components/widget';
|
||||
|
||||
class CreateTicketForm extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Form>
|
||||
<DropDown items={[
|
||||
{content: 'Department1'},
|
||||
{content: 'Department2'},
|
||||
{content: 'Department3'}
|
||||
]} />
|
||||
<Input label="Title" name="title" required />
|
||||
<TextEditor label="Content" name="content" required />
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CreateTicketForm;
|
|
@ -1,11 +1,13 @@
|
|||
import React from 'react';
|
||||
|
||||
import CreateTicketForm from 'app/main/dashboard/dashboard-create-ticket/create-ticket-form';
|
||||
|
||||
class DashboardCreateTicketPage extends React.Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
DASHBOARD CREATE TICKET
|
||||
<CreateTicketForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ class MainLayout extends React.Component {
|
|||
return (
|
||||
<div className="main-layout">
|
||||
<MainHeader />
|
||||
<div className="main-layout--content">
|
||||
<div className="main-layout--content row">
|
||||
{this.props.children}
|
||||
</div>
|
||||
<MainFooter />
|
||||
|
|
|
@ -6,6 +6,9 @@ import classNames from 'classnames';
|
|||
// CORE LIBS
|
||||
import callback from 'lib-core/callback';
|
||||
|
||||
// CORE COMPONENTS
|
||||
import Icon from 'core-components/icon';
|
||||
|
||||
class Button extends React.Component {
|
||||
|
||||
static contextTypes = {
|
||||
|
@ -16,6 +19,7 @@ class Button extends React.Component {
|
|||
children: React.PropTypes.node,
|
||||
type: React.PropTypes.oneOf([
|
||||
'primary',
|
||||
'primary-icon',
|
||||
'clean',
|
||||
'link'
|
||||
]),
|
||||
|
@ -23,7 +27,8 @@ class Button extends React.Component {
|
|||
to: React.PropTypes. string.isRequired,
|
||||
params: React.PropTypes.object,
|
||||
query: React.PropTypes.query
|
||||
})
|
||||
}),
|
||||
iconName: React.PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -33,7 +38,7 @@ class Button extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<button {...this.getProps()}>
|
||||
{this.props.children}
|
||||
{(this.props.iconName) ? <Icon size="sm" name={this.props.iconName}/> : this.props.children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
.button {
|
||||
|
||||
&-primary {
|
||||
&-primary,
|
||||
&-primary-icon {
|
||||
background-color: $primary-red;
|
||||
border: solid transparent;
|
||||
border-radius: 4px;
|
||||
|
@ -12,6 +13,11 @@
|
|||
width: 239px;
|
||||
}
|
||||
|
||||
&-primary-icon {
|
||||
width: initial;
|
||||
height: initial;
|
||||
}
|
||||
|
||||
&-clean {
|
||||
background: none;
|
||||
border: none;
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
|
||||
import TextEditor from 'core-components/text-editor';
|
||||
|
||||
class TextArea extends React.Component {
|
||||
|
||||
static contextTypes = {
|
||||
loading: React.PropTypes.bool
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
value: React.PropTypes.string,
|
||||
validation: React.PropTypes.string,
|
||||
onChange: React.PropTypes.func,
|
||||
required: React.PropTypes.bool,
|
||||
error: React.PropTypes.string
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
editorState: EditorState.createEmpty()
|
||||
};
|
||||
this.onChange = (editorState) => this.setState({editorState});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<label className={this.getClass()}>
|
||||
<span className="text-area__label">{this.props.label}</span>
|
||||
<TextEditor />
|
||||
{this.renderError()}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
renderError() {
|
||||
let error = null;
|
||||
|
||||
if (this.props.error){
|
||||
error = <span className="text-area__error"> {this.props.error} </span>;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*getEditorProps() {
|
||||
let props = _.clone(this.props);
|
||||
|
||||
props['aria-required'] = this.props.required;
|
||||
props.className = 'text-area__input';
|
||||
props.ref = 'nativeTextArea';
|
||||
props.disabled = this.context.loading;
|
||||
|
||||
delete props.required;
|
||||
delete props.validation;
|
||||
delete props.error;
|
||||
delete props.password;
|
||||
|
||||
return props;
|
||||
}*/
|
||||
|
||||
getClass() {
|
||||
let classes = {
|
||||
'text-area': true,
|
||||
'text-area_with-error': (this.props.error),
|
||||
|
||||
[this.props.className]: (this.props.className)
|
||||
};
|
||||
|
||||
return classNames(classes);
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (this.refs.nativeTextArea) {
|
||||
this.refs.nativeTextArea.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TextArea;
|
|
@ -0,0 +1,42 @@
|
|||
@import "../scss/vars";
|
||||
|
||||
.text-area {
|
||||
display: block;
|
||||
|
||||
&__editor {
|
||||
border: 1px solid $grey;
|
||||
border-radius: 3px;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
border-color: $medium-grey;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $primary-blue;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
color: $primary-black;
|
||||
font-size: 15px;
|
||||
display: block;
|
||||
padding: 3px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&_with-error {
|
||||
.text-area__error {
|
||||
color: $primary-red;
|
||||
font-size: $font-size--sm;
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
.text-area__text {
|
||||
border: 1px solid $primary-red;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
import {Editor, EditorState, RichUtils} from 'draft-js';
|
||||
import Button from 'core-components/button';
|
||||
|
||||
class TextEditor extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
editorState: EditorState.createEmpty(),
|
||||
focused: false
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={this.getClass()}>
|
||||
{this.renderEditOptions()}
|
||||
<div className="text-editor__editor" onClick={this.focus.bind(this)}>
|
||||
<Editor {...this.getEditorProps()} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderEditOptions() {
|
||||
const onBoldClick = (event) => {
|
||||
event.preventDefault();
|
||||
this.onEditorChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD'));
|
||||
};
|
||||
const onItalicsClick = (event) => {
|
||||
event.preventDefault();
|
||||
this.onEditorChange(RichUtils.toggleInlineStyle(this.state.editorState, 'ITALICS'));
|
||||
};
|
||||
const onUnderlineClick = (event) => {
|
||||
event.preventDefault();
|
||||
this.onEditorChange(RichUtils.toggleInlineStyle(this.state.editorState, 'UNDERLINE'));
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="text-editor__options">
|
||||
<Button type="primary-icon" iconName="bold" onClick={onBoldClick.bind(this)} onMouseDown={(e) => {e.preventDefault()}} />
|
||||
<Button type="primary-icon" iconName="italic" onClick={onItalicsClick.bind(this)} onMouseDown={(e) => {e.preventDefault()}} />
|
||||
<Button type="primary-icon" iconName="underline" onClick={onUnderlineClick.bind(this)} onMouseDown={(e) => {e.preventDefault()}} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
getClass() {
|
||||
let classes = {
|
||||
'text-editor': true,
|
||||
'text-editor_focused': (this.state.focused),
|
||||
|
||||
[this.props.className]: (this.props.className)
|
||||
};
|
||||
|
||||
return classNames(classes);
|
||||
}
|
||||
|
||||
getEditorProps() {
|
||||
return {
|
||||
editorState: this.state.editorState,
|
||||
ref: 'editor',
|
||||
onChange: this.onEditorChange.bind(this),
|
||||
onFocus: this.onEditorFocus.bind(this),
|
||||
onBlur: this.onBlur.bind(this)
|
||||
};
|
||||
}
|
||||
|
||||
onEditorChange(editorState) {
|
||||
this.setState({editorState});
|
||||
}
|
||||
|
||||
onEditorFocus() {
|
||||
this.setState({focused: true});
|
||||
}
|
||||
|
||||
onBlur() {
|
||||
this.setState({focused: false});
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (this.refs.editor) {
|
||||
this.refs.editor.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TextEditor;
|
|
@ -0,0 +1,33 @@
|
|||
@import "../scss/vars";
|
||||
|
||||
.text-editor {
|
||||
|
||||
&__editor {
|
||||
border: 1px solid $grey;
|
||||
border-radius: 3px;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
text-align: left;
|
||||
overflow: auto;
|
||||
|
||||
&:hover {
|
||||
border-color: $medium-grey;
|
||||
}
|
||||
}
|
||||
|
||||
&__options {
|
||||
text-align: left;
|
||||
margin-bottom: 5px;
|
||||
|
||||
.button {
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&_focused {
|
||||
.text-editor__editor {
|
||||
border-color: $primary-blue;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue