├── .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 |
213 |
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 |
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 |
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 |
--------------------------------------------------------------------------------