Skip to content

Commit beb1899

Browse files
committed
Merge branch 'issue-11-add-functionality-to-create-new-shapes' of https://github.com/TryShape/tryshape into issue-11-add-functionality-to-create-new-shapes
2 parents 0f642bc + ac403ca commit beb1899

11 files changed

Lines changed: 458 additions & 208 deletions

File tree

components/core/App.js

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,22 @@ import Loader from "react-loader-spinner";
1414
import { ShapeList, Header } from '..';
1515

1616
const App = (props) => {
17-
const [data, setData] = useState([]);
18-
const [loading, setLoading] = useState(true);
17+
const [data, setData] = useState([]); // shapes
18+
const [loading, setLoading] = useState(true); // shapes loading
19+
20+
const [searchTerm, setSearchTerm] = useState(""); // search
21+
const [sort, setSort] = useState("recent"); // sort
22+
23+
const [shapeAction, setShapeAction] = useState({
24+
'action': '',
25+
'payload': {}
26+
});
27+
1928
const { user } = props;
2029

2130
useEffect(async () => {
2231
setData([]);
2332
setLoading(true);
24-
2533
let shapes = [];
2634

2735
if(user.length === 0) {
@@ -80,16 +88,21 @@ const App = (props) => {
8088
});
8189

8290
console.log(shapes);
83-
91+
console.log({shapeAction});
8492
await setData(shapes);
93+
console.log(shapes);
8594
setLoading(false);
86-
}, [user]);
87-
88-
95+
}, [user, shapeAction]);
8996

