1
- import React from 'react'
1
+ import React , { useEffect , useRef } from 'react'
2
2
import { connect } from 'react-redux'
3
3
import AceEditor from 'react-ace'
4
4
import { WithContext as Tags } from 'react-tag-input'
@@ -7,166 +7,133 @@ import 'brace/theme/textmate'
7
7
8
8
import Notification from './common/Notification'
9
9
import ListBoxWithSearch from './ListBoxWithSearch'
10
+
10
11
import { fetchSyntaxes , postSnippet } from '../actions'
11
12
12
- import { validateSnippet } from '../entries/snippetValidation'
13
- import { getCurrentModeName , getModesByName } from '../misc/modes'
14
13
import { onEditorLoad } from '../misc/editor'
15
- import { recalcLangHeaderHeight } from '../misc/dom '
14
+ import { getCurrentModeName , getModesByName } from '../misc/modes '
16
15
16
+ import { validateSnippet } from '../entries/snippetValidation'
17
17
import { delimeterKeys } from '../entries/keyboardKeys'
18
18
import { defaultOptions } from '../entries/aceEditorOptions'
19
19
20
- import '../styles/NewSnippet.styl '
20
+ import useForm from '../hooks/useForm '
21
21
22
- class NewSnippet extends React . Component {
23
- constructor ( props ) {
24
- super ( props )
22
+ import '../styles/NewSnippet.styl'
25
23
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 )
34
32
35
- componentDidMount ( ) {
36
- const { dispatch } = this . props
33
+ useEffect ( ( ) => {
37
34
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
+ } , [ ] )
50
36
51
- this . setState ( { tags : tags . filter ( ( tag , index ) => index !== i ) } , ( ) => {
52
- recalcLangHeaderHeight ( )
53
- } )
54
- }
37
+ useEffect ( ( ) => {
38
+ recalcLangHeaderHeight ( )
39
+ } , [ tags ] )
55
40
56
- onTagBlur = tag => {
57
- this . onTagAdded ( { id : tag , text : tag } )
41
+ function validate ( ) {
42
+ return validateSnippet ( { content : content . trim ( ) } )
58
43
}
59
44
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 } ` ) ) )
62
49
}
63
50
64
- onInputChange = e => {
65
- const { name , value } = e . target
51
+ const recalcLangHeaderHeight = ( ) => {
52
+ const height = snippetHeader . current . offsetHeight
66
53
67
- this . setState ( { [ name ] : value } )
54
+ document . getElementsByClassName ( 'new-snippet-lang-header' ) [ 0 ]
55
+ . setAttribute ( 'style' , `height:${ height } px` )
68
56
}
69
57
70
- validate = ( ) => {
71
- const { content } = this . state
58
+ const onTagBlur = tag => onTagAdded ( { id : tag , text : tag } )
72
59
73
- return validateSnippet ( { content : content . trim ( ) } )
60
+ const onTagAdded = tag => {
61
+ if ( tag && tag . text ) {
62
+ return { tags : [ ...tags , tag ] }
63
+ }
74
64
}
75
65
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 ) }
92
68
}
93
69
94
- getSyntaxes = ( ) => {
70
+ const handleSyntax = syntax => ( { syntax } )
71
+ const handleContent = content => ( { content } )
72
+
73
+ const getSyntaxes = ( ) => {
95
74
const { modesByName } = getModesByName ( )
96
75
97
- return this . props . syntaxes . map ( item => ( {
76
+ return syntaxes . map ( item => ( {
98
77
name : modesByName [ item ] . caption ,
99
78
value : item ,
100
79
} ) )
101
80
}
102
81
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
+ />
160
109
</ 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 ) }
165
122
/>
123
+ < div className = "new-snippet-code-bottom-bar" >
124
+ { renderValidationError ( ) }
125
+ < input type = "submit" value = "POST SNIPPET" />
126
+ </ div >
166
127
</ 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
+ )
170
137
}
171
138
172
139
export default connect ( state => ( {
0 commit comments