Skip to content

Commit abcab6d

Browse files
committed
feat: Implemented copy source functionality
1 parent a00e092 commit abcab6d

4 files changed

Lines changed: 200 additions & 43 deletions

File tree

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/ExportShape.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,6 @@ import download from "downloadjs";
3636
// misc utilities
3737
import { getShapeId } from "../../utils/misc";
3838

39-
// Radios
40-
import { Radios } from "..";
41-
4239
// Component
4340
const ExportShape = ({ show, setShow, shape }) => {
4441
console.log({ shape });

components/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export { default as App } from "./core/App";
33
export { default as Landing } from "./core/Landing";
44
export { default as SignInModal } from "./core/SignInModal";
55
export { default as ExportShape } from "./core/ExportShape";
6+
export {default as CopyShapeSource} from "./core/CopyShapeSource";
67

78
// utils
89
export { default as Header } from "./utils/Header";

components/utils/ShapeList.js

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { BiExport } from "react-icons/bi";
2828
import { BsFillHeartFill, BsHeart} from "react-icons/bs";
2929

3030
// Export Shape
31-
import { ExportShape } from '..';
31+
import { ExportShape, CopyShapeSource } from '..';
3232

3333
// misc unitless
3434
import { getShapeFileName, getShapeId } from '../../utils/misc';
@@ -209,12 +209,19 @@ const ShapeList = (
209209
return shapeName.includes(searchTerm.toLowerCase());
210210
})
211211
}
212-
212+
// All shapes
213213
const [shapes, setShapes] = useState(data);
214+
// filtered shapes as state
214215
const [filteredShape, setFilteredShape] = useState(shapes);
216+
217+
// All about export shape states
215218
const [showExportModal, setShowExportModal] = useState(false);
216219
const [shapeToExport, setShapeToExport] = useState();
217220

221+
// All about copy source states
222+
const [showCopySourceModal, setCopySourceModal] = useState(false);
223+
const [shapeToSourceCopy, setShapeToSourceCopy] = useState();
224+
218225
useEffect(() =>{
219226
const copy = [...shapes];
220227
if(sort === 'recent') {
@@ -242,17 +249,11 @@ const ShapeList = (
242249
};
243250

244251
/**
245-
* Copy the clip-path value to clipboard
252+
* Method to execute when use clicks on the copy source
246253
*/
247-
async function performCopy(event, formula) {
248-
event.preventDefault();
249-
try {
250-
await navigator.clipboard.writeText(formula);
251-
toast.success("Successfully Copied!");
252-
console.log("The clip-path formula copied to clipboard");
253-
} catch (err) {
254-
console.error("Failed to copy: ", err);
255-
}
254+
async function performCopySource(shape) {
255+
setShapeToSourceCopy(shape);
256+
setCopySourceModal(true);
256257
}
257258

258259
/**
@@ -355,6 +356,12 @@ const ShapeList = (
355356
setShow={ setShowExportModal }
356357
shape = { shapeToExport } /> }
357358

359+
{ shapeToSourceCopy && <CopyShapeSource
360+
show= {showCopySourceModal}
361+
setShow={ setShapeToSourceCopy }
362+
shape= { shapeToSourceCopy } />
363+
}
364+
358365
{filteredShape.map((shape, index) => (
359366
<React.Fragment key={index}>
360367
<ShapeCard>
@@ -392,10 +399,14 @@ const ShapeList = (
392399
}
393400

394401
</span>{" "}
395-
<Button title="Export" variant="outline-light" onClick={() => performExport(shape)} className="btn-icon">
402+
<Button title="Export Shape" variant="outline-light" onClick={() => performExport(shape)} className="btn-icon">
396403
<ExportIcon
397404
size={24} />
398405
</Button>
406+
<Button title="Copy Source" variant="outline-light" onClick={() => performCopySource(shape)} className="btn-icon">
407+
<CopyIcon
408+
size={24} />
409+
</Button>
399410
</ShapeActionsContainer>
400411
</ShapeActions>
401412
</ShapeCardBody>
@@ -408,33 +419,6 @@ const ShapeList = (
408419
</ShapeCreditsOwner>
409420
</ShapeCredits>
410421
</ShapeCardHeader>
411-
<ShapeCardSwitch>
412-
<label htmlFor={`${getShapeFileName(shape.name)}-form`}>
413-
<span>Show Clip-Path Info</span>{" "}
414-
<Switch
415-
onChange={() => handleSwicth(shape.name)}
416-
checked={shape.showAdvanced}
417-
id={`${getShapeFileName(shape.name)}-form`}
418-
/>
419-
</label>
420-
</ShapeCardSwitch>
421-
422-
{shape.showAdvanced && (
423-
<ShapeDetailsItems>
424-
<span>
425-
<b>Clip-Path:</b>{" "}
426-
<code>
427-
<b>{shape.formula}</b>
428-
</code>
429-
</span>{" "}
430-
<span title="Copy">
431-
<CopyIcon
432-
size={24}
433-
onClick={(event) => performCopy(event, shape.formula)}
434-
/>
435-
</span>
436-
</ShapeDetailsItems>
437-
)}
438422
</ShapeCard>
439423
</React.Fragment>
440424
))}

0 commit comments

Comments
 (0)