9097
return (
9198
<>
92-
<Header {...props} />
99+
<Header {...props}
100+
searchTerm={searchTerm}
101+
setSearchTerm={setSearchTerm}
102+
sort={sort}
103+
setSort={setSort}
104+
shapeAction={shapeAction}
105+
setShapeAction={setShapeAction} />
93106
{loading ? (
94107
<Loader
95108
style={{margin: '20% auto auto 42%'}}
@@ -99,7 +112,7 @@ const App = (props) => {
99112
width={300}
100113
/>
101114
) : (
102-
<ShapeList {...props} data={ data } />
115+
<ShapeList {...props} data={ data } searchTerm={searchTerm} sort={sort} />
103116
)}
104117
</>
105118
);

components/core/CopyShapeSource.js

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import React, { useState } from "react";
2+
3+
// dynamic from Next.js
4+
import dynamic from "next/dynamic";
5+
6+
// Bootstrap
7+
import Modal from "react-bootstrap/Modal";
8+
import Container from 'react-bootstrap/Container'
9+
import Row from 'react-bootstrap/Row'
10+
import Col from 'react-bootstrap/Col'
11+
import Button from "react-bootstrap/Button";
12+
import Form from 'react-bootstrap/Form';
13+
import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup'
14+
import ToggleButton from 'react-bootstrap/ToggleButton'
15+
16+
// Styled Component
17+
import styled from "styled-components";
18+
19+
// Clip-Path
20+
const Shape = dynamic(import("react-clip-path"), { ssr: false });
21+
22+
const ShapeContainer = styled.section`
23+
border: solid 1px var(--color-neutral-30);
24+
padding: 1rem;
25+
`;
26+
27+
// Toast
28+
import toast from "react-hot-toast";
29+
30+
// icons
31+
import { FiCopy } from 'react-icons/fi';
32+
33+
// misc utilities
34+
import { getShapeId } from "../../utils/misc";
35+
36+
const CSSDisplay = styled.span`
37+
white-space: pre-line;
38+
`;
39+
40+
const CopyIcon = styled(FiCopy)`
41+
cursor: pointer;
42+
`;
43+
44+
const CopyShapeSource = ({ show, setShow, shape }) => {
45+
const [type, setType] = useState('css');
46+
const [selector, setSelector] = useState(shape.name);
47+
48+
console.log(shape);
49+
50+
const handleSelectorChange = evt => {
51+
const value = evt.target.value;
52+
if (!value) {
53+
setSelector(shape.name);
54+
} else {
55+
setSelector(value);
56+
}
57+
}
58+
59+
const getCSSSelector = () => {
60+
61+
return selector.toLowerCase().split(' ').join('-');
62+
}
63+
64+
const getCSS = formula => {
65+
const css = `.${getCSSSelector()} { \n clip-path: ${formula}; \n background-color: #809000; \n width: 300px; \n height: 300px; \n }`;
66+
console.log(css);
67+
return css;
68+
}
69+
70+
const copy = async (formula, css) => {
71+
let text = formula;
72+
if (css) {
73+
text = getCSS(formula);
74+
}
75+
try {
76+
await navigator.clipboard.writeText(text);
77+
toast.success("Successfully Copied!");
78+
console.log("The CSS copied to clipboard");
79+
} catch (err) {
80+
console.error("Failed to copy: ", err);
81+
}
82+
}
83+
84+
return(
85+
<>
86+
{true && (
87+
<Modal
88+
size="lg"
89+
aria-labelledby="contained-modal-title-vcenter"
90+
show={show}
91+
onHide={() => setShow(false)}
92+
centered
93+
>
94+
<Modal.Header closeButton>
95+
<Modal.Title>Copy Source for {shape.name} </Modal.Title>
96+
</Modal.Header>
97+
<Modal.Body>
98+
<Container fluid>
99+
<Row>
100+
<Col>
101+
<Form>
102+
<div>
103+
<Form.Group>
104+
<Form.Label>Export As</Form.Label>
105+
<div>
106+
<ToggleButtonGroup type="radio" name="options" defaultValue={1} variant="outline-dark" size="sm" defaultValue={type}>
107+
<ToggleButton id="tbg-radio-1" value={'css'} variant="outline-dark" onClick={() => setType('css')}>
108+
Show CSS
109+
</ToggleButton>
110+
<ToggleButton id="tbg-radio-2" value={'clip-path'} variant="outline-dark" onClick={() => setType('clip-path')}>
111+
Show Clip-Path
112+
</ToggleButton>
113+
</ToggleButtonGroup>
114+
</div>
115+
</Form.Group>
116+
</div>
117+
{
118+
(type === 'css' && <Form.Group className="mb-3" id="export-name">
119+
<Form.Label>CSS Selector Name</Form.Label>
120+
<Form.Control type="text" name="name" value={selector} onChange={handleSelectorChange}/>
121+
</Form.Group>)
122+
}
123+
{
124+
(type &&
125+
<div>
126+
{type === 'css' &&
127+
<>
128+
<h3>CSS Snippet</h3>
129+
<CSSDisplay>
130+
<code>{getCSS(shape.formula)}</code>
131+
</CSSDisplay>
132+
<CopyIcon size={20} onClick={() => copy(shape.formula, true)}/>
133+
</>
134+
}
135+
{type === 'clip-path' &&
136+
<div>
137+
<h3>Clip-Path Value</h3>
138+
<code>{shape.formula}</code>
139+
<CopyIcon size={20} onClick={() => copy(shape.formula, false)}/>
140+
</div>
141+
}
142+
</div>
143+
)
144+
}
145+
</Form>
146+
</Col>
147+
<Col>
148+
<ShapeContainer>
149+
<Shape
150+
name={shape.name}
151+
formula={shape.formula}
152+
width="300px"
153+
height="300px"
154+
backgroundColor={shape.backgroundColor}
155+
id={getShapeId(shape.name, true)}
156+
/>
157+
</ShapeContainer>
158+
</Col>
159+
</Row>
160+
</Container>
161+
</Modal.Body>
162+
163+
<Modal.Footer>
164+
<Button variant="outline-info" onClick={() => setShow(false)}>
165+
Cancel
166+
</Button>
167+
</Modal.Footer>
168+
</Modal>
169+
)}
170+
</>
171+
)
172+
173+
};
174+
175+
export default CopyShapeSource;

components/core/CreateShape.js

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,25 @@ import Button from "react-bootstrap/Button";
1010
// ShapeForm and ShapePreview Components
1111
import { ShapeForm, ShapePreview } from "..";
1212

13+
// Toast
14+
import toast from "react-hot-toast";
15+
16+
// harperDb fetch call
17+
import { harperFetch } from "../../utils/HarperFetch";
18+
1319
const CreateShape = (props) => {
1420

1521
// shapeInformation holds all information about the shape
1622
const [shapeInformation, setShapeInformation] = useState({
1723
"name": "Tilted Square",
1824
"formula": "polygon(10% 10%, 90% 10%, 90% 90%, 10% 80%)",
1925
"vertices": 4,
26+
"private": false,
2027
"edges": 4,
2128
"notes": "",
2229
"clipPathType": "polygon",
2330
"showShadow": true,
24-
"backgroundColor": "#12a8d6",
31+
"backgroundColor": "#d61284",
2532
"verticeCoordinates" : [
2633
{
2734
"x": "10%",
@@ -47,10 +54,21 @@ const CreateShape = (props) => {
4754
const name = event.target.name || event.type;
4855
const value = event.target.type === "checkbox" ? event.target.checked : event.target.value;
4956

50-
console.log(event, data);
57+
// console.log(event, data);
5158

5259
// If Clip-Path formula value is changed, it makes sure that the parentheses stay there and also alters the verticeCoordinates value
53-
if (name === "formula") {
60+
if (name === "name") {
61+
setShapeInformation({
62+
...shapeInformation,
63+
"name": value,
64+
});
65+
} else if (name === 'private') {
66+
setShapeInformation({
67+
...shapeInformation,
68+
"private": !shapeInformation.private,
69+
70+
});
71+
} else if (name === "formula") {
5472
const edgeVerticeNumber = shapeInformation.clipPathType === "polygon" ? value.split(",").length: 0;
5573

5674
// If user deletes all, set formula to the Clip-Path type with an empty set of parentheses
@@ -186,6 +204,7 @@ const CreateShape = (props) => {
186204
});
187205
}
188206

207+
189208
function addNewVerticeCoordinates(x ,y, number) {
190209
const xPercentage = Math.round((x / 280.0) * 100.0);
191210
const yPercentage = Math.round((y / 280.0) * 100.0);
@@ -212,17 +231,70 @@ const CreateShape = (props) => {
212231
return newFormula;
213232
}
214233

215-
216-
217234
const [validated, setValidated] = useState(false);
218235

219-
const handleSubmit = (event) => {
236+
const handleSubmit = async(event) => {
237+
event.preventDefault();
220238
const form = event.currentTarget;
221239
if (form.checkValidity() === false) {
222240
event.preventDefault();
223241
event.stopPropagation();
224242
}
225243
setValidated(true);
244+
245+
console.log(shapeInformation);
246+
247+
// Create the shape in the DB
248+
const insertShape = await harperFetch({
249+
operation: "sql",
250+
sql: `INSERT into tryshape.shapes(backgroundColor, createdAt, createdBy, edges, email, formula, likes, name, notes, private, type, vertices)
251+
values('${shapeInformation.backgroundColor}', null, '${props.user.email}', ${shapeInformation.edges}, null, '${shapeInformation.formula}', 0, '${shapeInformation.name}', '${shapeInformation.notes}', ${shapeInformation.private}, '${shapeInformation.clipPathType}', ${shapeInformation.vertices})`,
252+
});
253+
254+
console.log(insertShape);
255+
256+
// Create the user in the db
257+
if (insertShape['inserted_hashes'].length > 0) {
258+
// First check if the user exist
259+
const result = await harperFetch({
260+
operation: "sql",
261+
sql: `SELECT count(*) from tryshape.users WHERE email='${props.user.email}'`,
262+
});
263+
const count = (result[0]['COUNT(*)']);
264+
console.log({count});
265+
// If doesn't exist, create in db
266+
if (count === 0) {
267+
const insertUser = await harperFetch({
268+
operation: "sql",
269+
sql: `INSERT into tryshape.users(email, name, photoURL)
270+
values('${props.user.email}', '${props.user.displayName}', '${props.user.photoURL}')`,
271+
});
272+
} else {
273+
console.log(`The user ${props.user.email} present in DB`);
274+
}
275+
}
276+
props.handleClose();
277+
toast.success(`Shape ${shapeInformation.name} created successfully.`);
278+
props.setShapeAction({
279+
...props.shapeAction,
280+
"action": "add",
281+
"payload": {
282+
"backgroundColor": shapeInformation.backgroundColor,
283+
"createdAt": null,
284+
"createdBy": props.user.email,
285+
"edges": shapeInformation.edges,
286+
"email": null,
287+
"email1": props.user.email,
288+
"formula": shapeInformation.formula,
289+
"likes": 0,
290+
"name": shapeInformation.name,
291+
"name1": props.user.displayName,
292+
"notes": shapeInformation.notes,
293+
"photoURL": props.user.photoURL,
294+
"private": shapeInformation.private,
295+
"type": shapeInformation.clipPathType
296+
}
297+
});
226298
}
227299

228300
return(
@@ -235,7 +307,7 @@ const CreateShape = (props) => {
235307
backdrop="static"
236308
>
237309
<Modal.Header closeButton>
238-
<Modal.Title>Create Shape</Modal.Title>
310+
<Modal.Title>Create a Shape</Modal.Title>
239311
</Modal.Header>
240312
<Modal.Body>
241313
<Container fluid>
@@ -261,8 +333,8 @@ const CreateShape = (props) => {
261333
<Button onClick={() => props.handleClose()} variant="outline-info">
262334
Close
263335
</Button>
264-
<Button variant="secondary" type="submit" form="createShapeForm">
265-
Save
336+
<Button variant="secondary" type="submit" form="createShapeForm" disabled={!shapeInformation.name}>
337+
Create
266338
</Button>
267339
</Modal.Footer>
268340
</Modal>

0 commit comments

Comments
 (0)