1- import React , { useState , memo , useEffect } from 'react'
2- import { Link , Box , Stack } from '@chakra-ui/core'
1+ import React , { useState , memo , useEffect , useMemo } from 'react'
2+ import {
3+ Link ,
4+ Box ,
5+ Stack ,
6+ Modal ,
7+ ModalOverlay ,
8+ ModalContent ,
9+ ModalHeader ,
10+ ModalCloseButton ,
11+ ModalBody ,
12+ FormControl ,
13+ FormLabel ,
14+ Input ,
15+ FormErrorMessage ,
16+ FormHelperText ,
17+ ModalFooter ,
18+ Button ,
19+ useDisclosure ,
20+ Text ,
21+ } from '@chakra-ui/core'
322import Panels from './panels/Panels'
423import { GoRepo , GoCode } from 'react-icons/go'
524import { FiTrash2 } from 'react-icons/fi'
@@ -11,11 +30,13 @@ import {
1130 getSelectedComponent ,
1231 getComponents ,
1332 getSelectedComponentId ,
33+ getComponentNames ,
1434} from '../../core/selectors/components'
1535import ActionButton from './ActionButton'
16- import { generateComponentCode } from '../../utils/code'
36+ import { generateComponentCode , formatCode } from '../../utils/code'
1737import useClipboard from '../../hooks/useClipboard'
1838import { useInspectorUpdate } from '../../contexts/inspector-context'
39+ import { componentsList } from '../../componentsList'
1940
2041const CodeActionButton = memo ( ( ) => {
2142 const [ isLoading , setIsLoading ] = useState ( false )
@@ -36,8 +57,13 @@ const CodeActionButton = memo(() => {
3657 variantColor = { hasCopied ? 'green' : 'gray' }
3758 onClick = { async ( ) => {
3859 setIsLoading ( true )
39- const code = await generateComponentCode ( parent , components )
40- onCopy ( code )
60+ const code = await generateComponentCode ( {
61+ component : parent ,
62+ components,
63+ componentName : components [ selectedId ] . componentName ,
64+ forceBuildBlock : true ,
65+ } )
66+ onCopy ( await formatCode ( code ) )
4167 setIsLoading ( false )
4268 } }
4369 icon = { hasCopied ? 'check' : GoCode }
@@ -48,9 +74,30 @@ const CodeActionButton = memo(() => {
4874const Inspector = ( ) => {
4975 const dispatch = useDispatch ( )
5076 const component = useSelector ( getSelectedComponent )
77+ const { isOpen, onOpen, onClose } = useDisclosure ( )
78+ const [ componentName , onChangeComponentName ] = useState ( '' )
79+ const componentsNames = useSelector ( getComponentNames )
5180
5281 const { clearActiveProps } = useInspectorUpdate ( )
5382
83+ const saveComponent = ( e : React . FormEvent < HTMLFormElement > ) => {
84+ e . preventDefault ( )
85+ dispatch . components . setComponentName ( {
86+ componentId : component . id ,
87+ name : componentName ,
88+ } )
89+ onClose ( )
90+ onChangeComponentName ( '' )
91+ }
92+ const isValidComponentName = useMemo ( ( ) => {
93+ return (
94+ ! ! componentName . match ( / ^ [ A - Z ] \w * $ / g) &&
95+ ! componentsNames . includes ( componentName ) &&
96+ // @ts -ignore
97+ ! componentsList . includes ( componentName )
98+ )
99+ } , [ componentName , componentsNames ] )
100+
54101 const { type, rootParentType, id, children } = component
55102
56103 const isRoot = id === 'root'
@@ -75,10 +122,15 @@ const Inspector = () => {
75122 shadow = "sm"
76123 bg = "yellow.100"
77124 display = "flex"
78- alignItems = "center"
79125 justifyContent = "space-between"
126+ flexDir = "column"
80127 >
81128 { isRoot ? 'Document' : type }
129+ { ! ! component . componentName && (
130+ < Text fontSize = "xs" fontWeight = "light" >
131+ { component . componentName }
132+ </ Text >
133+ ) }
82134 </ Box >
83135 { ! isRoot && (
84136 < Stack
@@ -92,6 +144,13 @@ const Inspector = () => {
92144 justify = "flex-end"
93145 >
94146 < CodeActionButton />
147+ { ! component . componentName && (
148+ < ActionButton
149+ label = "Name component"
150+ icon = "edit"
151+ onClick = { onOpen }
152+ />
153+ ) }
95154 < ActionButton
96155 label = "Duplicate"
97156 onClick = { ( ) => dispatch . components . duplicate ( ) }
@@ -132,6 +191,54 @@ const Inspector = () => {
132191 showChildren = { componentHasChildren }
133192 parentIsRoot = { parentIsRoot }
134193 />
194+ < Modal onClose = { onClose } isOpen = { isOpen } isCentered >
195+ < ModalOverlay />
196+ < ModalContent >
197+ < form onSubmit = { saveComponent } >
198+ < ModalHeader > Save this component</ ModalHeader >
199+ < ModalCloseButton />
200+ < ModalBody >
201+ < FormControl isInvalid = { ! isValidComponentName } >
202+ < FormLabel > Component name</ FormLabel >
203+ < Input
204+ size = "md"
205+ autoFocus
206+ variant = "outline"
207+ isFullWidth
208+ focusBorderColor = "blue.500"
209+ errorBorderColor = "red.500"
210+ value = { componentName }
211+ onChange = { ( e : React . ChangeEvent < HTMLInputElement > ) =>
212+ onChangeComponentName ( e . target . value )
213+ }
214+ />
215+ { ! isValidComponentName && (
216+ < FormErrorMessage >
217+ Component name must start with a capital character and must
218+ not contain space or special character, and name should not
219+ be already taken (including existing chakra-ui components).
220+ </ FormErrorMessage >
221+ ) }
222+ < FormHelperText >
223+ This will name your component that you will see in the code
224+ panel as a separated component.
225+ </ FormHelperText >
226+ </ FormControl >
227+ </ ModalBody >
228+ < ModalFooter >
229+ < Button
230+ variantColor = "blue"
231+ mr = { 3 }
232+ type = "submit"
233+ isDisabled = { ! isValidComponentName }
234+ >
235+ Save
236+ </ Button >
237+ < Button onClick = { onClose } > Cancel</ Button >
238+ </ ModalFooter >
239+ </ form >
240+ </ ModalContent >
241+ </ Modal >
135242 </ >
136243 )
137244}
0 commit comments