Skip to content

Commit 4cc98d4

Browse files
authored
Add import export feature (#78)
1 parent c8c29e9 commit 4cc98d4

File tree

9 files changed

+201
-25
lines changed

9 files changed

+201
-25
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"@reach/combobox": "^0.7.3",
1313
"@rehooks/local-storage": "^2.1.1",
1414
"@rematch/core": "^1.3.0",
15+
"browser-nativefs": "^0.3.1",
1516
"codesandbox": "^2.1.11",
1617
"coloreact": "^0.3.1",
1718
"copy-to-clipboard": "^3.2.1",

src/App.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const App = () => {
2727
<DndProvider backend={Backend}>
2828
<Flex h="calc(100vh - 3rem)">
2929
<Sidebar />
30-
3130
<EditorErrorBoundary>
3231
<Box bg="white" flex={1} zIndex={10} position="relative">
3332
<Editor />

src/components/Header.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import useDispatch from '../hooks/useDispatch'
2828
import { useSelector } from 'react-redux'
2929
import { getComponents } from '../core/selectors/components'
3030
import { getShowLayout, getShowCode } from '../core/selectors/app'
31+
import HeaderMenu from './HeaderMenu'
3132

3233
const CodeSandboxButton = () => {
3334
const components = useSelector(getComponents)
@@ -95,6 +96,9 @@ const Header = () => {
9596

9697
<Flex flexGrow={1} justifyContent="space-between" alignItems="center">
9798
<Stack isInline spacing={4} justify="center" align="center">
99+
<Box>
100+
<HeaderMenu />
101+
</Box>
98102
<FormControl>
99103
<Tooltip
100104
zIndex={100}
@@ -138,7 +142,6 @@ const Header = () => {
138142

139143
<Stack isInline>
140144
<CodeSandboxButton />
141-
142145
<Popover>
143146
{({ onClose }) => (
144147
<>

src/components/HeaderMenu.tsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import React, { memo } from 'react'
2+
import {
3+
Box,
4+
Button,
5+
LightMode,
6+
Menu,
7+
MenuButton,
8+
MenuList,
9+
MenuItem,
10+
MenuDivider,
11+
LinkProps,
12+
MenuItemProps,
13+
MenuButtonProps,
14+
ButtonProps,
15+
} from '@chakra-ui/core'
16+
import useDispatch from '../hooks/useDispatch'
17+
import { loadFromJSON, saveAsJSON } from '../utils/import'
18+
import { useSelector } from 'react-redux'
19+
import { getComponents } from '../core/selectors/components'
20+
import { FaBomb, FaSave } from 'react-icons/fa'
21+
import { GoRepo } from 'react-icons/go'
22+
import { FiUpload } from 'react-icons/fi'
23+
24+
type MenuItemLinkProps = MenuItemProps | LinkProps
25+
26+
// Ignore because of AS typing issues
27+
// @ts-ignore
28+
const MenuItemLink: React.FC<MenuItemLinkProps> = React.forwardRef(
29+
(props, ref: React.Ref<HTMLLinkElement>) => {
30+
// @ts-ignore
31+
return <MenuItem ref={ref} as="a" {...props} />
32+
},
33+
)
34+
35+
// @ts-ignore
36+
const CustomMenuButton: React.FC<
37+
MenuButtonProps | ButtonProps
38+
> = React.forwardRef((props, ref: React.Ref<HTMLLinkElement>) => {
39+
// @ts-ignore
40+
return <MenuButton as={Button} {...props} />
41+
})
42+
43+
const ExportMenuItem = () => {
44+
const components = useSelector(getComponents)
45+
46+
return (
47+
<MenuItem onClick={() => saveAsJSON(components)}>
48+
<Box mr={2} as={FaSave} />
49+
Save components
50+
</MenuItem>
51+
)
52+
}
53+
const HeaderMenu = () => {
54+
const dispatch = useDispatch()
55+
56+
return (
57+
<Menu>
58+
<CustomMenuButton
59+
rightIcon="chevron-down"
60+
as={Button}
61+
size="xs"
62+
variant="ghost"
63+
variantColor="gray"
64+
>
65+
Editor
66+
</CustomMenuButton>
67+
<LightMode>
68+
<MenuList zIndex={100}>
69+
<ExportMenuItem />
70+
<MenuItem
71+
onClick={async () => {
72+
const components = await loadFromJSON()
73+
dispatch.components.reset(components)
74+
}}
75+
>
76+
<Box mr={2} as={FiUpload} />
77+
Import components
78+
</MenuItem>
79+
80+
<MenuDivider />
81+
82+
<MenuItemLink isExternal href="https://chakra-ui.com/getting-started">
83+
<Box mr={2} as={GoRepo} />
84+
Chakra UI Docs
85+
</MenuItemLink>
86+
<MenuItemLink href="https://github.com/premieroctet/openchakra/issues">
87+
<Box mr={2} as={FaBomb} />
88+
Report issue
89+
</MenuItemLink>
90+
</MenuList>
91+
</LightMode>
92+
</Menu>
93+
)
94+
}
95+
96+
export default memo(HeaderMenu)

src/components/inspector/controls/ColorsControl.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { ReactNode, useState, memo } from 'react'
22
import {
3-
theme,
43
Popover,
54
PopoverTrigger,
65
PopoverContent,
@@ -20,6 +19,7 @@ import {
2019
TabPanels,
2120
TabPanel,
2221
Input,
22+
useTheme,
2323
} from '@chakra-ui/core'
2424
import FormControl from './FormControl'
2525
import { useForm } from '../../../hooks/useForm'
@@ -39,6 +39,7 @@ const ColorsControl = (props: ColorControlPropsType) => {
3939
const { setValue, setValueFromEvent } = useForm()
4040
const [hue, setHue] = useState(500)
4141
const value = usePropsSelector(props.name)
42+
const theme = useTheme()
4243

4344
const themeColors: any = omit(theme.colors, [
4445
'transparent',
@@ -115,7 +116,6 @@ const ColorsControl = (props: ColorControlPropsType) => {
115116
{props.label}
116117
</IconButton>
117118
</PopoverTrigger>
118-
119119
<PopoverContent width="200px" zIndex={theme.zIndices.modal}>
120120
<PopoverArrow />
121121
<PopoverBody>

src/core/models/app.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ const app = createModel({
3535
inputTextFocused: !state.inputTextFocused,
3636
}
3737
},
38-
3938
setOverlay(state: AppState, overlay: Overlay | undefined): AppState {
4039
return {
4140
...state,

src/react-app-env.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/// <reference types="react-scripts" />;
22
declare module 'prettier/standalone'
33
declare module 'coloreact'
4+
declare module 'browser-nativefs'
45

56
type ComponentType =
67
| 'AspectRatioBox'

src/utils/import.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { fileOpen, fileSave } from 'browser-nativefs'
2+
import { INITIAL_COMPONENTS } from '../core/models/components'
3+
4+
export async function loadFromJSON() {
5+
const blob = await fileOpen({
6+
extensions: ['json'],
7+
mimeTypes: ['application/json'],
8+
})
9+
10+
const contents: string = await new Promise(resolve => {
11+
const reader = new FileReader()
12+
reader.readAsText(blob, 'utf8')
13+
reader.onloadend = () => {
14+
if (reader.readyState === FileReader.DONE) {
15+
resolve(reader.result as string)
16+
}
17+
}
18+
})
19+
20+
try {
21+
return JSON.parse(contents)
22+
} catch (error) {}
23+
24+
return INITIAL_COMPONENTS
25+
}
26+
27+
export async function saveAsJSON(components: IComponents) {
28+
const serialized = JSON.stringify(components)
29+
const name = `components.json`
30+
31+
await fileSave(
32+
new Blob([serialized], { type: 'application/json' }),
33+
{
34+
fileName: name,
35+
description: 'Excalidraw file',
36+
},
37+
(window as any).handle,
38+
)
39+
}

yarn.lock

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -930,13 +930,20 @@
930930
dependencies:
931931
regenerator-runtime "^0.13.2"
932932

933-
"@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":
933+
"@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":
934934
version "7.8.3"
935935
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.3.tgz#0811944f73a6c926bb2ad35e918dcc1bfab279f1"
936936
integrity sha512-fVHx1rzEmwB130VTkLnxR+HmxcTjGzH12LYQcFFoBwakMd3aOMD4OsRN7tGG/UOYE2ektgFrS8uACAoRk1CY0w==
937937
dependencies:
938938
regenerator-runtime "^0.13.2"
939939

940+
"@babel/runtime@^7.8.3":
941+
version "7.8.4"
942+
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
943+
integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==
944+
dependencies:
945+
regenerator-runtime "^0.13.2"
946+
940947
"@babel/template@^7.4.0", "@babel/template@^7.7.4":
941948
version "7.7.4"
942949
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b"
@@ -1731,19 +1738,20 @@
17311738
pretty-format "^24.9.0"
17321739
wait-for-expect "^3.0.0"
17331740

1734-
"@testing-library/jest-dom@^4.2.4":
1735-
version "4.2.4"
1736-
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-4.2.4.tgz#00dfa0cbdd837d9a3c2a7f3f0a248ea6e7b89742"
1737-
integrity sha512-j31Bn0rQo12fhCWOUWy9fl7wtqkp7In/YP2p5ZFyRuiiB9Qs3g+hS4gAmDWONbAHcRmVooNJ5eOHQDCOmUFXHg==
1741+
"@testing-library/jest-dom@^5.1.1":
1742+
version "5.1.1"
1743+
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.1.1.tgz#e88a5c08f9b9f36b384f948a0532eae2abbc8204"
1744+
integrity sha512-7xnmBFcUmmUVAUhFiZ/u3CxFh1e46THAwra4SiiKNCW4By26RedCRwEk0rtleFPZG0wlTSNOKDvJjWYy93dp0w==
17381745
dependencies:
1739-
"@babel/runtime" "^7.5.1"
1740-
chalk "^2.4.1"
1741-
css "^2.2.3"
1746+
"@babel/runtime" "^7.8.3"
1747+
"@types/testing-library__jest-dom" "^5.0.0"
1748+
chalk "^3.0.0"
1749+
css "^2.2.4"
17421750
css.escape "^1.5.1"
1743-
jest-diff "^24.0.0"
1744-
jest-matcher-utils "^24.0.0"
1745-
lodash "^4.17.11"
1746-
pretty-format "^24.0.0"
1751+
jest-diff "^25.1.0"
1752+
jest-matcher-utils "^25.1.0"
1753+
lodash "^4.17.15"
1754+
pretty-format "^25.1.0"
17471755
redent "^3.0.0"
17481756

17491757
"@testing-library/react@^9.4.0":
@@ -1755,10 +1763,10 @@
17551763
"@testing-library/dom" "^6.11.0"
17561764
"@types/testing-library__react" "^9.1.2"
17571765

1758-
"@testing-library/user-event@^7.1.2":
1759-
version "7.2.1"
1760-
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-7.2.1.tgz#2ad4e844175a3738cb9e7064be5ea070b8863a1c"
1761-
integrity sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA==
1766+
"@testing-library/user-event@^8.1.0":
1767+
version "8.1.3"
1768+
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-8.1.3.tgz#f1d803da6fafa6c6b89392f3b8da9ae2df1a419d"
1769+
integrity sha512-l8IX2Zs6cLZgwJNmBJaJT2yvstwiNi8kKyO+USrZWJV6DSyUlrWfgWSSic8YLiOHLWUNRLJBOPN43nxTKHXKfg==
17621770

17631771
"@types/babel__core@^7.1.0":
17641772
version "7.1.3"
@@ -1845,6 +1853,14 @@
18451853
"@types/istanbul-lib-coverage" "*"
18461854
"@types/istanbul-lib-report" "*"
18471855

1856+
"@types/jest@*":
1857+
version "25.1.3"
1858+
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.1.3.tgz#9b0b5addebccfb631175870be8ba62182f1bc35a"
1859+
integrity sha512-jqargqzyJWgWAJCXX96LBGR/Ei7wQcZBvRv0PLEu9ZByMfcs23keUJrKv9FMR6YZf9YCbfqDqgmY+JUBsnqhrg==
1860+
dependencies:
1861+
jest-diff "^25.1.0"
1862+
pretty-format "^25.1.0"
1863+
18481864
"@types/jest@^25.1.2":
18491865
version "25.1.2"
18501866
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.1.2.tgz#1c4c8770c27906c7d8def5d2033df9dbd39f60da"
@@ -1949,6 +1965,13 @@
19491965
dependencies:
19501966
pretty-format "^24.3.0"
19511967

1968+
"@types/testing-library__jest-dom@^5.0.0":
1969+
version "5.0.1"
1970+
resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.0.1.tgz#cc7f384535a3d9597e27f58d38a795f5c137cc53"
1971+
integrity sha512-GiPXQBVF9O4DG9cssD2d266vozBJvC5Tnv6aeH5ujgYJgys1DYm9AFCz7YC+STR5ksGxq3zCt+yP8T1wbk2DFg==
1972+
dependencies:
1973+
"@types/jest" "*"
1974+
19521975
"@types/testing-library__react@^9.1.2":
19531976
version "9.1.2"
19541977
resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-9.1.2.tgz#e33af9124c60a010fc03a34eff8f8a34a75c4351"
@@ -2968,6 +2991,11 @@ brorand@^1.0.1:
29682991
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
29692992
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
29702993

2994+
browser-nativefs@^0.3.1:
2995+
version "0.3.1"
2996+
resolved "https://registry.yarnpkg.com/browser-nativefs/-/browser-nativefs-0.3.1.tgz#17545ca47b1b8912f543f12590f1b1335864c7d7"
2997+
integrity sha512-dLcodrZMuyyJvNGJaLKuhPF3uAX/mU2+D3nwbzIc7a1RaNd2enpJhATbq6xZZWeYEvckNRg7KmqgiX+0Ht3zzA==
2998+
29712999
browser-process-hrtime@^0.1.2:
29723000
version "0.1.3"
29733001
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4"
@@ -4085,7 +4113,7 @@ css.escape@^1.5.1:
40854113
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
40864114
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
40874115

4088-
css@^2.0.0, css@^2.2.3:
4116+
css@^2.0.0, css@^2.2.4:
40894117
version "2.2.4"
40904118
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
40914119
integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==
@@ -7132,7 +7160,7 @@ jest-config@^24.9.0:
71327160
pretty-format "^24.9.0"
71337161
realpath-native "^1.1.0"
71347162

7135-
jest-diff@^24.0.0, jest-diff@^24.9.0:
7163+
jest-diff@^24.9.0:
71367164
version "24.9.0"
71377165
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da"
71387166
integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==
@@ -7261,7 +7289,7 @@ jest-leak-detector@^24.9.0:
72617289
jest-get-type "^24.9.0"
72627290
pretty-format "^24.9.0"
72637291

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

7302+
jest-matcher-utils@^25.1.0:
7303+
version "25.1.0"
7304+
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz#fa5996c45c7193a3c24e73066fc14acdee020220"
7305+
integrity sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ==
7306+
dependencies:
7307+
chalk "^3.0.0"
7308+
jest-diff "^25.1.0"
7309+
jest-get-type "^25.1.0"
7310+
pretty-format "^25.1.0"
7311+
72747312
jest-message-util@^24.9.0:
72757313
version "24.9.0"
72767314
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3"
@@ -10190,7 +10228,7 @@ pretty-error@^2.1.1:
1019010228
renderkid "^2.0.1"
1019110229
utila "~0.4"
1019210230

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

0 commit comments

Comments
 (0)