Skip to content

Commit 5890e99

Browse files
author
Maksim Daunarovich
authored
Export project as ZIP file (#87)
* Add file-saver and jszip packages * Create Common components for overlays * Create scaffold for ExportPopup * Add ExportPopup to render * Add project generator files * Add type definitions for file-saver package * Add logic to create zip file from existing models * Update snippets and files with replacement tags * Add new and refactor existing generator code * Add Jest to create tests for generator * Add generator utilities with tests * More utils for generation * Add prettier package * Update deploy contract snippet * More code generation * More generator utilities * More code generation magic * More script and transaction code generation * Complete transaction test generator * Add code to handle contract deployment * Remove unnecessary import * Update unit test template * Update imports for types * Fix babel parser for standalone bundle * Update exporting code * Move all generator related functions to utilities. Update popup styles * Adjust styles of components * Add handlebars package and setup for it * Create Handlebars templates and helpers for contract deployment * Remove unused imports * Add new method that utilizes Handlebars templates * Add temporary browser tests * Create new, update existing and delete unused templates * Remove temporary code * Refactor generator code. Remove unused methods. Add new default values * Update tests * Clean up templates. Fix small bugs in templates
1 parent 63471e2 commit 5890e99

31 files changed

+12427
-5107
lines changed

babel.config.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// babel.config.js
2+
module.exports = {
3+
presets: [
4+
['@babel/preset-env', {targets: {node: 'current'}}],
5+
'@babel/preset-typescript'
6+
]
7+
};

package-lock.json

+11,108-5,011
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+11
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@
1414
"@emotion/core": "^10.0.35",
1515
"@emotion/styled": "^10.0.27",
1616
"@reach/router": "^1.3.4",
17+
"@types/file-saver": "^2.0.1",
1718
"apollo-cache-inmemory": "^1.6.6",
1819
"apollo-client": "^2.6.10",
1920
"apollo-link": "^1.2.14",
2021
"apollo-link-http": "^1.5.17",
2122
"detect-browser": "^5.1.1",
23+
"file-saver": "^2.0.5",
2224
"framer-motion": "^2.6.6",
2325
"graphql": "^15.3.0",
2426
"graphql-tag": "^2.11.0",
27+
"handlebars-loader": "^1.7.1",
2528
"isomorphic-fetch": "^2.2.1",
29+
"jszip": "^3.5.0",
2630
"mixpanel-browser": "^2.39.0",
2731
"monaco-editor": "^0.20.0",
2832
"monaco-languageclient": "^0.13.0",
@@ -38,6 +42,9 @@
3842
"vscode-jsonrpc": "^5.0.1"
3943
},
4044
"devDependencies": {
45+
"@babel/core": "^7.12.10",
46+
"@babel/preset-env": "^7.12.11",
47+
"@babel/preset-typescript": "^7.12.7",
4148
"@graphql-codegen/cli": "^1.17.8",
4249
"@graphql-codegen/fragment-matcher": "^1.17.8",
4350
"@graphql-codegen/introspection": "^1.12.1",
@@ -55,13 +62,17 @@
5562
"@types/styled-components": "^5.1.3",
5663
"@types/theme-ui": "^0.3.6",
5764
"@types/vscode": "^1.48.0",
65+
"babel-jest": "^26.6.3",
5866
"clean-webpack-plugin": "^3.0.0",
5967
"copy-webpack-plugin": "^6.1.0",
6068
"css-loader": "^4.2.2",
6169
"dotenv-webpack": "^2.0.0",
6270
"file-loader": "^6.1.0",
71+
"handlebars": "^4.7.6",
6372
"html-webpack-plugin": "^4.4.1",
73+
"jest": "^26.6.3",
6474
"monaco-editor-webpack-plugin": "^1.9.0",
75+
"prettier": "^2.2.1",
6576
"style-loader": "^1.2.1",
6677
"ts-loader": "^8.0.3",
6778
"typescript": "^4.0.2",

src/components/Arguments/SingleArgument/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react";
22
import { Argument } from "components/Arguments/types";
3-
import { Container, Input, Label, Type, Error } from "./styles";
3+
import { InputBlock, Input, Label, Type, Error } from "./styles";
44

55
type SingleArgumentProps = {
66
argument: Argument,
@@ -11,7 +11,7 @@ type SingleArgumentProps = {
1111
const SingleArgument: React.FC<SingleArgumentProps> = ({ argument, error, onChange }) => {
1212
const {name, type} = argument
1313
return (
14-
<Container>
14+
<InputBlock>
1515
<Label>
1616
{name}
1717
<Type>{type}</Type>
@@ -21,7 +21,7 @@ const SingleArgument: React.FC<SingleArgumentProps> = ({ argument, error, onChan
2121
onChange(name,value)
2222
}}/>
2323
{error && <Error>{error}</Error>}
24-
</Container>
24+
</InputBlock>
2525
)
2626
}
2727

src/components/Arguments/SingleArgument/styles.tsx

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import styled from "styled-components"
22

3-
export const Container = styled.div`
3+
interface InpubBlockProps {
4+
mb?: string
5+
}
6+
7+
export const InputBlock = styled.div<InpubBlockProps>`
48
display: flex;
59
flex-direction: column;
10+
margin-bottom: ${({mb = "0"}) => mb};
611
`
712

813
export const Label = styled.p`
914
margin: 0;
10-
font-size: 12px;
11-
margin-bottom: 1em;
12-
color: #000;
13-
font-weight: bold;
15+
font-size: 14px;
1416
margin-bottom: 5px;
17+
color: #000;
1518
`;
1619

1720
export const Type = styled.span`

src/components/Button.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ interface StyledButtonProps {
1313
}
1414

1515
const StyledButton: React.FC<StyledButtonProps> = styled(ThemedButton)`
16+
17+
display: flex;
18+
align-items: center;
19+
justify-content: center;
20+
1621
@keyframes rotating {
1722
from {
1823
transform: rotate(0deg);
@@ -29,7 +34,23 @@ const StyledButton: React.FC<StyledButtonProps> = styled(ThemedButton)`
2934
.loading {
3035
animation: rotating 0.5s linear infinite;
3136
}
37+
38+
&.violet{
39+
background-color: #BDC4F4;
40+
color: #575E89;
41+
}
42+
43+
&.grey{
44+
background-color: #EDEDED;
45+
color: #696969;
46+
}
3247
48+
&.modal{
49+
width: 100px;
50+
font-size: 16px;
51+
font-weight: bold;
52+
}
53+
3354
display: flex;
3455
align-items: center;
3556

src/components/Common.tsx

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import styled from '@emotion/styled';
2+
3+
interface FullScreenContainerProps {
4+
elevation?: number;
5+
}
6+
7+
export const FullScreenContainer = styled.div<FullScreenContainerProps>`
8+
display: flex;
9+
align-items: center;
10+
justify-content: center;
11+
z-index: ${({ elevation = 0 }) => elevation};
12+
top: 0;
13+
left: 0;
14+
bottom: 0;
15+
right: 0;
16+
position: fixed;
17+
`;
18+
19+
export const WhiteOverlay = styled.div`
20+
position: absolute;
21+
top: 0;
22+
left: 0;
23+
bottom: 0;
24+
right: 0;
25+
background: rgba(255, 255, 255, 0.9);
26+
z-index: -1;
27+
`;
28+
29+
interface PopupContainerProps {
30+
width?: string;
31+
}
32+
33+
export const PopupContainer = styled.div<PopupContainerProps>`
34+
display: flex;
35+
width: ${({ width }) => width};
36+
max-width: 30%;
37+
flex-direction: column;
38+
padding: 20px;
39+
border-radius: 6px;
40+
background-color: white;
41+
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.1), 0 0 3px 1px rgba(0,0,0,0.05);
42+
z-index: 2;
43+
box-sizing: border-box;
44+
`;
45+
46+
interface CommonProps {
47+
mb?: string;
48+
color?: string;
49+
lineColor?: string;
50+
}
51+
52+
export const PopupHeader = styled.h3<CommonProps>`
53+
font-size: 14px;
54+
font-weight: bold;
55+
text-transform: uppercase;
56+
color: ${({ color = 'inherit' }) => color};
57+
margin-bottom: ${({ mb = '0' }) => mb};
58+
:after{
59+
content: "";
60+
display: block;
61+
height: 3px;
62+
width: 16px;
63+
background-color: ${({lineColor = "currentColor"}) => lineColor};
64+
margin-top: 4px;
65+
}
66+
`;
67+
68+
export const SpaceBetween = styled.div<CommonProps>`
69+
display: flex;
70+
width: 100%;
71+
justify-content: space-between;
72+
margin-bottom: ${({ mb = '0' }) => mb};
73+
`;
74+
75+
export const Separator = styled.div`
76+
width: 2px;
77+
height: 30px;
78+
background-color: #ccc;
79+
margin: 0 16px;
80+
`;

src/components/Examples.tsx

+2-11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Text } from "theme-ui";
33
import styled from "@emotion/styled";
44
import { motion } from "framer-motion";
55
import { IoMdClose } from "react-icons/io";
6+
import { WhiteOverlay } from "components/Common";
67

78
import Mixpanel from "../util/mixpanel";
89

@@ -71,16 +72,6 @@ const ExamplesContainer = styled(motion.div)`
7172
}
7273
`;
7374

74-
const Background = styled.div`
75-
position: absolute;
76-
top: 0;
77-
left: 0;
78-
bottom: 0;
79-
right: 0;
80-
background: rgba(255, 255, 255, 0.95);
81-
z-index: -1;
82-
`;
83-
8475
const Stack = styled.div`
8576
display: flex;
8677
flex-direction: column;
@@ -197,7 +188,7 @@ const Examples: React.FC<{
197188
animate={visible ? "visible" : "hidden"}
198189
variants={ExampleContainers}
199190
>
200-
<Background onClick={triggerClose} />
191+
<WhiteOverlay onClick={triggerClose} />
201192
<Stack>
202193
<Header>
203194
<h3>Playground Tutorials</h3>

src/components/ExportPopup.tsx

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import React, { useState, useRef, useEffect } from 'react';
2+
import { useProject } from 'providers/Project/projectHooks';
3+
import { default as FlowButton } from 'components/Button';
4+
5+
import {
6+
FullScreenContainer,
7+
PopupContainer,
8+
PopupHeader,
9+
WhiteOverlay,
10+
SpaceBetween,
11+
} from 'components/Common';
12+
13+
import { createZip } from '../util/generator';
14+
15+
import {
16+
Input,
17+
InputBlock,
18+
Label,
19+
} from 'components/Arguments/SingleArgument/styles';
20+
21+
const ExportPopup: React.FC<{
22+
visible: boolean;
23+
triggerClose?: (e: React.SyntheticEvent) => any;
24+
}> = ({ visible, triggerClose }) => {
25+
const { project } = useProject();
26+
const [processing, setProcessing] = useState(false);
27+
const [projectName, setProjectName] = useState(`project-${project.id}`);
28+
const [folderName, setFolderName] = useState('cadence');
29+
30+
const firstInput = useRef<HTMLInputElement>(null!);
31+
32+
useEffect(() => {
33+
firstInput.current?.focus();
34+
}, [firstInput.current]);
35+
36+
return (
37+
visible && (
38+
<FullScreenContainer elevation={15}>
39+
<PopupContainer width="350px">
40+
<PopupHeader mb="20px" color="#575E89" lineColor="#B4BEFC">
41+
Export Project
42+
</PopupHeader>
43+
<InputBlock mb={'12px'}>
44+
<Label>Project Name</Label>
45+
<Input
46+
ref={firstInput}
47+
value={projectName}
48+
onChange={(event) => setProjectName(event.target.value)}
49+
/>
50+
</InputBlock>
51+
<InputBlock mb={'30px'}>
52+
<Label>Cadence Folder</Label>
53+
<Input
54+
value={folderName}
55+
onChange={(event) => setFolderName(event.target.value)}
56+
/>
57+
</InputBlock>
58+
{processing ? (
59+
<p>Processing...</p>
60+
) : (
61+
<SpaceBetween>
62+
<FlowButton className="grey modal" onClick={triggerClose}>
63+
Close
64+
</FlowButton>
65+
<FlowButton
66+
className="violet modal"
67+
onClick={async () => {
68+
setProcessing(true);
69+
await createZip(folderName, projectName, project);
70+
setProcessing(false);
71+
triggerClose(null);
72+
}}
73+
>
74+
Export
75+
</FlowButton>
76+
</SpaceBetween>
77+
)}
78+
</PopupContainer>
79+
<WhiteOverlay onClick={triggerClose} />
80+
</FullScreenContainer>
81+
)
82+
);
83+
};
84+
85+
export default ExportPopup;

0 commit comments

Comments
 (0)