Skip to content

Commit

Permalink
Adds custom props panel (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
baptadn authored Feb 19, 2020
1 parent f5a643a commit 072f470
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 40 deletions.
12 changes: 8 additions & 4 deletions src/components/inspector/Inspector.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React, { useState, memo } from 'react'
import React, { useState, memo, useEffect } from 'react'
import { Link, Box, Stack } from '@chakra-ui/core'

import Panels from './panels/Panels'
import { GoRepo, GoCode } from 'react-icons/go'
import { FiTrash2 } from 'react-icons/fi'
import { IoMdRefresh } from 'react-icons/io'
import { useSelector } from 'react-redux'
import useDispatch from '../../hooks/useDispatch'
import QuickPropsPanel from './QuickPropsPanel'
import StylesPanel from './panels/StylesPanel'
import {
getSelectedComponent,
Expand All @@ -17,6 +15,7 @@ import {
import ActionButton from './ActionButton'
import { generateComponentCode } from '../../utils/code'
import useClipboard from '../../hooks/useClipboard'
import { useInspectorContext } from '../../contexts/inspector-context'

const CodeActionButton = memo(() => {
const [isLoading, setIsLoading] = useState(false)
Expand Down Expand Up @@ -50,6 +49,8 @@ const Inspector = () => {
const dispatch = useDispatch()
const component = useSelector(getSelectedComponent)

let { activePropsRef } = useInspectorContext()

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

const isRoot = id === 'root'
Expand All @@ -58,6 +59,10 @@ const Inspector = () => {
const docType = rootParentType || type
const componentHasChildren = children.length > 0

useEffect(() => {
activePropsRef.current = []
}, [activePropsRef])

return (
<>
<Box bg="white">
Expand Down Expand Up @@ -120,7 +125,6 @@ const Inspector = () => {
</Box>

<Box pb={1} bg="white" px={3}>
{!isRoot && <QuickPropsPanel />}
<Panels component={component} isRoot={isRoot} />
</Box>

Expand Down
36 changes: 0 additions & 36 deletions src/components/inspector/QuickPropsPanel.tsx

This file was deleted.

117 changes: 117 additions & 0 deletions src/components/inspector/panels/CustomPropsPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React, { memo, useState, FormEvent, ChangeEvent, useRef } from 'react'
import { useInspectorContext } from '../../../contexts/inspector-context'
import { getSelectedComponent } from '../../../core/selectors/components'
import { useSelector } from 'react-redux'
import { IoIosFlash } from 'react-icons/io'
import {
IconButton,
Flex,
Box,
SimpleGrid,
InputGroup,
InputRightElement,
Input,
ButtonGroup,
} from '@chakra-ui/core'
import useDispatch from '../../../hooks/useDispatch'
import { useForm } from '../../../hooks/useForm'

const SEPARATOR = '='

const CustomPropsPanel = () => {
const dispatch = useDispatch()
const inputRef = useRef<HTMLInputElement>(null)

const { activePropsRef } = useInspectorContext()
const { props, id } = useSelector(getSelectedComponent)
const { setValue } = useForm()

const [quickProps, setQuickProps] = useState('')
const [hasError, setError] = useState(false)

const onDelete = (propsName: string) => {
dispatch.components.deleteProps({
id,
name: propsName,
})
}

const activeProps = activePropsRef.current || []
const customProps = Object.keys(props).filter(
propsName => !activeProps.includes(propsName),
)

return (
<>
<form
onSubmit={(event: FormEvent) => {
event.preventDefault()

const [name, value] = quickProps.split(SEPARATOR)

if (name && value) {
setValue(name, value)
setQuickProps('')
setError(false)
} else {
setError(true)
}
}}
>
<InputGroup mb={3} size="sm">
<InputRightElement
children={<Box as={IoIosFlash} color="gray.300" />}
/>
<Input
ref={inputRef}
isInvalid={hasError}
value={quickProps}
placeholder={`props${SEPARATOR}value`}
onChange={(event: ChangeEvent<HTMLInputElement>) =>
setQuickProps(event.target.value)
}
/>
</InputGroup>
</form>

{customProps.map((propsName, i) => (
<Flex
alignItems="center"
px={2}
bg={i % 2 === 0 ? 'white' : 'gray.50'}
fontSize="xs"
justifyContent="space-between"
>
<SimpleGrid width="100%" columns={2} spacing={1}>
<Box fontWeight="bold">{propsName}</Box>
<Box>{props[propsName]}</Box>
</SimpleGrid>

<ButtonGroup display="flex" size="xs" isAttached>
<IconButton
onClick={() => {
setQuickProps(`${propsName}=`)
if (inputRef.current) {
inputRef.current.focus()
}
}}
variant="ghost"
size="xs"
aria-label="edit"
icon="edit"
/>
<IconButton
onClick={() => onDelete(propsName)}
variant="ghost"
size="xs"
aria-label="delete"
icon="small-close"
/>
</ButtonGroup>
</Flex>
))}
</>
)
}

export default memo(CustomPropsPanel)
7 changes: 7 additions & 0 deletions src/components/inspector/panels/StylesPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ColorsControl from '../controls/ColorsControl'
import EffectsPanel from './styles/EffectsPanel'
import ChildrenInspector from '../ChildrenInspector'
import ParentInspector from '../ParentInspector'
import CustomPropsPanel from './CustomPropsPanel'

interface Props {
isRoot: boolean
Expand All @@ -24,6 +25,12 @@ const StylesPanel: React.FC<Props> = ({
parentIsRoot,
}) => (
<Accordion defaultIndex={[0]} allowMultiple>
{!isRoot && (
<AccordionContainer title="Custom props">
<CustomPropsPanel />
</AccordionContainer>
)}

{!isRoot && !parentIsRoot && (
<AccordionContainer title="Parent">
<ParentInspector />
Expand Down
32 changes: 32 additions & 0 deletions src/contexts/inspector-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useContext, useRef, MutableRefObject } from 'react'

interface InspectorContextInterface {
activePropsRef: MutableRefObject<string[] | null>
}

const InspectorContext = React.createContext<InspectorContextInterface>({
activePropsRef: React.createRef(),
})

interface InspectorProviderProps {
children: React.ReactNode
}

function InspectorProvider(props: InspectorProviderProps) {
const activePropsRef = useRef<string[] | null>(null)

return (
<InspectorContext.Provider
value={{
activePropsRef,
}}
{...props}
/>
)
}

function useInspectorContext() {
return useContext(InspectorContext)
}

export { InspectorProvider, useInspectorContext }
13 changes: 13 additions & 0 deletions src/core/models/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DEFAULT_PROPS } from '../../utils/defaultProps'
import templates, { TemplateType } from '../../templates'
import { generateId } from '../../utils/generateId'
import { duplicateComponent, deleteComponent } from '../../utils/recursive'
import omit from 'lodash/omit'

export type ComponentsState = {
components: IComponents
Expand Down Expand Up @@ -79,6 +80,18 @@ const components = createModel({
},
}
},
deleteProps(state: ComponentsState, payload: { id: string; name: string }) {
return {
...state,
components: {
...state.components,
[payload.id]: {
...state.components[payload.id],
props: omit(state.components[payload.id].props, payload.name),
},
},
}
},
deleteComponent(state: ComponentsState, componentId: string) {
if (componentId === 'root') {
return state
Expand Down
7 changes: 7 additions & 0 deletions src/hooks/usePropsSelector.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { useSelector } from 'react-redux'
import { RootState } from '../core/store'
import { getDefaultFormProps } from '../utils/defaultProps'
import { useInspectorContext } from '../contexts/inspector-context'

const usePropsSelector = (propsName: string) => {
const { activePropsRef } = useInspectorContext()

if (activePropsRef.current) {
activePropsRef.current.push(propsName)
}

const value = useSelector((state: RootState) => {
const component =
state.components.present.components[state.components.present.selectedId]
Expand Down

0 comments on commit 072f470

Please sign in to comment.