├── .circleci └── config.yml ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── manifest.json └── robots.txt └── src ├── App.css ├── App.js ├── App.test.js ├── Components ├── AuthorInput │ └── AuthorInput.js ├── ColorBox │ ├── ColorBox.css │ └── ColorBox.js ├── Footer │ ├── Footer.css │ └── Footer.js ├── UserInput │ ├── UserInput.css │ └── UserInput.js ├── UserOutput │ ├── UserOutput.css │ └── UserOutput.js └── index.js ├── index.css ├── index.js ├── logo.svg ├── serviceWorker.js └── setupTests.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | node: circleci/node@3.0.0 4 | workflows: 5 | node-tests: 6 | jobs: 7 | - node/test 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QuoteWithMe 2 | 3 | Live Application:- https://quote-with-me-react.vercel.app/ 4 | 5 | A quote maker App ✒️📝, entirely built on React ⚛️ You can create your quote. You can change the color 🎨 of the background as well as the text color. Also, through the random-quote API 👨‍💻, you can generate random quotes. And finally 🚀, once done, you can download the file in png format 🌠 6 | 7 | ### Key Features 8 | 9 | - :trophy: You can download the quote template. 10 | - :trophy: Change the color of the background as well as text. 11 | - :trophy: Generate a random quote(using API fetch). 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "udemyass1", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.0", 7 | "@testing-library/jest-dom": "^4.2.4", 8 | "@testing-library/react": "^9.5.0", 9 | "@testing-library/user-event": "^7.2.1", 10 | "canvg": "^3.0.6", 11 | "dom-to-image": "^2.6.0", 12 | "file-saver": "^2.0.2", 13 | "html2canvas": "^1.0.0-rc.5", 14 | "material-ui-color-picker": "^3.5.1", 15 | "omggif": "^1.0.10", 16 | "promise-polyfill": "^8.1.3", 17 | "react": "^16.13.1", 18 | "react-dom": "^16.13.1", 19 | "react-scripts": "3.4.1", 20 | "stackblur-canvas": "^2.3.0" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": "react-app" 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kothariji/QuoteWithMe-React/77fe7a1daeb7bca2de3a47f115a5fe1708fdb6fc/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 19 | 20 | 24 | 25 | 29 | 33 | 37 | 38 | Quote Maker 39 | 40 | 53 | 54 | 55 | 56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | display: block; 4 | font-family: "Raleway", sans-serif; 5 | } 6 | 7 | .Colors { 8 | display: inline-flex; 9 | flex-direction: row; 10 | flex-wrap: wrap; 11 | margin-bottom: 30px; 12 | } 13 | 14 | .generate { 15 | margin-top: 150px; 16 | } 17 | 18 | .color-picker { 19 | cursor: pointer; 20 | } 21 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, Fragment } from "react" 2 | import domtoimage from "dom-to-image"; 3 | import { saveAs } from "file-saver" 4 | import ColorPicker from 'material-ui-color-picker' 5 | import { 6 | FormControl, 7 | FormControlLabel, 8 | FormLabel, 9 | Radio, 10 | RadioGroup, 11 | } from "@material-ui/core"; 12 | 13 | 14 | import "./App.css"; 15 | import { 16 | AuthorInput, 17 | ColorBox, 18 | Footer, 19 | UserInput, 20 | UserOutput 21 | } from "./Components/index" 22 | 23 | 24 | const App = () => { 25 | const [textState, setTextState] = useState({ 26 | text: "Chai. Code. Cube.", 27 | }); 28 | 29 | const colors = [ 30 | { code: "#1abc9c" }, 31 | { code: "#2ecc71" }, 32 | { code: "#3498db" }, 33 | { code: "#9b59b6" }, 34 | { code: "#34495e" }, 35 | { code: "#f1c40f" }, 36 | { code: "#e67e22" }, 37 | { code: "#e74c3c" }, 38 | { code: "#ecf0f1" }, 39 | { code: "#050505" }, 40 | ]; 41 | 42 | const [backState, setBackState] = useState({ 43 | color: "#f1c40f", 44 | }); 45 | 46 | const [textColorState, setTextColorState] = useState({ 47 | color: "#000000", 48 | }); 49 | 50 | const [authorTextState, setAuthorTextState] = useState({ 51 | text: "~kothariji", 52 | }); 53 | 54 | const [authorState, setAuthorState] = useState({ 55 | author: true, 56 | }); 57 | 58 | const [radioState, setRadioState] = useState({ 59 | value: "background", 60 | }); 61 | 62 | const handleNameChange = (event) => { 63 | setTextState({ text: event.target.value }); 64 | }; 65 | 66 | const handleAuthorNameChange = (event) => { 67 | setAuthorTextState({ text: event.target.value }); 68 | }; 69 | 70 | const toggleAuthor = () => { 71 | setAuthorState({ author: !authorState.author }); 72 | }; 73 | 74 | const handleColorChange = (colorCode) => { 75 | if (radioState.value === "background") { 76 | setBackState({ 77 | color: colorCode, 78 | }); 79 | } else { 80 | setTextColorState({ 81 | color: colorCode, 82 | }); 83 | } 84 | }; 85 | 86 | const handleRadio = (event) => { 87 | setRadioState({ 88 | value: event.target.value, 89 | }); 90 | }; 91 | 92 | const downloadHandler = (event) => { 93 | event.preventDefault(); 94 | domtoimage.toBlob(document.getElementById("my-node")).then(function (blob) { 95 | saveAs(blob, "myImage.png"); 96 | }); 97 | }; 98 | 99 | const getQuote = () => { 100 | const randomNumber = Math.floor(Math.random() * (1600 - 1 + 1) + 1) 101 | fetch("https://type.fit/api/quotes") 102 | .then((res) => res.json()) 103 | .then( 104 | (result) => { 105 | setTextState({ text: result[randomNumber].text }) 106 | setAuthorTextState({ text: result[randomNumber].author }) 107 | }, 108 | (error) => { 109 | console.log(error); 110 | } 111 | ); 112 | }; 113 | 114 | return ( 115 |
116 |

