Skip to content

Commit

Permalink
Add import export feature (#78)
Browse files Browse the repository at this point in the history
  • Loading branch information
baptadn authored Mar 6, 2020
1 parent c8c29e9 commit 4cc98d4
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 25 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@reach/combobox": "^0.7.3",
"@rehooks/local-storage": "^2.1.1",
"@rematch/core": "^1.3.0",
"browser-nativefs": "^0.3.1",
"codesandbox": "^2.1.11",
"coloreact": "^0.3.1",
"copy-to-clipboard": "^3.2.1",
Expand Down
1 change: 0 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const App = () => {
<DndProvider backend={Backend}>
<Flex h="calc(100vh - 3rem)">
<Sidebar />

<EditorErrorBoundary>
<Box bg="white" flex={1} zIndex={10} position="relative">
<Editor />
Expand Down
5 changes: 4 additions & 1 deletion src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import useDispatch from '../hooks/useDispatch'
import { useSelector } from 'react-redux'
import { getComponents } from '../core/selectors/components'
import { getShowLayout, getShowCode } from '../core/selectors/app'
import HeaderMenu from './HeaderMenu'

const CodeSandboxButton = () => {
const components = useSelector(getComponents)
Expand Down Expand Up @@ -95,6 +96,9 @@ const Header = () => {

<Flex flexGrow={1} justifyContent="space-between" alignItems="center">
<Stack isInline spacing={4} justify="center" align="center">
<Box>
<HeaderMenu />
</Box>
<FormControl>
<Tooltip
zIndex={100}
Expand Down Expand Up @@ -138,7 +142,6 @@ const Header = () => {

<Stack isInline>
<CodeSandboxButton />

<Popover>
{({ onClose }) => (
<>
Expand Down
96 changes: 96 additions & 0 deletions src/components/HeaderMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React, { memo } from 'react'
import {
Box,
Button,
LightMode,
Menu,
MenuButton,
MenuList,
MenuItem,
MenuDivider,
LinkProps,
MenuItemProps,
MenuButtonProps,
ButtonProps,
} from '@chakra-ui/core'
import useDispatch from '../hooks/useDispatch'
import { loadFromJSON, saveAsJSON } from '../utils/import'
import { useSelector } from 'react-redux'
import { getComponents } from '../core/selectors/components'
import { FaBomb, FaSave } from 'react-icons/fa'
import { GoRepo } from 'react-icons/go'
import { FiUpload } from 'react-icons/fi'

type MenuItemLinkProps = MenuItemProps | LinkProps

// Ignore because of AS typing issues
// @ts-ignore
const MenuItemLink: React.FC<MenuItemLinkProps> = React.forwardRef(
(props, ref: React.Ref<HTMLLinkElement>) => {
// @ts-ignore
return <MenuItem ref={ref} as="a" {...props} />
},
)

// @ts-ignore
const CustomMenuButton: React.FC<
MenuButtonProps | ButtonProps
> = React.forwardRef((props, ref: React.Ref<HTMLLinkElement>) => {
// @ts-ignore
return <MenuButton as={Button} {...props} />
})

const ExportMenuItem = () => {
const components = useSelector(getComponents)

return (
<MenuItem onClick={() => saveAsJSON(components)}>
<Box mr={2} as={FaSave} />
Save components
</MenuItem>
)
}
const HeaderMenu = () => {
const dispatch = useDispatch()

return (
<Menu>
<CustomMenuButton
rightIcon="chevron-down"
as={Button}
size="xs"
variant="ghost"
variantColor="gray"
>
Editor
</CustomMenuButton>
<LightMode>
<MenuList zIndex={100}>
<ExportMenuItem />
<MenuItem
onClick={async () => {
const components = await loadFromJSON()
dispatch.components.reset(components)
}}
>
<Box mr={2} as={FiUpload} />
Import components
</MenuItem>

<MenuDivider />

<MenuItemLink isExternal href="https://chakra-ui.com/getting-started">
<Box mr={2} as={GoRepo} />
Chakra UI Docs
</MenuItemLink>
<MenuItemLink href="https://github.com/premieroctet/openchakra/issues">
<Box mr={2} as={FaBomb} />
Report issue
</MenuItemLink>
</MenuList>
</LightMode>
</Menu>
)
}

export default memo(HeaderMenu)
4 changes: 2 additions & 2 deletions src/components/inspector/controls/ColorsControl.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { ReactNode, useState, memo } from 'react'
import {
theme,
Popover,
PopoverTrigger,
PopoverContent,
Expand All @@ -20,6 +19,7 @@ import {
TabPanels,
TabPanel,
Input,
useTheme,
} from '@chakra-ui/core'
import FormControl from './FormControl'
import { useForm } from '../../../hooks/useForm'
Expand All @@ -39,6 +39,7 @@ const ColorsControl = (props: ColorControlPropsType) => {
const { setValue, setValueFromEvent } = useForm()
const [hue, setHue] = useState(500)
const value = usePropsSelector(props.name)
const theme = useTheme()

const themeColors: any = omit(theme.colors, [
'transparent',
Expand Down Expand Up @@ -115,7 +116,6 @@ const ColorsControl = (props: ColorControlPropsType) => {
{props.label}
</IconButton>
</PopoverTrigger>

<PopoverContent width="200px" zIndex={theme.zIndices.modal}>
<PopoverArrow />
<PopoverBody>
Expand Down
1 change: 0 additions & 1 deletion src/core/models/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ const app = createModel({
inputTextFocused: !state.inputTextFocused,
}
},

setOverlay(state: AppState, overlay: Overlay | undefined): AppState {
return {
...state,
Expand Down
1 change: 1 addition & 0 deletions src/react-app-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference types="react-scripts" />;
declare module 'prettier/standalone'
declare module 'coloreact'
declare module 'browser-nativefs'

type ComponentType =
| 'AspectRatioBox'
Expand Down
39 changes: 39 additions & 0 deletions src/utils/import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { fileOpen, fileSave } from 'browser-nativefs'
import { INITIAL_COMPONENTS } from '../core/models/components'

export async function loadFromJSON() {
const blob = await fileOpen({
extensions: ['json'],
mimeTypes: ['application/json'],
})

const contents: string = await new Promise(resolve => {
const reader = new FileReader()
reader.readAsText(blob, 'utf8')
reader.onloadend = () => {
if (reader.readyState === FileReader.DONE) {
resolve(reader.result as string)
}
}
})

try {
return JSON.parse(contents)
} catch (error) {}

return INITIAL_COMPONENTS
}

export async function saveAsJSON(components: IComponents) {
const serialized = JSON.stringify(components)
const name = `components.json`

await fileSave(
new Blob([serialized], { type: 'application/json' }),
{
fileName: name,
description: 'Excalidraw file',
},
(window as any).handle,
)
}
78 changes: 58 additions & 20 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -930,13 +930,20 @@
dependencies:
regenerator-runtime "^0.13.2"

"@babel/runtime@^7.3.1", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6":
"@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.3.tgz#0811944f73a6c926bb2ad35e918dcc1bfab279f1"
integrity sha512-fVHx1rzEmwB130VTkLnxR+HmxcTjGzH12LYQcFFoBwakMd3aOMD4OsRN7tGG/UOYE2ektgFrS8uACAoRk1CY0w==
dependencies:
regenerator-runtime "^0.13.2"

"@babel/runtime@^7.8.3":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==
dependencies:
regenerator-runtime "^0.13.2"

"@babel/template@^7.4.0", "@babel/template@^7.7.4":
version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b"
Expand Down Expand Up @@ -1731,19 +1738,20 @@
pretty-format "^24.9.0"
wait-for-expect "^3.0.0"

"@testing-library/jest-dom@^4.2.4":
version "4.2.4"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-4.2.4.tgz#00dfa0cbdd837d9a3c2a7f3f0a248ea6e7b89742"
integrity sha512-j31Bn0rQo12fhCWOUWy9fl7wtqkp7In/YP2p5ZFyRuiiB9Qs3g+hS4gAmDWONbAHcRmVooNJ5eOHQDCOmUFXHg==
"@testing-library/jest-dom@^5.1.1":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.1.1.tgz#e88a5c08f9b9f36b384f948a0532eae2abbc8204"
integrity sha512-7xnmBFcUmmUVAUhFiZ/u3CxFh1e46THAwra4SiiKNCW4By26RedCRwEk0rtleFPZG0wlTSNOKDvJjWYy93dp0w==
dependencies:
"@babel/runtime" "^7.5.1"
chalk "^2.4.1"
css "^2.2.3"
"@babel/runtime" "^7.8.3"
"@types/testing-library__jest-dom" "^5.0.0"
chalk "^3.0.0"
css "^2.2.4"
css.escape "^1.5.1"
jest-diff "^24.0.0"
jest-matcher-utils "^24.0.0"
lodash "^4.17.11"
pretty-format "^24.0.0"
jest-diff "^25.1.0"
jest-matcher-utils "^25.1.0"
lodash "^4.17.15"
pretty-format "^25.1.0"
redent "^3.0.0"

"@testing-library/react@^9.4.0":
Expand All @@ -1755,10 +1763,10 @@
"@testing-library/dom" "^6.11.0"
"@types/testing-library__react" "^9.1.2"

"@testing-library/user-event@^7.1.2":
version "7.2.1"
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-7.2.1.tgz#2ad4e844175a3738cb9e7064be5ea070b8863a1c"
integrity sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA==
"@testing-library/user-event@^8.1.0":
version "8.1.3"
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-8.1.3.tgz#f1d803da6fafa6c6b89392f3b8da9ae2df1a419d"
integrity sha512-l8IX2Zs6cLZgwJNmBJaJT2yvstwiNi8kKyO+USrZWJV6DSyUlrWfgWSSic8YLiOHLWUNRLJBOPN43nxTKHXKfg==

"@types/babel__core@^7.1.0":
version "7.1.3"
Expand Down Expand Up @@ -1845,6 +1853,14 @@
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-lib-report" "*"

"@types/jest@*":
version "25.1.3"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.1.3.tgz#9b0b5addebccfb631175870be8ba62182f1bc35a"
integrity sha512-jqargqzyJWgWAJCXX96LBGR/Ei7wQcZBvRv0PLEu9ZByMfcs23keUJrKv9FMR6YZf9YCbfqDqgmY+JUBsnqhrg==
dependencies:
jest-diff "^25.1.0"
pretty-format "^25.1.0"

"@types/jest@^25.1.2":
version "25.1.2"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.1.2.tgz#1c4c8770c27906c7d8def5d2033df9dbd39f60da"
Expand Down Expand Up @@ -1949,6 +1965,13 @@
dependencies:
pretty-format "^24.3.0"

"@types/testing-library__jest-dom@^5.0.0":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.0.1.tgz#cc7f384535a3d9597e27f58d38a795f5c137cc53"
integrity sha512-GiPXQBVF9O4DG9cssD2d266vozBJvC5Tnv6aeH5ujgYJgys1DYm9AFCz7YC+STR5ksGxq3zCt+yP8T1wbk2DFg==
dependencies:
"@types/jest" "*"

"@types/testing-library__react@^9.1.2":
version "9.1.2"
resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-9.1.2.tgz#e33af9124c60a010fc03a34eff8f8a34a75c4351"
Expand Down Expand Up @@ -2968,6 +2991,11 @@ brorand@^1.0.1:
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=

browser-nativefs@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/browser-nativefs/-/browser-nativefs-0.3.1.tgz#17545ca47b1b8912f543f12590f1b1335864c7d7"
integrity sha512-dLcodrZMuyyJvNGJaLKuhPF3uAX/mU2+D3nwbzIc7a1RaNd2enpJhATbq6xZZWeYEvckNRg7KmqgiX+0Ht3zzA==

browser-process-hrtime@^0.1.2:
version "0.1.3"
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4"
Expand Down Expand Up @@ -4085,7 +4113,7 @@ css.escape@^1.5.1:
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=

css@^2.0.0, css@^2.2.3:
css@^2.0.0, css@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==
Expand Down Expand Up @@ -7132,7 +7160,7 @@ jest-config@^24.9.0:
pretty-format "^24.9.0"
realpath-native "^1.1.0"

jest-diff@^24.0.0, jest-diff@^24.9.0:
jest-diff@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da"
integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==
Expand Down Expand Up @@ -7261,7 +7289,7 @@ jest-leak-detector@^24.9.0:
jest-get-type "^24.9.0"
pretty-format "^24.9.0"

jest-matcher-utils@^24.0.0, jest-matcher-utils@^24.9.0:
jest-matcher-utils@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073"
integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==
Expand All @@ -7271,6 +7299,16 @@ jest-matcher-utils@^24.0.0, jest-matcher-utils@^24.9.0:
jest-get-type "^24.9.0"
pretty-format "^24.9.0"

jest-matcher-utils@^25.1.0:
version "25.1.0"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz#fa5996c45c7193a3c24e73066fc14acdee020220"
integrity sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ==
dependencies:
chalk "^3.0.0"
jest-diff "^25.1.0"
jest-get-type "^25.1.0"
pretty-format "^25.1.0"

jest-message-util@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3"
Expand Down Expand Up @@ -10190,7 +10228,7 @@ pretty-error@^2.1.1:
renderkid "^2.0.1"
utila "~0.4"

pretty-format@^24.0.0, pretty-format@^24.3.0, pretty-format@^24.9.0:
pretty-format@^24.3.0, pretty-format@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9"
integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==
Expand Down

0 comments on commit 4cc98d4

Please sign in to comment.