Skip to content

Commit

Permalink
Add component naming (#92)
Browse files Browse the repository at this point in the history
* Add component naming

* Fix tests

* Add component name in children inspector
  • Loading branch information
foyarash authored Jul 29, 2020
1 parent 27e4921 commit 3080439
Show file tree
Hide file tree
Showing 12 changed files with 425 additions and 136 deletions.
119 changes: 113 additions & 6 deletions src/components/inspector/Inspector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
import React, { useState, memo, useEffect } from 'react'
import { Link, Box, Stack } from '@chakra-ui/core'
import React, { useState, memo, useEffect, useMemo } from 'react'
import {
Link,
Box,
Stack,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
FormControl,
FormLabel,
Input,
FormErrorMessage,
FormHelperText,
ModalFooter,
Button,
useDisclosure,
Text,
} from '@chakra-ui/core'
import Panels from './panels/Panels'
import { GoRepo, GoCode } from 'react-icons/go'
import { FiTrash2 } from 'react-icons/fi'
Expand All @@ -11,11 +30,13 @@ import {
getSelectedComponent,
getComponents,
getSelectedComponentId,
getComponentNames,
} from '../../core/selectors/components'
import ActionButton from './ActionButton'
import { generateComponentCode } from '../../utils/code'
import { generateComponentCode, formatCode } from '../../utils/code'
import useClipboard from '../../hooks/useClipboard'
import { useInspectorUpdate } from '../../contexts/inspector-context'
import { componentsList } from '../../componentsList'

const CodeActionButton = memo(() => {
const [isLoading, setIsLoading] = useState(false)
Expand All @@ -36,8 +57,13 @@ const CodeActionButton = memo(() => {
variantColor={hasCopied ? 'green' : 'gray'}
onClick={async () => {
setIsLoading(true)
const code = await generateComponentCode(parent, components)
onCopy(code)
const code = await generateComponentCode({
component: parent,
components,
componentName: components[selectedId].componentName,
forceBuildBlock: true,
})
onCopy(await formatCode(code))
setIsLoading(false)
}}
icon={hasCopied ? 'check' : GoCode}
Expand All @@ -48,9 +74,30 @@ const CodeActionButton = memo(() => {
const Inspector = () => {
const dispatch = useDispatch()
const component = useSelector(getSelectedComponent)
const { isOpen, onOpen, onClose } = useDisclosure()
const [componentName, onChangeComponentName] = useState('')
const componentsNames = useSelector(getComponentNames)

const { clearActiveProps } = useInspectorUpdate()

const saveComponent = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
dispatch.components.setComponentName({
componentId: component.id,
name: componentName,
})
onClose()
onChangeComponentName('')
}
const isValidComponentName = useMemo(() => {
return (
!!componentName.match(/^[A-Z]\w*$/g) &&
!componentsNames.includes(componentName) &&
// @ts-ignore
!componentsList.includes(componentName)
)
}, [componentName, componentsNames])

const { type, rootParentType, id, children } = component

const isRoot = id === 'root'
Expand All @@ -75,10 +122,15 @@ const Inspector = () => {
shadow="sm"
bg="yellow.100"
display="flex"
alignItems="center"
justifyContent="space-between"
flexDir="column"
>
{isRoot ? 'Document' : type}
{!!component.componentName && (
<Text fontSize="xs" fontWeight="light">
{component.componentName}
</Text>
)}
</Box>
{!isRoot && (
<Stack
Expand All @@ -92,6 +144,13 @@ const Inspector = () => {
justify="flex-end"
>
<CodeActionButton />
{!component.componentName && (
<ActionButton
label="Name component"
icon="edit"
onClick={onOpen}
/>
)}
<ActionButton
label="Duplicate"
onClick={() => dispatch.components.duplicate()}
Expand Down Expand Up @@ -132,6 +191,54 @@ const Inspector = () => {
showChildren={componentHasChildren}
parentIsRoot={parentIsRoot}
/>
<Modal onClose={onClose} isOpen={isOpen} isCentered>
<ModalOverlay />
<ModalContent>
<form onSubmit={saveComponent}>
<ModalHeader>Save this component</ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl isInvalid={!isValidComponentName}>
<FormLabel>Component name</FormLabel>
<Input
size="md"
autoFocus
variant="outline"
isFullWidth
focusBorderColor="blue.500"
errorBorderColor="red.500"
value={componentName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
onChangeComponentName(e.target.value)
}
/>
{!isValidComponentName && (
<FormErrorMessage>
Component name must start with a capital character and must
not contain space or special character, and name should not
be already taken (including existing chakra-ui components).
</FormErrorMessage>
)}
<FormHelperText>
This will name your component that you will see in the code
panel as a separated component.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
variantColor="blue"
mr={3}
type="submit"
isDisabled={!isValidComponentName}
>
Save
</Button>
<Button onClick={onClose}>Cancel</Button>
</ModalFooter>
</form>
</ModalContent>
</Modal>
</>
)
}
Expand Down
13 changes: 11 additions & 2 deletions src/components/inspector/elements-list/ElementListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,20 @@ interface Props extends Pick<IComponent, 'type'> {
onMouseOver: PseudoBoxProps['onMouseOver']
onMouseOut: PseudoBoxProps['onMouseOut']
draggable?: boolean
name?: string
}

const ElementListItem = forwardRef(
(
{ type, opacity = 1, onSelect, onMouseOut, onMouseOver, draggable }: Props,
{
type,
opacity = 1,
onSelect,
onMouseOut,
onMouseOver,
draggable,
name,
}: Props,
ref: React.Ref<HTMLDivElement>,
) => {
return (
Expand All @@ -34,7 +43,7 @@ const ElementListItem = forwardRef(
<Flex align="center">
{draggable && <Icon fontSize="xs" mr={2} name="arrow-up-down" />}
<Text letterSpacing="wide" fontSize="sm" textTransform="capitalize">
{type}
{name || type}
</Text>
</Flex>
<ActionButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface Props extends Pick<IComponent, 'type' | 'id'> {
onSelect: (id: IComponent['id']) => void
onHover: (id: IComponent['id']) => void
onUnhover: () => void
name?: string
}

const ITEM_TYPE = 'elementItem'
Expand All @@ -20,6 +21,7 @@ const ElementListItemDraggable: React.FC<Props> = ({
index,
onHover,
onUnhover,
name,
}) => {
const ref = useRef<HTMLDivElement>(null)
const [, drop] = useDrop({
Expand Down Expand Up @@ -80,6 +82,7 @@ const ElementListItemDraggable: React.FC<Props> = ({
onMouseOut={onUnhover}
type={type}
draggable
name={name}
/>
)
}
Expand Down
1 change: 1 addition & 0 deletions src/components/inspector/elements-list/ElementsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const ElementsList: React.FC<Props> = ({
onSelect={onSelect}
onHover={onHover}
onUnhover={onUnhover}
name={element.componentName}
/>
),
)}
Expand Down
108 changes: 1 addition & 107 deletions src/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,113 +9,7 @@ import {
IconButton,
} from '@chakra-ui/core'
import DragItem from './DragItem'

type MenuItem = {
children?: MenuItems
soon?: boolean
rootParentType?: ComponentType
}

type MenuItems = Partial<
{
[k in ComponentType]: MenuItem
}
>

const menuItems: MenuItems = {
Accordion: {
children: {
Accordion: {},
AccordionItem: {},
AccordionHeader: {},
AccordionPanel: {},
AccordionIcon: {},
},
},
Alert: {
children: {
Alert: {},
AlertDescription: {},
AlertIcon: {},
AlertTitle: {},
},
},
AspectRatioBox: {},
AvatarGroup: {
rootParentType: 'Avatar',
},
Avatar: {},
AvatarBadge: {
rootParentType: 'Avatar',
},
Badge: {},
Box: {},
Breadcrumb: {
children: {
BreadcrumbItem: {},
BreadcrumbLink: {},
},
},
Button: {},
Checkbox: {},
CircularProgress: {},
CloseButton: {},
Code: {},
Divider: {},
Flex: {},
FormControl: {
children: {
FormControl: {},
FormLabel: {},
FormHelperText: {},
FormErrorMessage: {},
},
},
Grid: {},
Heading: {},
Icon: {},
IconButton: {},
Image: {},
Input: {},
InputGroup: {
rootParentType: 'Input',
children: {
InputGroup: {},
Input: {},
InputLeftAddon: {},
InputRightAddon: {},
InputRightElement: {},
InputLeftElement: {},
},
},
Link: {},
List: {
children: {
List: {},
ListItem: {},
},
},
NumberInput: {},
Progress: {},
Radio: {},
RadioGroup: {
rootParentType: 'Radio',
},
SimpleGrid: {},
Spinner: {},
Select: {},
Stack: {},
Switch: {},
Tag: {},
Text: {},
Textarea: {},
Menu: { soon: true },
Tab: { soon: true },
/*"Tabs",
"TabList",
"TabPanel",
"TabPanels"*/
}
import { menuItems, MenuItem } from '../../componentsList'

const Menu = () => {
const [searchTerm, setSearchTerm] = useState('')
Expand Down
Loading

0 comments on commit 3080439

Please sign in to comment.