Quote With Me

117 |
118 |
119 |
120 | 121 | 122 | 128 | {authorState.author && ( 129 | 130 |
131 |
132 |
133 | 137 |
138 | )} 139 |
140 |
141 |
142 | 143 | 144 | 145 |

Select the Option

146 |
147 | 153 | } 156 | label="Background Color" 157 | /> 158 | } 161 | label="Text Color" 162 | /> 163 | 164 |
165 |
166 | {colors.map(({ code }) => ( 167 | handleColorChange(code)} 170 | key={code} 171 | className="color-box" 172 | /> 173 | ))} 174 |
175 |
OR
176 |

177 | Pick a Color 178 |

179 | handleColorChange(color)} 185 | /> 186 |
187 |
188 | 194 | 201 | 207 |
208 |
209 |
210 |
211 |
212 |
214 | ); 215 | }; 216 | 217 | export default App; 218 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/Components/AuthorInput/AuthorInput.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | class AuthorInput extends Component { 4 | render() { 5 | return ( 6 |
7 |
8 | 12 | Author Name 13 | 14 |
15 | 24 |
25 | ); 26 | } 27 | } 28 | 29 | export default AuthorInput; 30 | -------------------------------------------------------------------------------- /src/Components/ColorBox/ColorBox.css: -------------------------------------------------------------------------------- 1 | .ColorBox { 2 | height: 100px; 3 | width: 100px; 4 | transition-duration: 0.3s; 5 | } 6 | 7 | .ColorBox:hover { 8 | height: 110px; 9 | width: 110px; 10 | cursor: pointer; 11 | } 12 | 13 | .ColorText { 14 | color: white; 15 | padding: 35px 15px; 16 | } 17 | -------------------------------------------------------------------------------- /src/Components/ColorBox/ColorBox.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import "./ColorBox.css"; 3 | 4 | class ColorBox extends Component { 5 | render() { 6 | return ( 7 |
12 | {this.props.color === "#ecf0f1" ? ( 13 |
{this.props.color}
14 | ) : ( 15 |
{this.props.color}
16 | )} 17 |
18 | ); 19 | } 20 | } 21 | 22 | export default ColorBox; 23 | -------------------------------------------------------------------------------- /src/Components/Footer/Footer.css: -------------------------------------------------------------------------------- 1 | .contact { 2 | padding-top: 60px; 3 | background-color: black; 4 | height: 300px; 5 | width: 100%; 6 | text-align: center; 7 | font-weight: 800; 8 | color: white; 9 | } 10 | 11 | .conhr { 12 | border: 1px solid whitesmoke; 13 | border-radius: 200px; 14 | width: 500px; 15 | align-content: center; 16 | margin: 10px 0px 10px 315px; 17 | } 18 | 19 | .madewithlove { 20 | margin-top: 60px; 21 | font-family: "Poppins"; 22 | font-weight: 400; 23 | letter-spacing: 1px; 24 | } 25 | 26 | .fa-heart { 27 | color: red; 28 | } 29 | 30 | .name { 31 | font-size: 0.9em; 32 | } 33 | -------------------------------------------------------------------------------- /src/Components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import "./Footer.css"; 3 | 4 | class Footer extends Component { 5 | state = {}; 6 | render() { 7 | return ( 8 |
9 |
10 |
11 |

Thanks For Visiting !

12 |
13 |
14 |

15 | Built with React 16 |

17 |

18 | Designed with by{" "} 19 | 20 | Dhruv Kothari 21 | 22 |

23 |
24 |
25 |
26 | ); 27 | } 28 | } 29 | 30 | export default Footer; 31 | -------------------------------------------------------------------------------- /src/Components/UserInput/UserInput.css: -------------------------------------------------------------------------------- 1 | .UserInput { 2 | margin: 150px auto 60px auto; 3 | } 4 | 5 | .input-group { 6 | border-radius: 5px; 7 | box-shadow: 3px 3px 10px #626262, -3px -3px 12px #ffffff; 8 | } 9 | -------------------------------------------------------------------------------- /src/Components/UserInput/UserInput.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import "./UserInput.css"; 3 | 4 | class UserInput extends Component { 5 | render() { 6 | return ( 7 |
8 |
9 |
10 | 11 | Enter Quote 12 | 13 |
14 | 22 |
23 |
24 | 25 | ); 26 | } 27 | } 28 | 29 | export default UserInput; 30 | -------------------------------------------------------------------------------- /src/Components/UserOutput/UserOutput.css: -------------------------------------------------------------------------------- 1 | #my-node { 2 | padding: 10px; 3 | margin: 15px; 4 | } 5 | .UserOuputWindow { 6 | margin: 40px auto; 7 | width: 480px; 8 | height: 480px; 9 | display: flex; 10 | justify-content: center; 11 | padding: 20px; 12 | } 13 | 14 | .WindowText { 15 | width: inherit; 16 | /*margin: 50px auto;*/ 17 | height: 480px; 18 | font-size: 1.9em; 19 | padding: 175px 25px 200px 25px; 20 | border-radius: 50px; 21 | box-shadow: 12px 12px 24px #626262, -12px -12px 24px #ffffff; 22 | } 23 | -------------------------------------------------------------------------------- /src/Components/UserOutput/UserOutput.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./UserOutput.css"; 3 | 4 | const UserOutput = (props) => { 5 | return ( 6 |
7 |
8 |

