Skip to content

Commit 3080439

Browse files
authored
Add component naming (#92)
* Add component naming * Fix tests * Add component name in children inspector
1 parent 27e4921 commit 3080439

File tree

12 files changed

+425
-136
lines changed

12 files changed

+425
-136
lines changed

src/components/inspector/Inspector.tsx

Lines changed: 113 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
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'
322
import Panels from './panels/Panels'
423
import { GoRepo, GoCode } from 'react-icons/go'
524
import { FiTrash2 } from 'react-icons/fi'
@@ -11,11 +30,13 @@ import {
1130
getSelectedComponent,
1231
getComponents,
1332
getSelectedComponentId,
33+
getComponentNames,
1434
} from '../../core/selectors/components'
1535
import ActionButton from './ActionButton'
16-
import { generateComponentCode } from '../../utils/code'
36+
import { generateComponentCode, formatCode } from '../../utils/code'
1737
import useClipboard from '../../hooks/useClipboard'
1838
import { useInspectorUpdate } from '../../contexts/inspector-context'
39+
import { componentsList } from '../../componentsList'
1940

2041
const 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(() => {
4874
const 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
}

src/components/inspector/elements-list/ElementListItem.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,20 @@ interface Props extends Pick<IComponent, 'type'> {
88
onMouseOver: PseudoBoxProps['onMouseOver']
99
onMouseOut: PseudoBoxProps['onMouseOut']
1010
draggable?: boolean
11+
name?: string
1112
}
1213

1314
const ElementListItem = forwardRef(
1415
(
15-
{ type, opacity = 1, onSelect, onMouseOut, onMouseOver, draggable }: Props,
16+
{
17+
type,
18+
opacity = 1,
19+
onSelect,
20+
onMouseOut,
21+
onMouseOver,
22+
draggable,
23+
name,
24+
}: Props,
1625
ref: React.Ref<HTMLDivElement>,
1726
) => {
1827
return (
@@ -34,7 +43,7 @@ const ElementListItem = forwardRef(
3443
<Flex align="center">
3544
{draggable && <Icon fontSize="xs" mr={2} name="arrow-up-down" />}
3645
<Text letterSpacing="wide" fontSize="sm" textTransform="capitalize">
37-
{type}
46+
{name || type}
3847
</Text>
3948
</Flex>
4049
<ActionButton

src/components/inspector/elements-list/ElementListItemDraggable.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ interface Props extends Pick<IComponent, 'type' | 'id'> {
88
onSelect: (id: IComponent['id']) => void
99
onHover: (id: IComponent['id']) => void
1010
onUnhover: () => void
11+
name?: string
1112
}
1213

1314
const ITEM_TYPE = 'elementItem'
@@ -20,6 +21,7 @@ const ElementListItemDraggable: React.FC<Props> = ({
2021
index,
2122
onHover,
2223
onUnhover,
24+
name,
2325
}) => {
2426
const ref = useRef<HTMLDivElement>(null)
2527
const [, drop] = useDrop({
@@ -80,6 +82,7 @@ const ElementListItemDraggable: React.FC<Props> = ({
8082
onMouseOut={onUnhover}
8183
type={type}
8284
draggable
85+
name={name}
8386
/>
8487
)
8588
}

src/components/inspector/elements-list/ElementsList.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const ElementsList: React.FC<Props> = ({
3131
onSelect={onSelect}
3232
onHover={onHover}
3333
onUnhover={onUnhover}
34+
name={element.componentName}
3435
/>
3536
),
3637
)}

src/components/sidebar/Sidebar.tsx

Lines changed: 1 addition & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -9,113 +9,7 @@ import {
99
IconButton,
1010
} from '@chakra-ui/core'
1111
import DragItem from './DragItem'
12-
13-
type MenuItem = {
14-
children?: MenuItems
15-
soon?: boolean
16-
rootParentType?: ComponentType
17-
}
18-
19-
type MenuItems = Partial<
20-
{
21-
[k in ComponentType]: MenuItem
22-
}
23-
>
24-
25-
const menuItems: MenuItems = {
26-
Accordion: {
27-
children: {
28-
Accordion: {},
29-
AccordionItem: {},
30-
AccordionHeader: {},
31-
AccordionPanel: {},
32-
AccordionIcon: {},
33-
},
34-
},
35-
Alert: {
36-
children: {
37-
Alert: {},
38-
AlertDescription: {},
39-
AlertIcon: {},
40-
AlertTitle: {},
41-
},
42-
},
43-
AspectRatioBox: {},
44-
AvatarGroup: {
45-
rootParentType: 'Avatar',
46-
},
47-
Avatar: {},
48-
AvatarBadge: {
49-
rootParentType: 'Avatar',
50-
},
51-
Badge: {},
52-
Box: {},
53-
Breadcrumb: {
54-
children: {
55-
BreadcrumbItem: {},
56-
BreadcrumbLink: {},
57-
},
58-
},
59-
Button: {},
60-
Checkbox: {},
61-
CircularProgress: {},
62-
CloseButton: {},
63-
Code: {},
64-
Divider: {},
65-
Flex: {},
66-
FormControl: {
67-
children: {
68-
FormControl: {},
69-
FormLabel: {},
70-
FormHelperText: {},
71-
FormErrorMessage: {},
72-
},
73-
},
74-
Grid: {},
75-
Heading: {},
76-
Icon: {},
77-
IconButton: {},
78-
Image: {},
79-
Input: {},
80-
InputGroup: {
81-
rootParentType: 'Input',
82-
children: {
83-
InputGroup: {},
84-
Input: {},
85-
InputLeftAddon: {},
86-
InputRightAddon: {},
87-
InputRightElement: {},
88-
InputLeftElement: {},
89-
},
90-
},
91-
Link: {},
92-
List: {
93-
children: {
94-
List: {},
95-
ListItem: {},
96-
},
97-
},
98-
NumberInput: {},
99-
Progress: {},
100-
Radio: {},
101-
RadioGroup: {
102-
rootParentType: 'Radio',
103-
},
104-
SimpleGrid: {},
105-
Spinner: {},
106-
Select: {},
107-
Stack: {},
108-
Switch: {},
109-
Tag: {},
110-
Text: {},
111-
Textarea: {},
112-
Menu: { soon: true },
113-
Tab: { soon: true },
114-
/*"Tabs",
115-
"TabList",
116-
"TabPanel",
117-
"TabPanels"*/
118-
}
12+
import { menuItems, MenuItem } from '../../componentsList'
11913

12014
const Menu = () => {
12115
const [searchTerm, setSearchTerm] = useState('')

0 commit comments

Comments
 (0)