1- import React from 'react'
1+ import React , { useEffect , useRef } from 'react'
22import { connect } from 'react-redux'
33import AceEditor from 'react-ace'
44import { WithContext as Tags } from 'react-tag-input'
@@ -7,166 +7,133 @@ import 'brace/theme/textmate'
77
88import Notification from './common/Notification'
99import ListBoxWithSearch from './ListBoxWithSearch'
10+
1011import { fetchSyntaxes , postSnippet } from '../actions'
1112
12- import { validateSnippet } from '../entries/snippetValidation'
13- import { getCurrentModeName , getModesByName } from '../misc/modes'
1413import { onEditorLoad } from '../misc/editor'
15- import { recalcLangHeaderHeight } from '../misc/dom '
14+ import { getCurrentModeName , getModesByName } from '../misc/modes '
1615
16+ import { validateSnippet } from '../entries/snippetValidation'
1717import { delimeterKeys } from '../entries/keyboardKeys'
1818import { defaultOptions } from '../entries/aceEditorOptions'
1919
20- import '../styles/NewSnippet.styl '
20+ import useForm from '../hooks/useForm '
2121
22- class NewSnippet extends React . Component {
23- constructor ( props ) {
24- super ( props )
22+ import '../styles/NewSnippet.styl'
2523
26- this . state = {
27- content : '' ,
28- title : '' ,
29- tags : [ ] ,
30- syntax : '' ,
31- validationError : null ,
32- }
33- }
24+ const NewSnippet = ( { dispatch , history , syntaxes } ) => {
25+ const snippetHeader = useRef ( )
26+ const {
27+ values : { title = '' , syntax = '' , content = '' , tags = [ ] } ,
28+ error ,
29+ handleChange ,
30+ handleSubmit ,
31+ } = useForm ( post , validate )
3432
35- componentDidMount ( ) {
36- const { dispatch } = this . props
33+ useEffect ( ( ) => {
3734 dispatch ( fetchSyntaxes )
38- }
39-
40- onTagAdded = tag => {
41- if ( tag && tag . text ) {
42- this . setState ( { tags : [ ...this . state . tags , tag ] } , ( ) => {
43- recalcLangHeaderHeight ( )
44- } )
45- }
46- }
47-
48- onTagRemoved = i => {
49- const { tags } = this . state
35+ } , [ ] )
5036
51- this . setState ( { tags : tags . filter ( ( tag , index ) => index !== i ) } , ( ) => {
52- recalcLangHeaderHeight ( )
53- } )
54- }
37+ useEffect ( ( ) => {
38+ recalcLangHeaderHeight ( )
39+ } , [ tags ] )
5540
56- onTagBlur = tag => {
57- this . onTagAdded ( { id : tag , text : tag } )
41+ function validate ( ) {
42+ return validateSnippet ( { content : content . trim ( ) } )
5843 }
5944
60- onSyntaxClick = syntax => {
61- this . setState ( { syntax } )
45+ function post ( ) {
46+ dispatch ( postSnippet ( {
47+ content, title, tags : tags . map ( tag => tag . text ) , syntax,
48+ } , json => history . push ( `/${ json . id } ` ) ) )
6249 }
6350
64- onInputChange = e => {
65- const { name , value } = e . target
51+ const recalcLangHeaderHeight = ( ) => {
52+ const height = snippetHeader . current . offsetHeight
6653
67- this . setState ( { [ name ] : value } )
54+ document . getElementsByClassName ( 'new-snippet-lang-header' ) [ 0 ]
55+ . setAttribute ( 'style' , `height:${ height } px` )
6856 }
6957
70- validate = ( ) => {
71- const { content } = this . state
58+ const onTagBlur = tag => onTagAdded ( { id : tag , text : tag } )
7259
73- return validateSnippet ( { content : content . trim ( ) } )
60+ const onTagAdded = tag => {
61+ if ( tag && tag . text ) {
62+ return { tags : [ ...tags , tag ] }
63+ }
7464 }
7565
76- post = e => {
77- e . preventDefault ( )
78- const { dispatch, history } = this . props
79- const { error } = this . validate ( )
80-
81- this . setState ( { validationError : error } )
82-
83- if ( ! error ) {
84- const {
85- content, title, tags, syntax,
86- } = this . state
87-
88- dispatch ( postSnippet ( {
89- content, title, tags : tags . map ( tag => tag . text ) , syntax,
90- } , json => history . push ( `/${ json . id } ` ) ) )
91- }
66+ const onTagRemoved = i => {
67+ return { tags : tags . filter ( ( tag , index ) => index !== i ) }
9268 }
9369
94- getSyntaxes = ( ) => {
70+ const handleSyntax = syntax => ( { syntax } )
71+ const handleContent = content => ( { content } )
72+
73+ const getSyntaxes = ( ) => {
9574 const { modesByName } = getModesByName ( )
9675
97- return this . props . syntaxes . map ( item => ( {
76+ return syntaxes . map ( item => ( {
9877 name : modesByName [ item ] . caption ,
9978 value : item ,
10079 } ) )
10180 }
10281
103- renderValidationError = ( ) => {
104- const { validationError } = this . state
105-
106- return validationError && < Notification
107- message = "Content is required :("
108- show = { ! ! validationError }
109- />
110- }
111-
112- render ( ) {
113- const { syntax, content, title, tags } = this . state
114-
115- return (
116- < form
117- className = "new-snippet"
118- key = "New Snippet"
119- onSubmit = { this . post }
120- role = "presentation"
121- >
122- < div className = "new-snippet-code-wrapper" >
123- < div className = "new-snippet-code-header" >
124- < input
125- className = "input"
126- placeholder = "Title"
127- name = "title"
128- type = "text"
129- value = { title }
130- onChange = { this . onInputChange }
131- />
132- < Tags
133- placeholder = "Tags"
134- tags = { tags }
135- handleDelete = { this . onTagRemoved }
136- handleAddition = { this . onTagAdded }
137- handleInputBlur = { this . onTagBlur }
138- delimiters = { delimeterKeys }
139- />
140- </ div >
141- < div className = "new-snippet-code" >
142- < AceEditor
143- mode = { getCurrentModeName ( syntax ) }
144- width = "100%"
145- height = "100%"
146- focus
147- theme = "textmate"
148- onLoad = { onEditorLoad }
149- setOptions = { defaultOptions }
150- editorProps = { { $blockScrolling : Infinity } }
151- value = { content }
152- onChange = { ( content ) => { this . setState ( { content } ) } }
153- />
154-
155- < div className = "new-snippet-code-bottom-bar" >
156- { this . renderValidationError ( ) }
157- < input type = "submit" value = "POST SNIPPET" />
158- </ div >
159- </ div >
82+ const renderValidationError = ( ) => ( error && < Notification message = { error } /> )
83+
84+ return (
85+ < form
86+ className = "new-snippet"
87+ key = "New Snippet"
88+ onSubmit = { handleSubmit }
89+ role = "presentation"
90+ >
91+ < div className = "new-snippet-code-wrapper" >
92+ < div className = "new-snippet-code-header" ref = { snippetHeader } >
93+ < input
94+ className = "input"
95+ placeholder = "Title"
96+ name = "title"
97+ type = "text"
98+ value = { title }
99+ onChange = { handleChange }
100+ />
101+ < Tags
102+ placeholder = "Tags"
103+ tags = { tags }
104+ handleDelete = { ( value ) => handleChange ( value , onTagRemoved ) }
105+ handleAddition = { ( value ) => handleChange ( value , onTagAdded ) }
106+ handleInputBlur = { ( value ) => handleChange ( value , onTagBlur ) }
107+ delimiters = { delimeterKeys }
108+ />
160109 </ div >
161- < div className = "new-snippet-lang-wrapper" >
162- < ListBoxWithSearch
163- items = { this . getSyntaxes ( ) }
164- onClick = { this . onSyntaxClick }
110+ < div className = "new-snippet-code" >
111+ < AceEditor
112+ mode = { getCurrentModeName ( syntax ) }
113+ width = "100%"
114+ height = "100%"
115+ focus
116+ theme = "textmate"
117+ onLoad = { onEditorLoad }
118+ setOptions = { defaultOptions }
119+ editorProps = { { $blockScrolling : Infinity } }
120+ value = { content }
121+ onChange = { ( value ) => handleChange ( value , handleContent ) }
165122 />
123+ < div className = "new-snippet-code-bottom-bar" >
124+ { renderValidationError ( ) }
125+ < input type = "submit" value = "POST SNIPPET" />
126+ </ div >
166127 </ div >
167- </ form >
168- )
169- }
128+ </ div >
129+ < div className = "new-snippet-lang-wrapper" >
130+ < ListBoxWithSearch
131+ items = { getSyntaxes ( ) }
132+ onClick = { ( syntax ) => handleChange ( syntax , handleSyntax ) }
133+ />
134+ </ div >
135+ </ form >
136+ )
170137}
171138
172139export default connect ( state => ( {
0 commit comments