15 | {props.text} 16 | {props.showAuthor &&

{props.authorText}
} 17 |

18 |
19 |
20 | ); 21 | }; 22 | 23 | export default UserOutput; 24 | -------------------------------------------------------------------------------- /src/Components/index.js: -------------------------------------------------------------------------------- 1 | export { default as AuthorInput } from "./AuthorInput/AuthorInput" 2 | export { default as ColorBox } from "./ColorBox/ColorBox" 3 | export { default as Footer } from "./Footer/Footer" 4 | export { default as UserInput } from "./UserInput/UserInput" 5 | export { default as UserOutput } from "./UserOutput/UserOutput" -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | serviceWorker.unregister(); 15 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const isLocalhost = Boolean( 4 | window.location.hostname === 'localhost' || 5 | window.location.hostname === '[::1]' || 6 | window.location.hostname.match( 7 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 8 | ) 9 | ); 10 | 11 | export function register(config) { 12 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 13 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 14 | if (publicUrl.origin !== window.location.origin) { 15 | return; 16 | } 17 | 18 | window.addEventListener('load', () => { 19 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 20 | 21 | if (isLocalhost) { 22 | checkValidServiceWorker(swUrl, config); 23 | 24 | navigator.serviceWorker.ready.then(() => { 25 | console.log( 26 | 'This web app is being served cache-first by a service ' + 27 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 28 | ); 29 | }); 30 | } else { 31 | registerValidSW(swUrl, config); 32 | } 33 | }); 34 | } 35 | } 36 | 37 | function registerValidSW(swUrl, config) { 38 | navigator.serviceWorker 39 | .register(swUrl) 40 | .then(registration => { 41 | registration.onupdatefound = () => { 42 | const installingWorker = registration.installing; 43 | if (installingWorker == null) { 44 | return; 45 | } 46 | installingWorker.onstatechange = () => { 47 | if (installingWorker.state === 'installed') { 48 | if (navigator.serviceWorker.controller) { 49 | console.log( 50 | 'New content is available and will be used when all ' + 51 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 52 | ); 53 | 54 | if (config && config.onUpdate) { 55 | config.onUpdate(registration); 56 | } 57 | } else { 58 | console.log('Content is cached for offline use.'); 59 | 60 | if (config && config.onSuccess) { 61 | config.onSuccess(registration); 62 | } 63 | } 64 | } 65 | }; 66 | }; 67 | }) 68 | .catch(error => { 69 | console.error('Error during service worker registration:', error); 70 | }); 71 | } 72 | 73 | function checkValidServiceWorker(swUrl, config) { 74 | fetch(swUrl, { 75 | headers: { 'Service-Worker': 'script' }, 76 | }) 77 | .then(response => { 78 | const contentType = response.headers.get('content-type'); 79 | if ( 80 | response.status === 404 || 81 | (contentType != null && contentType.indexOf('javascript') === -1) 82 | ) { 83 | navigator.serviceWorker.ready.then(registration => { 84 | registration.unregister().then(() => { 85 | window.location.reload(); 86 | }); 87 | }); 88 | } else { 89 | registerValidSW(swUrl, config); 90 | } 91 | }) 92 | .catch(() => { 93 | console.log( 94 | 'No internet connection found. App is running in offline mode.' 95 | ); 96 | }); 97 | } 98 | 99 | export function unregister() { 100 | if ('serviceWorker' in navigator) { 101 | navigator.serviceWorker.ready 102 | .then(registration => { 103 | registration.unregister(); 104 | }) 105 | .catch(error => { 106 | console.error(error.message); 107 | }); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | 2 | import '@testing-library/jest-dom/extend-expect'; 3 | --------------------------------------------------------------------------------