├── .gitignore
├── .prettierrc
├── README.md
├── package-lock.json
├── package.json
├── public
├── 404.html
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
└── src
├── App.css
├── App.test.js
├── AppOriginal.css
├── components
├── Another.jsx
├── App.jsx
├── AppClass.jsx
├── AppOriginal.jsx
├── NavigationBar.jsx
├── NoTodos.jsx
├── Root.jsx
├── TodoClearCompleted.jsx
├── TodoCompleteAllTodos.jsx
├── TodoFilters.jsx
├── TodoForm.jsx
├── TodoItemsRemaining.jsx
├── TodoList.jsx
├── api
│ ├── Joke.jsx
│ ├── Reddit.jsx
│ └── useFetch.js
├── github-markdown.css
└── pages
│ ├── Comments.jsx
│ ├── Details.jsx
│ ├── IconClosed.jsx
│ ├── IconOpen.jsx
│ ├── Issues.jsx
│ ├── NoMatch.jsx
│ └── api.jsx
├── context
└── TodosContext.js
├── hooks
├── useLocalStorage.js
└── useToggle.js
├── index.css
├── index.js
├── logo.svg
├── reportWebVitals.js
├── reset.css
└── setupTests.js
/.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 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "arrowParens": "avoid"
4 | }
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Todo App
2 |
3 |
4 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
5 |
6 | ## Getting Started
7 | [Live Demo](https://ismaildasci.github.io/react-todo/)
8 |
9 |
10 | ### Installation
11 | Follow the steps below to run this project on your local machine.
12 |
13 | Prerequisites
14 |
15 | Node.js and NPM (comes with Node.js)
16 |
17 | 1. Clone the Repository
18 |
19 | Clone the repository to your local machine:
20 | ```sh
21 | git clone https://github.com/ismaildasci/react-todo.git
22 | cd react-todo
23 |
24 | ```
25 |
26 | 2. Install Dependencies
27 | Install the project dependencies:
28 | ```sh
29 | npm install
30 |
31 | ```
32 |
33 | 1. Run the Application
34 |
35 | Start the application:
36 | ```sh
37 | npm start
38 |
39 | ```
40 | This command runs the app in development mode. Open http://localhost:3000/react-todo to view it in the browser.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lc-react",
3 | "version": "0.1.0",
4 | "homepage": "https://ismaildasci.github.io/react-todo",
5 | "private": true,
6 | "dependencies": {
7 | "@testing-library/jest-dom": "^5.16.5",
8 | "@testing-library/react": "^13.4.0",
9 | "@testing-library/user-event": "^13.5.0",
10 | "date-fns": "^2.30.0",
11 | "prop-types": "^15.8.1",
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0",
14 | "react-markdown": "^8.0.7",
15 | "react-query": "^3.39.3",
16 | "react-router-dom": "^6.14.0",
17 | "react-scripts": "5.0.1",
18 | "react-transition-group": "^4.4.5",
19 | "web-vitals": "^2.1.4"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start --public-url /react-todo/",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject",
26 | "predeploy": "npm run build",
27 | "deploy": "gh-pages -d build"
28 | },
29 | "eslintConfig": {
30 | "extends": [
31 | "react-app",
32 | "react-app/jest"
33 | ]
34 | },
35 | "browserslist": {
36 | "production": [
37 | ">0.2%",
38 | "not dead",
39 | "not op_mini all"
40 | ],
41 | "development": [
42 | "last 1 chrome version",
43 | "last 1 firefox version",
44 | "last 1 safari version"
45 | ]
46 | },
47 | "devDependencies": {
48 | "gh-pages": "^5.0.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Redirecting
7 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ismaildasci/react-todo/01816e6a4762f12870d128f9a9cdb276b39e1c5a/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ismaildasci/react-todo/01816e6a4762f12870d128f9a9cdb276b39e1c5a/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ismaildasci/react-todo/01816e6a4762f12870d128f9a9cdb276b39e1c5a/public/logo512.png
--------------------------------------------------------------------------------
/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 | *,
2 | *::before,
3 | *::after {
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | margin: 0;
9 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
10 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
11 | sans-serif;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | }
15 |
16 | .todo-app-container {
17 | min-height: 100vh;
18 | background: #f3f4f6;
19 | }
20 |
21 | .container {
22 | margin: auto;
23 | padding-top: 24px;
24 | max-width: 48rem;
25 | }
26 |
27 | .todo-app {
28 | margin: auto;
29 | margin-top: 30px;
30 | padding: 2rem;
31 | background: white;
32 | border-radius: 0.25rem;
33 | box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
34 | max-width: 32rem;
35 | color: #374151;
36 | }
37 |
38 | h2 {
39 | font-size: 24px;
40 | font-weight: bold;
41 | }
42 |
43 | .todo-input {
44 | width: 100%;
45 | border: none;
46 | box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
47 | padding: 14px;
48 | font-size: 16px;
49 | margin-top: 16px;
50 | }
51 |
52 | .todo-list {
53 | margin-top: 32px;
54 | }
55 |
56 | .todo-item-container {
57 | display: flex;
58 | justify-content: space-between;
59 | align-items: center;
60 | margin-top: 24px;
61 | }
62 |
63 | .todo-item {
64 | display: flex;
65 | align-items: center;
66 | flex: 1;
67 | font-size: 18px;
68 | margin-right: 24px;
69 | }
70 |
71 | .todo-item-label {
72 | margin-left: 16px;
73 | }
74 |
75 | .todo-item-input {
76 | margin-left: 8px;
77 | width: 100%;
78 | border: none;
79 | box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
80 | padding: 6px 8px;
81 | font-size: 18px;
82 | }
83 |
84 | .line-through {
85 | text-decoration: line-through;
86 | }
87 |
88 | .x-button {
89 | background: white;
90 | color: #6B7280;
91 | border: none;
92 | cursor: pointer;
93 | }
94 |
95 | .x-button:hover {
96 | color: #1F2937;
97 | }
98 |
99 | .x-button-icon {
100 | width: 1.5rem;
101 | height: 1.5rem;
102 | }
103 |
104 | .button {
105 | color: #6B7280;
106 | font-size: 14px;
107 | background: white;
108 | border: 1px solid lightgray;
109 | padding: 6px;
110 | border-radius: 5px;
111 | cursor: pointer;
112 | }
113 |
114 | .button:hover {
115 | border: 1px solid lightgray;
116 | }
117 |
118 | .filter-button {
119 | border: 1px solid white;
120 | }
121 |
122 | .filter-button-active {
123 | border: 1px solid lightgray;
124 | }
125 |
126 | .check-all-container {
127 | display: flex;
128 | justify-content: space-between;
129 | align-items: center;
130 | color: #6B7280;
131 | margin-top: 22px;
132 | padding-top: 16px;
133 | border-top: 1px solid lightgray;
134 | }
135 |
136 | .other-buttons-container {
137 | display: flex;
138 | justify-content: space-between;
139 | align-items: center;
140 | color: #6B7280;
141 | margin-top: 22px;
142 | padding-top: 16px;
143 | border-top: 1px solid lightgray;
144 | }
145 |
146 | .no-todos-container {
147 | width: 300px;
148 | margin: 30px auto;
149 | }
150 |
151 | .no-todos-container p {
152 | margin-top: 20px;
153 | text-align: center;
154 | }
155 |
156 | .name-container {
157 | margin-bottom: 40px;
158 | }
159 |
160 | .name-label {
161 | margin-top: 15px;
162 | }
163 |
164 | .toggles-container {
165 | margin: 20px 0;
166 | }
167 |
168 | nav {
169 | background: white;
170 | padding: 16px;
171 | }
172 |
173 | nav ul {
174 | display: flex;
175 | justify-content: center;
176 | margin: auto;
177 | }
178 |
179 | nav ul li:not(:first-child) {
180 | margin-left: 16px;
181 | }
182 |
183 | .active {
184 | font-weight: bold;
185 | }
186 |
187 | /* Transitions */
188 |
189 | .slide-vertical-enter {
190 | opacity: 0;
191 | transform: translateY(-20px);
192 | }
193 |
194 | .slide-vertical-enter-active {
195 | opacity: 1;
196 | transform: translateY(0);
197 | transition: all 300ms;
198 | }
199 |
200 | .slide-vertical-exit {
201 | opacity: 1;
202 | transform: translateY(0);
203 | }
204 |
205 | .slide-vertical-exit-active {
206 | opacity: 0;
207 | transition: all 300ms;
208 | transform: translateY(-20px);
209 | }
210 |
211 | .slide-horizontal-enter {
212 | opacity: 0;
213 | transform: translateX(20px);
214 | }
215 |
216 | .slide-horizontal-enter-active {
217 | opacity: 1;
218 | transform: translateX(0);
219 | transition: all 300ms;
220 | }
221 |
222 | .slide-horizontal-exit {
223 | opacity: 1;
224 | transform: translateX(0);
225 | }
226 |
227 | .slide-horizontal-exit-active {
228 | opacity: 0;
229 | transition: all 300ms;
230 | transform: translateX(20px);
231 | }
232 |
233 | *,
234 | *::before,
235 | *::after {
236 | box-sizing: border-box;
237 | }
238 |
239 | body {
240 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
241 | color: #111827;
242 | }
243 |
244 | a {
245 | color: #0265d6;
246 | text-decoration: none;
247 | }
248 |
249 | .container {
250 | max-width: 54rem;
251 | margin: 32px auto;
252 | }
253 |
254 | .font-bold {
255 | font-weight: bold;
256 | }
257 |
258 | .issues-heading {
259 | background: #f5f8fa;
260 | padding: 15px 15px;
261 | border-top-left-radius: 10px;
262 | border-top-right-radius: 10px;
263 | border: 1px solid #e1e4e8;
264 | border-bottom: none;
265 | }
266 |
267 | .issues-heading a {
268 | font-weight: bold;
269 | font-size: 20px;
270 | }
271 |
272 | .issues-heading a:hover {
273 | text-decoration: underline;
274 | }
275 |
276 | .issues-entry {
277 | display: flex;
278 | justify-content: space-between;
279 | align-items: center;
280 | border: 1px solid #e1e4e8;
281 | padding: 12px 8px;
282 | }
283 |
284 | .issues-entry:last-child {
285 | border-bottom-left-radius: 10px;
286 | border-bottom-right-radius: 10px;
287 | }
288 |
289 | .issues-entry:hover {
290 | background: #f5f8fa;
291 | }
292 |
293 | .issues-entry-title-container {
294 | display: flex;
295 | }
296 |
297 |
298 | .issues-entry-title-container {
299 | display: flex;
300 | }
301 |
302 | .issues-title {
303 | margin-left: 8px;
304 | font-weight: bold;
305 | }
306 |
307 | .issues-title a {
308 | color: #111827;
309 | text-decoration: none;
310 | }
311 |
312 | .issues-title a:hover {
313 | color: #0265d6;
314 | }
315 |
316 | .issues-title-details {
317 | font-size: 12px;
318 | font-weight: normal;
319 | margin-top: 8px;
320 | color: #374151;
321 | }
322 |
323 | .comments-count-container {
324 | display: flex;
325 | text-decoration: none;
326 | color: #374151;
327 | }
328 |
329 | .comments-count-container:hover {
330 | color: #0265d6;
331 | }
332 |
333 | .comments-count {
334 | text-decoration: none;
335 | margin-left: 4px;
336 | font-size: 14px;
337 | }
338 |
339 | .open-closed-buttons {
340 | margin-top: 12px;
341 | }
342 |
343 | .open-closed-buttons button {
344 | display: inline-flex;
345 | align-items: center;
346 | border: none;
347 | background: none;
348 | padding: 0;
349 | cursor: pointer;
350 | }
351 |
352 | .open-closed-buttons button:last-child {
353 | margin-left: 14px;
354 | }
355 |
356 | .open-closed-buttons span {
357 | margin-left: 4px;
358 | font-size: 14px;
359 | }
360 |
361 | svg {
362 | fill: currentColor;
363 | }
364 |
365 | .open {
366 | color: #22863a;
367 | }
368 |
369 | .closed {
370 | color: #e5534b;
371 | }
372 |
373 | /* Comments */
374 |
375 | .comments-container {
376 | margin-top: 48px;
377 | }
378 |
379 | .comments-container h2 {
380 | font-size: 24px;
381 | font-weight: bold;
382 | }
383 |
384 | .comments-container h2 span {
385 | color: #374151;
386 | font-weight: normal;
387 | }
388 |
389 | .issue-details {
390 | font-size: 14px;
391 | margin-top: 12px;
392 | color: #374151;
393 | }
394 |
395 | .issue-details a {
396 | color: #374151;
397 | font-weight: bold;
398 | }
399 |
400 | .issue-details a:hover {
401 | color: #0265d6;
402 | }
403 |
404 | .comment-container {
405 | display: flex;
406 | }
407 |
408 | .comment-container:not(:first-child) {
409 | margin-top: 32px;
410 | }
411 |
412 | .avatar {
413 | width: 42px;
414 | border-radius: 50%;
415 | }
416 |
417 | .comment {
418 | margin-left: 18px;
419 | flex: 1;
420 | max-width: 100%;
421 | }
422 |
423 | .comment-heading {
424 | font-size: 14px;
425 | background: #f5f8fa;
426 | padding: 15px 15px;
427 | border-top-left-radius: 10px;
428 | border-top-right-radius: 10px;
429 | border: 1px solid #e1e4e8;
430 | border-bottom: none;
431 | }
432 |
433 | .comment-heading a {
434 | color: #111827;
435 | font-weight: bold;
436 | }
437 |
438 | .comment-heading a:hover {
439 | color: #0265d6;
440 | text-decoration: underline;
441 | }
442 |
443 | .comment-body {
444 | border-bottom-left-radius: 10px;
445 | border-bottom-right-radius: 10px;
446 | border: 1px solid #e1e4e8;
447 | padding: 16px;
448 | line-height: 1.5;
449 | }
450 |
451 | .border {
452 | padding-top: 32px;
453 | border-bottom: 1px solid #F3F4F6;
454 | }
455 | .navbar {
456 | background-color: #2f3640;
457 | overflow: hidden;
458 | box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
459 | }
460 |
461 | .navbar ul {
462 | list-style: none;
463 | margin: 0;
464 | padding: 0;
465 | display: flex;
466 | align-items: center;
467 | }
468 |
469 | .navbar li {
470 | padding: 15px 25px;
471 | }
472 |
473 | .navbar a {
474 | color: #ecf0f1;
475 | text-decoration: none;
476 | font-size: 16px;
477 | font-family: 'Roboto', sans-serif;
478 | transition: color 0.3s ease-in-out;
479 | }
480 |
481 | .navbar a:hover,
482 | .navbar a.active {
483 | color: #3498db;
484 | }
485 | .api-button {
486 | background-color: #34495e;
487 | border: none;
488 | color: white;
489 | text-align: center;
490 | text-decoration: none;
491 | display: inline-block;
492 | font-size: 16px;
493 | margin: 10px;
494 | padding: 15px 30px;
495 | cursor: pointer;
496 | border-radius: 8px;
497 | transition: background-color 0.3s ease-in-out, transform 0.3s ease-in-out;
498 | }
499 |
500 | .buttons {
501 | display: flex;
502 | justify-content: center;
503 | margin: 20px;
504 | }
505 |
506 | .api-button {
507 | background-color: #2f3640;
508 | border: 1px solid #444;
509 | color: #fff;
510 | text-align: center;
511 | text-decoration: none;
512 | display: inline-block;
513 | font-size: 14px;
514 | margin: 10px;
515 | padding: 10px 20px;
516 | cursor: pointer;
517 | border-radius: 4px;
518 | }
519 |
520 | .api-button:hover {
521 | background-color: #3498db;
522 | }
523 |
524 | .content {
525 | display: flex;
526 | flex-direction: column;
527 | align-items: center;
528 | margin: 20px;
529 | }
530 |
531 | .reddit-content,
532 | .joke-content {
533 | background-color: #f9f9f9;
534 | padding: 20px;
535 | border-radius: 4px;
536 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
537 | width: 100%;
538 | max-width: 800px;
539 | margin: 10px;
540 | text-align: left;
541 | }
542 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/src/AppOriginal.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | /* animation: App-logo-spin infinite 20s linear; */
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 |
36 | to {
37 | transform: rotate(360deg);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/Another.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function Another(props) {
4 | return Another Component, {props.name}
;
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/App.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState, useRef } from 'react';
2 | import NoTodos from './NoTodos';
3 | import TodoForm from './TodoForm';
4 | import TodoList from './TodoList';
5 | import useLocalStorage from '../hooks/useLocalStorage';
6 | import '../reset.css';
7 | import '../App.css';
8 | import { TodosContext } from '../context/TodosContext';
9 | import { CSSTransition, SwitchTransition } from 'react-transition-group';
10 |
11 | function App() {
12 | const [name, setName] = useLocalStorage('name', '');
13 |
14 | const nameInputEl = useRef(null);
15 | const [todos, setTodos] = useLocalStorage('todos', []);
16 |
17 | const [idForTodo, setIdForTodo] = useLocalStorage('idForTodo', 1);
18 |
19 | const [filter, setFilter] = useState('all');
20 |
21 | function todosFiltered() {
22 | if (filter === 'all') {
23 | return todos;
24 | } else if (filter === 'active') {
25 | return todos.filter(todo => !todo.isComplete);
26 | } else if (filter === 'completed') {
27 | return todos.filter(todo => todo.isComplete);
28 | }
29 | }
30 |
31 | useEffect(() => {
32 | // console.log('use effect running');
33 | nameInputEl.current.focus();
34 |
35 | // setName(JSON.parse(localStorage.getItem('name')) ?? '');
36 |
37 | return function cleanup() {
38 | // console.log('cleaning up');
39 | };
40 | }, []);
41 |
42 | function handleNameInput(event) {
43 | setName(event.target.value);
44 | // localStorage.setItem('name', JSON.stringify(event.target.value));
45 | }
46 |
47 | return (
48 |
59 |
60 |
61 |
What is your name?
62 |
72 |
73 |
0}
75 | timeout={300}
76 | classNames="slide-vertical"
77 | unmountOnExit
78 | >
79 | Hello, {name}
80 |
81 |
82 |
Todo App
83 |
84 |
85 |
86 | 0}
88 | timeout={300}
89 | classNames="slide-vertical"
90 | unmountOnExit
91 | >
92 | {todos.length > 0 ? : }
93 |
94 |
95 |
96 | {/*
0}
98 | timeout={300}
99 | classNames="slide-vertical"
100 | unmountOnExit
101 | >
102 |
103 |
104 |
105 |
111 |
112 | */}
113 |
114 |
115 | );
116 | }
117 |
118 | export default App;
119 |
--------------------------------------------------------------------------------
/src/components/AppClass.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export default class AppClass extends Component {
4 | constructor(props) {
5 | super(props);
6 | this.state = {
7 | idForTodo: 4,
8 | todos: [
9 | {
10 | id: 1,
11 | title: 'Finish React Series',
12 | isComplete: false,
13 | },
14 | {
15 | id: 2,
16 | title: 'Go Grocery',
17 | isComplete: true,
18 | },
19 | {
20 | id: 3,
21 | title: 'Take over world',
22 | isComplete: false,
23 | },
24 | ],
25 | };
26 | }
27 |
28 | addTodo = event => {
29 | event.preventDefault();
30 |
31 | this.setState(prevState => {
32 | const newTodos = [
33 | ...prevState.todos,
34 | {
35 | id: 4,
36 | title: 'This is class based components',
37 | isComplete: false,
38 | },
39 | ];
40 |
41 | return { todos: newTodos };
42 | });
43 | };
44 |
45 | render() {
46 | return (
47 |
48 |
49 |
Todo App
50 |
57 |
58 |
84 |
85 |
86 |
89 |
90 |
3 items remaining
91 |
92 |
93 |
94 |
95 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | );
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/components/AppOriginal.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import Another from './Another';
3 | import logo from './logo.svg';
4 | import '../App.css';
5 |
6 | function App() {
7 | const [count, setCount] = useState(0);
8 |
9 | function decrement() {
10 | setCount(prevCount => prevCount - 1);
11 | }
12 |
13 | function increment() {
14 | setCount(prevCount => prevCount + 1);
15 | }
16 |
17 | const someStyle = {
18 | background: 'blue',
19 | color: 'white',
20 | fontSize: '28px',
21 | fontWeight: 'bold',
22 | };
23 |
24 | return (
25 |
48 | );
49 | }
50 |
51 | export default App;
52 |
--------------------------------------------------------------------------------
/src/components/NavigationBar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { NavLink } from 'react-router-dom';
3 |
4 | export default function NavigationBar() {
5 | return (
6 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/NoTodos.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function NoTodos() {
4 | return (
5 |
6 |
60 |
Add some todos...
61 |
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/src/components/Root.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import App from './App';
3 | import NavigationBar from './NavigationBar';
4 | import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
5 | import About from './pages/api';
6 | import Issues from './pages/Issues';
7 | import Details from './pages/Details';
8 | import NoMatch from './pages/NoMatch';
9 |
10 | // const basename = process.env.NODE_ENV === 'development' ? '/' : '/react-todo';
11 |
12 | export default function Root() {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 | } exact />
20 | } />
21 | } />
22 | } />
23 | } />
24 |
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/TodoClearCompleted.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { TodosContext } from '../context/TodosContext';
3 |
4 | function TodoClearCompleted() {
5 | const { todos, setTodos } = useContext(TodosContext);
6 |
7 | function clearCompleted() {
8 | setTodos([...todos].filter(todo => !todo.isComplete));
9 | }
10 |
11 | return (
12 |
15 | );
16 | }
17 |
18 | export default TodoClearCompleted;
19 |
--------------------------------------------------------------------------------
/src/components/TodoCompleteAllTodos.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { TodosContext } from '../context/TodosContext';
3 |
4 | function TodoCompleteAllTodos() {
5 | const { todos, setTodos } = useContext(TodosContext);
6 |
7 | function completeAllTodos() {
8 | const updatedTodos = todos.map(todo => {
9 | todo.isComplete = true;
10 |
11 | return todo;
12 | });
13 |
14 | setTodos(updatedTodos);
15 | }
16 |
17 | return (
18 |
19 |
20 | Check All
21 |
22 |
23 | );
24 | }
25 |
26 | export default TodoCompleteAllTodos;
27 |
--------------------------------------------------------------------------------
/src/components/TodoFilters.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { TodosContext } from '../context/TodosContext';
3 |
4 | function TodoFilters() {
5 | const { filter, setFilter, todosFiltered } = useContext(TodosContext);
6 |
7 | return (
8 |
9 |
20 |
31 |
42 |
43 | );
44 | }
45 |
46 | export default TodoFilters;
47 |
--------------------------------------------------------------------------------
/src/components/TodoForm.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react';
2 | import { TodosContext } from '../context/TodosContext';
3 |
4 | function TodoForm() {
5 | const { todos, setTodos, idForTodo, setIdForTodo } = useContext(TodosContext);
6 |
7 | const [todoInput, setTodoInput] = useState('');
8 |
9 | function handleInput(event) {
10 | setTodoInput(event.target.value);
11 | }
12 |
13 | function addTodo(event) {
14 | event.preventDefault();
15 |
16 | if (todoInput.trim().length === 0) {
17 | return;
18 | }
19 |
20 | setTodos([
21 | ...todos,
22 | {
23 | id: idForTodo,
24 | title: todoInput,
25 | isComplete: false,
26 | },
27 | ]);
28 |
29 | setIdForTodo(prevIdForTodo => prevIdForTodo + 1);
30 |
31 | setTodoInput('');
32 | }
33 |
34 | return (
35 |
44 | );
45 | }
46 |
47 | export default TodoForm;
48 |
--------------------------------------------------------------------------------
/src/components/TodoItemsRemaining.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useMemo } from 'react';
2 | import { TodosContext } from '../context/TodosContext';
3 |
4 | function TodoItemsRemaining() {
5 | const { todos } = useContext(TodosContext);
6 |
7 | function remainingCalculation() {
8 | // console.log('calculating remaining todos. This is slow.');
9 | // for (let index = 0; index < 2000000000; index++) {}
10 | return todos.filter(todo => !todo.isComplete).length;
11 | }
12 |
13 | const remaining = useMemo(remainingCalculation, [todos]);
14 |
15 | return {remaining} items remaining;
16 | }
17 |
18 | export default TodoItemsRemaining;
19 |
--------------------------------------------------------------------------------
/src/components/TodoList.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import TodoItemsRemaining from './TodoItemsRemaining';
3 | import TodoClearCompleted from './TodoClearCompleted';
4 | import TodoCompleteAllTodos from './TodoCompleteAllTodos';
5 | import TodoFilters from './TodoFilters';
6 | import useToggle from '../hooks/useToggle';
7 | import { TodosContext } from '../context/TodosContext';
8 | import { CSSTransition, TransitionGroup } from 'react-transition-group';
9 |
10 | function TodoList() {
11 | const { todos, setTodos, todosFiltered } = useContext(TodosContext);
12 | const [isFeaturesOneVisible, setFeaturesOneVisible] = useToggle();
13 | const [isFeaturesTwoVisible, setFeaturesTwoVisible] = useToggle();
14 |
15 | function deleteTodo(id) {
16 | setTodos([...todos].filter(todo => todo.id !== id));
17 | }
18 |
19 | function completeTodo(id) {
20 | const updatedTodos = todos.map(todo => {
21 | if (todo.id === id) {
22 | todo.isComplete = !todo.isComplete;
23 | }
24 |
25 | return todo;
26 | });
27 |
28 | setTodos(updatedTodos);
29 | }
30 |
31 | function markAsEditing(id) {
32 | const updatedTodos = todos.map(todo => {
33 | if (todo.id === id) {
34 | todo.isEditing = true;
35 | }
36 |
37 | return todo;
38 | });
39 |
40 | setTodos(updatedTodos);
41 | }
42 |
43 | function updateTodo(event, id) {
44 | const updatedTodos = todos.map(todo => {
45 | if (todo.id === id) {
46 | if (event.target.value.trim().length === 0) {
47 | todo.isEditing = false;
48 | return todo;
49 | }
50 | todo.title = event.target.value;
51 | todo.isEditing = false;
52 | }
53 |
54 | return todo;
55 | });
56 |
57 | setTodos(updatedTodos);
58 | }
59 |
60 | function cancelEdit(event, id) {
61 | const updatedTodos = todos.map(todo => {
62 | if (todo.id === id) {
63 | todo.isEditing = false;
64 | }
65 |
66 | return todo;
67 | });
68 |
69 | setTodos(updatedTodos);
70 | }
71 |
72 | return (
73 | <>
74 |
75 | {todosFiltered().map((todo, index) => (
76 |
81 |
82 |
83 | completeTodo(todo.id)}
86 | checked={todo.isComplete ? true : false}
87 | />
88 |
89 | {!todo.isEditing ? (
90 | markAsEditing(todo.id)}
92 | className={`todo-item-label ${
93 | todo.isComplete ? 'line-through' : ''
94 | }`}
95 | >
96 | {todo.title}
97 |
98 | ) : (
99 | updateTodo(event, todo.id)}
102 | onKeyDown={event => {
103 | if (event.key === 'Enter') {
104 | updateTodo(event, todo.id);
105 | } else if (event.key === 'Escape') {
106 | cancelEdit(event, todo.id);
107 | }
108 | }}
109 | className="todo-item-input"
110 | defaultValue={todo.title}
111 | autoFocus
112 | />
113 | )}
114 |
115 |
130 |
131 |
132 | ))}
133 |
134 |
135 |
136 |
139 |
142 |
143 |
144 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | >
171 | );
172 | }
173 |
174 | export default TodoList;
175 |
--------------------------------------------------------------------------------
/src/components/api/Joke.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useQuery } from 'react-query';
3 |
4 | export default function Joke() {
5 | const {
6 | data: joke,
7 | isLoading,
8 | isError,
9 | error,
10 | isSuccess,
11 | } = useQuery('joke', fetchJoke, {
12 | // staleTime: 6000,
13 | refetchOnWindowFocus: false,
14 | });
15 |
16 | function fetchJoke() {
17 | return fetch('https://official-joke-api.appspot.com/jokes/random').then(
18 | response => response.json()
19 | );
20 | }
21 |
22 | // const {
23 | // data: joke,
24 | // isLoading,
25 | // errorMessage,
26 | // } = useFetch('https://official-joke-api.appspot.com/jokes/random');
27 |
28 | return (
29 |
30 |
Joke API
31 | {isLoading &&
Loading...
}
32 | {isSuccess &&
{joke.setup + ' ' + joke.punchline}
}
33 | {isError &&
{error.message}
}
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/api/Reddit.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useQuery } from 'react-query';
3 |
4 | export default function Reddit() {
5 | // const {
6 | // data: posts,
7 | // isLoading,
8 | // errorMessage,
9 | // } = useFetch('https://www.reddit.com/r/aww.json');
10 |
11 | const {
12 | data: posts,
13 | isLoading,
14 | isError,
15 | error,
16 | isSuccess,
17 | } = useQuery('posts', fetchPosts, {
18 | retry: false,
19 | });
20 |
21 | function fetchPosts() {
22 | return fetch('https://www.reddit.com/r/aww.json').then(response =>
23 | response.json()
24 | );
25 | }
26 |
27 | return (
28 |
29 |
Reddit API
30 | {isLoading &&
Loading...
}
31 | {isSuccess && (
32 |
41 | )}
42 | {isError &&
{error.message}
}
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/api/useFetch.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | function useFetch(url) {
4 | const [data, setData] = useState(null);
5 | const [isLoading, setIsLoading] = useState(true);
6 | const [errorMessage, setErrorMessage] = useState(null);
7 |
8 | useEffect(() => {
9 | fetch(url)
10 | .then(response => response.json())
11 | .then(results => {
12 | setIsLoading(false);
13 | setData(results);
14 | })
15 | .catch(error => {
16 | setIsLoading(false);
17 | setErrorMessage('There was an error');
18 | });
19 | }, [url]);
20 |
21 | return { data, isLoading, errorMessage };
22 | }
23 |
24 | export default useFetch;
25 |
--------------------------------------------------------------------------------
/src/components/github-markdown.css:
--------------------------------------------------------------------------------
1 | .markdown-body .octicon {
2 | display: inline-block;
3 | fill: currentColor;
4 | vertical-align: text-bottom;
5 | }
6 |
7 | .markdown-body .anchor {
8 | float: left;
9 | line-height: 1;
10 | margin-left: -20px;
11 | padding-right: 4px;
12 | }
13 |
14 | .markdown-body .anchor:focus {
15 | outline: none;
16 | }
17 |
18 | .markdown-body h1 .octicon-link,
19 | .markdown-body h2 .octicon-link,
20 | .markdown-body h3 .octicon-link,
21 | .markdown-body h4 .octicon-link,
22 | .markdown-body h5 .octicon-link,
23 | .markdown-body h6 .octicon-link {
24 | color: #1b1f23;
25 | vertical-align: middle;
26 | visibility: hidden;
27 | }
28 |
29 | .markdown-body h1:hover .anchor,
30 | .markdown-body h2:hover .anchor,
31 | .markdown-body h3:hover .anchor,
32 | .markdown-body h4:hover .anchor,
33 | .markdown-body h5:hover .anchor,
34 | .markdown-body h6:hover .anchor {
35 | text-decoration: none;
36 | }
37 |
38 | .markdown-body h1:hover .anchor .octicon-link,
39 | .markdown-body h2:hover .anchor .octicon-link,
40 | .markdown-body h3:hover .anchor .octicon-link,
41 | .markdown-body h4:hover .anchor .octicon-link,
42 | .markdown-body h5:hover .anchor .octicon-link,
43 | .markdown-body h6:hover .anchor .octicon-link {
44 | visibility: visible;
45 | }
46 |
47 | .markdown-body h1:hover .anchor .octicon-link:before,
48 | .markdown-body h2:hover .anchor .octicon-link:before,
49 | .markdown-body h3:hover .anchor .octicon-link:before,
50 | .markdown-body h4:hover .anchor .octicon-link:before,
51 | .markdown-body h5:hover .anchor .octicon-link:before,
52 | .markdown-body h6:hover .anchor .octicon-link:before {
53 | width: 16px;
54 | height: 16px;
55 | content: ' ';
56 | display: inline-block;
57 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath fill-rule='evenodd' d='M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z'%3E%3C/path%3E%3C/svg%3E");
58 | }
59 |
60 | .markdown-body {
61 | -ms-text-size-adjust: 100%;
62 | -webkit-text-size-adjust: 100%;
63 | line-height: 1.5;
64 | color: #24292e;
65 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
66 | font-size: 16px;
67 | line-height: 1.5;
68 | word-wrap: break-word;
69 | }
70 |
71 | .markdown-body details {
72 | display: block;
73 | }
74 |
75 | .markdown-body summary {
76 | display: list-item;
77 | }
78 |
79 | .markdown-body a {
80 | background-color: initial;
81 | }
82 |
83 | .markdown-body a:active,
84 | .markdown-body a:hover {
85 | outline-width: 0;
86 | }
87 |
88 | .markdown-body strong {
89 | font-weight: inherit;
90 | font-weight: bolder;
91 | }
92 |
93 | .markdown-body h1 {
94 | font-size: 2em;
95 | margin: .67em 0;
96 | }
97 |
98 | .markdown-body img {
99 | border-style: none;
100 | }
101 |
102 | .markdown-body code,
103 | .markdown-body kbd,
104 | .markdown-body pre {
105 | font-family: monospace, monospace;
106 | font-size: 1em;
107 | }
108 |
109 | .markdown-body hr {
110 | box-sizing: initial;
111 | height: 0;
112 | overflow: visible;
113 | }
114 |
115 | .markdown-body input {
116 | font: inherit;
117 | margin: 0;
118 | }
119 |
120 | .markdown-body input {
121 | overflow: visible;
122 | }
123 |
124 | .markdown-body [type=checkbox] {
125 | box-sizing: border-box;
126 | padding: 0;
127 | }
128 |
129 | .markdown-body * {
130 | box-sizing: border-box;
131 | }
132 |
133 | .markdown-body input {
134 | font-family: inherit;
135 | font-size: inherit;
136 | line-height: inherit;
137 | }
138 |
139 | .markdown-body a {
140 | color: #0366d6;
141 | text-decoration: none;
142 | }
143 |
144 | .markdown-body a:hover {
145 | text-decoration: underline;
146 | }
147 |
148 | .markdown-body strong {
149 | font-weight: 600;
150 | }
151 |
152 | .markdown-body hr {
153 | height: 0;
154 | margin: 15px 0;
155 | overflow: hidden;
156 | background: transparent;
157 | border: 0;
158 | border-bottom: 1px solid #dfe2e5;
159 | }
160 |
161 | .markdown-body hr:after,
162 | .markdown-body hr:before {
163 | display: table;
164 | content: "";
165 | }
166 |
167 | .markdown-body hr:after {
168 | clear: both;
169 | }
170 |
171 | .markdown-body table {
172 | border-spacing: 0;
173 | border-collapse: collapse;
174 | }
175 |
176 | .markdown-body td,
177 | .markdown-body th {
178 | padding: 0;
179 | }
180 |
181 | .markdown-body details summary {
182 | cursor: pointer;
183 | }
184 |
185 | .markdown-body kbd {
186 | display: inline-block;
187 | padding: 3px 5px;
188 | font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
189 | line-height: 10px;
190 | color: #444d56;
191 | vertical-align: middle;
192 | background-color: #fafbfc;
193 | border: 1px solid #d1d5da;
194 | border-radius: 3px;
195 | box-shadow: inset 0 -1px 0 #d1d5da;
196 | }
197 |
198 | .markdown-body h1,
199 | .markdown-body h2,
200 | .markdown-body h3,
201 | .markdown-body h4,
202 | .markdown-body h5,
203 | .markdown-body h6 {
204 | margin-top: 0;
205 | margin-bottom: 0;
206 | }
207 |
208 | .markdown-body h1 {
209 | font-size: 32px;
210 | }
211 |
212 | .markdown-body h1,
213 | .markdown-body h2 {
214 | font-weight: 600;
215 | }
216 |
217 | .markdown-body h2 {
218 | font-size: 24px;
219 | }
220 |
221 | .markdown-body h3 {
222 | font-size: 20px;
223 | }
224 |
225 | .markdown-body h3,
226 | .markdown-body h4 {
227 | font-weight: 600;
228 | }
229 |
230 | .markdown-body h4 {
231 | font-size: 16px;
232 | }
233 |
234 | .markdown-body h5 {
235 | font-size: 14px;
236 | }
237 |
238 | .markdown-body h5,
239 | .markdown-body h6 {
240 | font-weight: 600;
241 | }
242 |
243 | .markdown-body h6 {
244 | font-size: 12px;
245 | }
246 |
247 | .markdown-body p {
248 | margin-top: 0;
249 | margin-bottom: 10px;
250 | }
251 |
252 | .markdown-body blockquote {
253 | margin: 0;
254 | }
255 |
256 | .markdown-body ol,
257 | .markdown-body ul {
258 | padding-left: 0;
259 | margin-top: 0;
260 | margin-bottom: 0;
261 | }
262 |
263 | .markdown-body ol ol,
264 | .markdown-body ul ol {
265 | list-style-type: lower-roman;
266 | }
267 |
268 | .markdown-body ol ol ol,
269 | .markdown-body ol ul ol,
270 | .markdown-body ul ol ol,
271 | .markdown-body ul ul ol {
272 | list-style-type: lower-alpha;
273 | }
274 |
275 | .markdown-body dd {
276 | margin-left: 0;
277 | }
278 |
279 | .markdown-body code,
280 | .markdown-body pre {
281 | font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
282 | font-size: 12px;
283 | }
284 |
285 | .markdown-body pre {
286 | margin-top: 0;
287 | margin-bottom: 0;
288 | }
289 |
290 | .markdown-body input::-webkit-inner-spin-button,
291 | .markdown-body input::-webkit-outer-spin-button {
292 | margin: 0;
293 | -webkit-appearance: none;
294 | appearance: none;
295 | }
296 |
297 | .markdown-body :checked+.radio-label {
298 | position: relative;
299 | z-index: 1;
300 | border-color: #0366d6;
301 | }
302 |
303 | .markdown-body .border {
304 | border: 1px solid #e1e4e8 !important;
305 | }
306 |
307 | .markdown-body .border-0 {
308 | border: 0 !important;
309 | }
310 |
311 | .markdown-body .border-bottom {
312 | border-bottom: 1px solid #e1e4e8 !important;
313 | }
314 |
315 | .markdown-body .rounded-1 {
316 | border-radius: 3px !important;
317 | }
318 |
319 | .markdown-body .bg-white {
320 | background-color: #fff !important;
321 | }
322 |
323 | .markdown-body .bg-gray-light {
324 | background-color: #fafbfc !important;
325 | }
326 |
327 | .markdown-body .text-gray-light {
328 | color: #6a737d !important;
329 | }
330 |
331 | .markdown-body .mb-0 {
332 | margin-bottom: 0 !important;
333 | }
334 |
335 | .markdown-body .my-2 {
336 | margin-top: 8px !important;
337 | margin-bottom: 8px !important;
338 | }
339 |
340 | .markdown-body .pl-0 {
341 | padding-left: 0 !important;
342 | }
343 |
344 | .markdown-body .py-0 {
345 | padding-top: 0 !important;
346 | padding-bottom: 0 !important;
347 | }
348 |
349 | .markdown-body .pl-1 {
350 | padding-left: 4px !important;
351 | }
352 |
353 | .markdown-body .pl-2 {
354 | padding-left: 8px !important;
355 | }
356 |
357 | .markdown-body .py-2 {
358 | padding-top: 8px !important;
359 | padding-bottom: 8px !important;
360 | }
361 |
362 | .markdown-body .pl-3,
363 | .markdown-body .px-3 {
364 | padding-left: 16px !important;
365 | }
366 |
367 | .markdown-body .px-3 {
368 | padding-right: 16px !important;
369 | }
370 |
371 | .markdown-body .pl-4 {
372 | padding-left: 24px !important;
373 | }
374 |
375 | .markdown-body .pl-5 {
376 | padding-left: 32px !important;
377 | }
378 |
379 | .markdown-body .pl-6 {
380 | padding-left: 40px !important;
381 | }
382 |
383 | .markdown-body .f6 {
384 | font-size: 12px !important;
385 | }
386 |
387 | .markdown-body .lh-condensed {
388 | line-height: 1.25 !important;
389 | }
390 |
391 | .markdown-body .text-bold {
392 | font-weight: 600 !important;
393 | }
394 |
395 | .markdown-body .pl-c {
396 | color: #6a737d;
397 | }
398 |
399 | .markdown-body .pl-c1,
400 | .markdown-body .pl-s .pl-v {
401 | color: #005cc5;
402 | }
403 |
404 | .markdown-body .pl-e,
405 | .markdown-body .pl-en {
406 | color: #6f42c1;
407 | }
408 |
409 | .markdown-body .pl-s .pl-s1,
410 | .markdown-body .pl-smi {
411 | color: #24292e;
412 | }
413 |
414 | .markdown-body .pl-ent {
415 | color: #22863a;
416 | }
417 |
418 | .markdown-body .pl-k {
419 | color: #d73a49;
420 | }
421 |
422 | .markdown-body .pl-pds,
423 | .markdown-body .pl-s,
424 | .markdown-body .pl-s .pl-pse .pl-s1,
425 | .markdown-body .pl-sr,
426 | .markdown-body .pl-sr .pl-cce,
427 | .markdown-body .pl-sr .pl-sra,
428 | .markdown-body .pl-sr .pl-sre {
429 | color: #032f62;
430 | }
431 |
432 | .markdown-body .pl-smw,
433 | .markdown-body .pl-v {
434 | color: #e36209;
435 | }
436 |
437 | .markdown-body .pl-bu {
438 | color: #b31d28;
439 | }
440 |
441 | .markdown-body .pl-ii {
442 | color: #fafbfc;
443 | background-color: #b31d28;
444 | }
445 |
446 | .markdown-body .pl-c2 {
447 | color: #fafbfc;
448 | background-color: #d73a49;
449 | }
450 |
451 | .markdown-body .pl-c2:before {
452 | content: "^M";
453 | }
454 |
455 | .markdown-body .pl-sr .pl-cce {
456 | font-weight: 700;
457 | color: #22863a;
458 | }
459 |
460 | .markdown-body .pl-ml {
461 | color: #735c0f;
462 | }
463 |
464 | .markdown-body .pl-mh,
465 | .markdown-body .pl-mh .pl-en,
466 | .markdown-body .pl-ms {
467 | font-weight: 700;
468 | color: #005cc5;
469 | }
470 |
471 | .markdown-body .pl-mi {
472 | font-style: italic;
473 | color: #24292e;
474 | }
475 |
476 | .markdown-body .pl-mb {
477 | font-weight: 700;
478 | color: #24292e;
479 | }
480 |
481 | .markdown-body .pl-md {
482 | color: #b31d28;
483 | background-color: #ffeef0;
484 | }
485 |
486 | .markdown-body .pl-mi1 {
487 | color: #22863a;
488 | background-color: #f0fff4;
489 | }
490 |
491 | .markdown-body .pl-mc {
492 | color: #e36209;
493 | background-color: #ffebda;
494 | }
495 |
496 | .markdown-body .pl-mi2 {
497 | color: #f6f8fa;
498 | background-color: #005cc5;
499 | }
500 |
501 | .markdown-body .pl-mdr {
502 | font-weight: 700;
503 | color: #6f42c1;
504 | }
505 |
506 | .markdown-body .pl-ba {
507 | color: #586069;
508 | }
509 |
510 | .markdown-body .pl-sg {
511 | color: #959da5;
512 | }
513 |
514 | .markdown-body .pl-corl {
515 | text-decoration: underline;
516 | color: #032f62;
517 | }
518 |
519 | .markdown-body .mb-0 {
520 | margin-bottom: 0 !important;
521 | }
522 |
523 | .markdown-body .my-2 {
524 | margin-bottom: 8px !important;
525 | }
526 |
527 | .markdown-body .my-2 {
528 | margin-top: 8px !important;
529 | }
530 |
531 | .markdown-body .pl-0 {
532 | padding-left: 0 !important;
533 | }
534 |
535 | .markdown-body .py-0 {
536 | padding-top: 0 !important;
537 | padding-bottom: 0 !important;
538 | }
539 |
540 | .markdown-body .pl-1 {
541 | padding-left: 4px !important;
542 | }
543 |
544 | .markdown-body .pl-2 {
545 | padding-left: 8px !important;
546 | }
547 |
548 | .markdown-body .py-2 {
549 | padding-top: 8px !important;
550 | padding-bottom: 8px !important;
551 | }
552 |
553 | .markdown-body .pl-3 {
554 | padding-left: 16px !important;
555 | }
556 |
557 | .markdown-body .pl-4 {
558 | padding-left: 24px !important;
559 | }
560 |
561 | .markdown-body .pl-5 {
562 | padding-left: 32px !important;
563 | }
564 |
565 | .markdown-body .pl-6 {
566 | padding-left: 40px !important;
567 | }
568 |
569 | .markdown-body .pl-7 {
570 | padding-left: 48px !important;
571 | }
572 |
573 | .markdown-body .pl-8 {
574 | padding-left: 64px !important;
575 | }
576 |
577 | .markdown-body .pl-9 {
578 | padding-left: 80px !important;
579 | }
580 |
581 | .markdown-body .pl-10 {
582 | padding-left: 96px !important;
583 | }
584 |
585 | .markdown-body .pl-11 {
586 | padding-left: 112px !important;
587 | }
588 |
589 | .markdown-body .pl-12 {
590 | padding-left: 128px !important;
591 | }
592 |
593 | .markdown-body hr {
594 | border-bottom-color: #eee;
595 | }
596 |
597 | .markdown-body kbd {
598 | display: inline-block;
599 | padding: 3px 5px;
600 | font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
601 | line-height: 10px;
602 | color: #444d56;
603 | vertical-align: middle;
604 | background-color: #fafbfc;
605 | border: 1px solid #d1d5da;
606 | border-radius: 3px;
607 | box-shadow: inset 0 -1px 0 #d1d5da;
608 | }
609 |
610 | .markdown-body:after,
611 | .markdown-body:before {
612 | display: table;
613 | content: "";
614 | }
615 |
616 | .markdown-body:after {
617 | clear: both;
618 | }
619 |
620 | .markdown-body>:first-child {
621 | margin-top: 0 !important;
622 | }
623 |
624 | .markdown-body>:last-child {
625 | margin-bottom: 0 !important;
626 | }
627 |
628 | .markdown-body a:not([href]) {
629 | color: inherit;
630 | text-decoration: none;
631 | }
632 |
633 | .markdown-body blockquote,
634 | .markdown-body details,
635 | .markdown-body dl,
636 | .markdown-body ol,
637 | .markdown-body p,
638 | .markdown-body pre,
639 | .markdown-body table,
640 | .markdown-body ul {
641 | margin-top: 0;
642 | margin-bottom: 16px;
643 | }
644 |
645 | .markdown-body hr {
646 | height: .25em;
647 | padding: 0;
648 | margin: 24px 0;
649 | background-color: #e1e4e8;
650 | border: 0;
651 | }
652 |
653 | .markdown-body blockquote {
654 | padding: 0 1em;
655 | color: #6a737d;
656 | border-left: .25em solid #dfe2e5;
657 | }
658 |
659 | .markdown-body blockquote>:first-child {
660 | margin-top: 0;
661 | }
662 |
663 | .markdown-body blockquote>:last-child {
664 | margin-bottom: 0;
665 | }
666 |
667 | .markdown-body h1,
668 | .markdown-body h2,
669 | .markdown-body h3,
670 | .markdown-body h4,
671 | .markdown-body h5,
672 | .markdown-body h6 {
673 | margin-top: 24px;
674 | margin-bottom: 16px;
675 | font-weight: 600;
676 | line-height: 1.25;
677 | }
678 |
679 | .markdown-body h1 {
680 | font-size: 2em;
681 | }
682 |
683 | .markdown-body h1,
684 | .markdown-body h2 {
685 | padding-bottom: .3em;
686 | border-bottom: 1px solid #eaecef;
687 | }
688 |
689 | .markdown-body h2 {
690 | font-size: 1.5em;
691 | }
692 |
693 | .markdown-body h3 {
694 | font-size: 1.25em;
695 | }
696 |
697 | .markdown-body h4 {
698 | font-size: 1em;
699 | }
700 |
701 | .markdown-body h5 {
702 | font-size: .875em;
703 | }
704 |
705 | .markdown-body h6 {
706 | font-size: .85em;
707 | color: #6a737d;
708 | }
709 |
710 | .markdown-body ol,
711 | .markdown-body ul {
712 | padding-left: 2em;
713 | }
714 |
715 | .markdown-body ol ol,
716 | .markdown-body ol ul,
717 | .markdown-body ul ol,
718 | .markdown-body ul ul {
719 | margin-top: 0;
720 | margin-bottom: 0;
721 | }
722 |
723 | .markdown-body li {
724 | word-wrap: break-all;
725 | }
726 |
727 | .markdown-body li>p {
728 | margin-top: 16px;
729 | }
730 |
731 | .markdown-body li+li {
732 | margin-top: .25em;
733 | }
734 |
735 | .markdown-body dl {
736 | padding: 0;
737 | }
738 |
739 | .markdown-body dl dt {
740 | padding: 0;
741 | margin-top: 16px;
742 | font-size: 1em;
743 | font-style: italic;
744 | font-weight: 600;
745 | }
746 |
747 | .markdown-body dl dd {
748 | padding: 0 16px;
749 | margin-bottom: 16px;
750 | }
751 |
752 | .markdown-body table {
753 | display: block;
754 | width: 100%;
755 | overflow: auto;
756 | }
757 |
758 | .markdown-body table th {
759 | font-weight: 600;
760 | }
761 |
762 | .markdown-body table td,
763 | .markdown-body table th {
764 | padding: 6px 13px;
765 | border: 1px solid #dfe2e5;
766 | }
767 |
768 | .markdown-body table tr {
769 | background-color: #fff;
770 | border-top: 1px solid #c6cbd1;
771 | }
772 |
773 | .markdown-body table tr:nth-child(2n) {
774 | background-color: #f6f8fa;
775 | }
776 |
777 | .markdown-body img {
778 | max-width: 100%;
779 | box-sizing: initial;
780 | background-color: #fff;
781 | }
782 |
783 | .markdown-body img[align=right] {
784 | padding-left: 20px;
785 | }
786 |
787 | .markdown-body img[align=left] {
788 | padding-right: 20px;
789 | }
790 |
791 | .markdown-body code {
792 | padding: .2em .4em;
793 | margin: 0;
794 | font-size: 85%;
795 | background-color: rgba(27, 31, 35, .05);
796 | border-radius: 3px;
797 | }
798 |
799 | .markdown-body pre {
800 | word-wrap: normal;
801 | }
802 |
803 | .markdown-body pre>code {
804 | padding: 0;
805 | margin: 0;
806 | font-size: 100%;
807 | word-break: normal;
808 | white-space: pre;
809 | background: transparent;
810 | border: 0;
811 | }
812 |
813 | .markdown-body .highlight {
814 | margin-bottom: 16px;
815 | }
816 |
817 | .markdown-body .highlight pre {
818 | margin-bottom: 0;
819 | word-break: normal;
820 | }
821 |
822 | .markdown-body .highlight pre,
823 | .markdown-body pre {
824 | padding: 16px;
825 | overflow: auto;
826 | font-size: 85%;
827 | line-height: 1.45;
828 | background-color: #f6f8fa;
829 | border-radius: 3px;
830 | }
831 |
832 | .markdown-body pre code {
833 | display: inline;
834 | max-width: auto;
835 | padding: 0;
836 | margin: 0;
837 | overflow: visible;
838 | line-height: inherit;
839 | word-wrap: normal;
840 | background-color: initial;
841 | border: 0;
842 | }
843 |
844 | .markdown-body .commit-tease-sha {
845 | display: inline-block;
846 | font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
847 | font-size: 90%;
848 | color: #444d56;
849 | }
850 |
851 | .markdown-body .full-commit .btn-outline:not(:disabled):hover {
852 | color: #005cc5;
853 | border-color: #005cc5;
854 | }
855 |
856 | .markdown-body .blob-wrapper {
857 | overflow-x: auto;
858 | overflow-y: hidden;
859 | }
860 |
861 | .markdown-body .blob-wrapper-embedded {
862 | max-height: 240px;
863 | overflow-y: auto;
864 | }
865 |
866 | .markdown-body .blob-num {
867 | width: 1%;
868 | min-width: 50px;
869 | padding-right: 10px;
870 | padding-left: 10px;
871 | font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
872 | font-size: 12px;
873 | line-height: 20px;
874 | color: rgba(27, 31, 35, .3);
875 | text-align: right;
876 | white-space: nowrap;
877 | vertical-align: top;
878 | cursor: pointer;
879 | -webkit-user-select: none;
880 | -moz-user-select: none;
881 | -ms-user-select: none;
882 | user-select: none;
883 | }
884 |
885 | .markdown-body .blob-num:hover {
886 | color: rgba(27, 31, 35, .6);
887 | }
888 |
889 | .markdown-body .blob-num:before {
890 | content: attr(data-line-number);
891 | }
892 |
893 | .markdown-body .blob-code {
894 | position: relative;
895 | padding-right: 10px;
896 | padding-left: 10px;
897 | line-height: 20px;
898 | vertical-align: top;
899 | }
900 |
901 | .markdown-body .blob-code-inner {
902 | overflow: visible;
903 | font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
904 | font-size: 12px;
905 | color: #24292e;
906 | word-wrap: normal;
907 | white-space: pre;
908 | }
909 |
910 | .markdown-body .pl-token.active,
911 | .markdown-body .pl-token:hover {
912 | cursor: pointer;
913 | background: #ffea7f;
914 | }
915 |
916 | .markdown-body .tab-size[data-tab-size="1"] {
917 | -moz-tab-size: 1;
918 | tab-size: 1;
919 | }
920 |
921 | .markdown-body .tab-size[data-tab-size="2"] {
922 | -moz-tab-size: 2;
923 | tab-size: 2;
924 | }
925 |
926 | .markdown-body .tab-size[data-tab-size="3"] {
927 | -moz-tab-size: 3;
928 | tab-size: 3;
929 | }
930 |
931 | .markdown-body .tab-size[data-tab-size="4"] {
932 | -moz-tab-size: 4;
933 | tab-size: 4;
934 | }
935 |
936 | .markdown-body .tab-size[data-tab-size="5"] {
937 | -moz-tab-size: 5;
938 | tab-size: 5;
939 | }
940 |
941 | .markdown-body .tab-size[data-tab-size="6"] {
942 | -moz-tab-size: 6;
943 | tab-size: 6;
944 | }
945 |
946 | .markdown-body .tab-size[data-tab-size="7"] {
947 | -moz-tab-size: 7;
948 | tab-size: 7;
949 | }
950 |
951 | .markdown-body .tab-size[data-tab-size="8"] {
952 | -moz-tab-size: 8;
953 | tab-size: 8;
954 | }
955 |
956 | .markdown-body .tab-size[data-tab-size="9"] {
957 | -moz-tab-size: 9;
958 | tab-size: 9;
959 | }
960 |
961 | .markdown-body .tab-size[data-tab-size="10"] {
962 | -moz-tab-size: 10;
963 | tab-size: 10;
964 | }
965 |
966 | .markdown-body .tab-size[data-tab-size="11"] {
967 | -moz-tab-size: 11;
968 | tab-size: 11;
969 | }
970 |
971 | .markdown-body .tab-size[data-tab-size="12"] {
972 | -moz-tab-size: 12;
973 | tab-size: 12;
974 | }
975 |
976 | .markdown-body .task-list-item {
977 | list-style-type: none;
978 | }
979 |
980 | .markdown-body .task-list-item+.task-list-item {
981 | margin-top: 3px;
982 | }
983 |
984 | .markdown-body .task-list-item input {
985 | margin: 0 .2em .25em -1.6em;
986 | vertical-align: middle;
987 | }
988 |
--------------------------------------------------------------------------------
/src/components/pages/Comments.jsx:
--------------------------------------------------------------------------------
1 | import { formatDistance } from 'date-fns';
2 | import React from 'react';
3 | import ReactMarkdown from 'react-markdown';
4 | import { useQuery } from 'react-query';
5 |
6 | export default function Comments({ issueNumber }) {
7 | const {
8 | isLoading,
9 | isSuccess,
10 | data: comments,
11 | } = useQuery(['comments', issueNumber], fetchComments);
12 |
13 | function fetchComments() {
14 | return fetch(
15 | `https://api.github.com/repos/facebook/create-react-app/issues/${issueNumber}/comments`
16 | ).then(response => response.json());
17 | }
18 |
19 | return (
20 | <>
21 | {isLoading && Loading...
}
22 | {isSuccess && (
23 | <>
24 | {comments.map(comment => (
25 |
26 |
27 |
32 |
33 |
34 |
35 |
{comment.user.login}{' '}
36 | commented{' '}
37 | {formatDistance(new Date(comment.created_at), new Date(), {
38 | addSuffix: true,
39 | })}
40 |
41 |
42 |
43 |
44 |
45 |
46 | ))}
47 | >
48 | )}
49 | >
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/pages/Details.jsx:
--------------------------------------------------------------------------------
1 | import { formatDistance } from 'date-fns';
2 | import React from 'react';
3 | import ReactMarkdown from 'react-markdown';
4 | import { useQuery } from 'react-query';
5 | import { useParams } from 'react-router-dom';
6 | import Comments from './Comments';
7 |
8 | export default function Details() {
9 | const params = useParams();
10 |
11 | const {
12 | isLoading,
13 | isSuccess,
14 | data: issue,
15 | } = useQuery(['issue', params.id], fetchIssue);
16 |
17 | function fetchIssue() {
18 | return fetch(
19 | `https://api.github.com/repos/facebook/create-react-app/issues/${params.id}`
20 | ).then(response => response.json());
21 | }
22 |
23 | return (
24 |
25 | {isLoading &&
Loading...
}
26 | {isSuccess && (
27 | <>
28 |
29 | {issue.title} #{issue.number}
30 |
31 |
32 |
{issue.user.login} opened this
33 | issue{' '}
34 | {formatDistance(new Date(issue.created_at), new Date(), {
35 | addSuffix: true,
36 | })}
37 |
38 | >
39 | )}
40 |
41 | {isSuccess && (
42 |
43 |
44 |
45 |
46 |
47 |
48 |
mdaj06 commented{' '}
49 | {formatDistance(new Date(issue.created_at), new Date(), {
50 | addSuffix: true,
51 | })}
52 |
53 |
54 |
55 |
56 |
57 |
58 | )}
59 |
60 |
61 | {isSuccess &&
}
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/pages/IconClosed.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function IconClosed() {
4 | return (
5 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/pages/IconOpen.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function IconOpen() {
4 | return (
5 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/pages/Issues.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { useQuery } from 'react-query';
3 | import IconOpen from './IconOpen';
4 | import IconClosed from './IconClosed';
5 | import { formatDistance } from 'date-fns';
6 | import { Link } from 'react-router-dom';
7 |
8 | export default function Contact() {
9 | const [filter, setFilter] = useState('open');
10 |
11 | const {
12 | isLoading,
13 | isSuccess,
14 | data: issues,
15 | } = useQuery(['issues', filter], fetchIssues);
16 |
17 | const { isSuccess: isSuccessIssuesOpen, data: issuesOpen } = useQuery(
18 | 'issuesOpen',
19 | fetchIssuesOpen
20 | );
21 |
22 | const { isSuccess: isSuccessIssuesClosed, data: issuesClosed } = useQuery(
23 | 'issuesClosed',
24 | fetchIssuesClosed
25 | );
26 |
27 | function fetchIssues() {
28 | return fetch(
29 | `https://api.github.com/repos/facebook/create-react-app/issues?per_page=10&state=${filter}`
30 | ).then(response => response.json());
31 | }
32 |
33 | function fetchIssuesOpen() {
34 | return fetch(
35 | `https://api.github.com/search/issues?q=repo:facebook/create-react-app+type:issue+state:open&per_page=1`
36 | ).then(response => response.json());
37 | }
38 |
39 | function fetchIssuesClosed() {
40 | return fetch(
41 | `https://api.github.com/search/issues?q=repo:facebook/create-react-app+type:issue+state:closed&per_page=1`
42 | ).then(response => response.json());
43 | }
44 |
45 | return (
46 |
47 | {isLoading &&
Loading...
}
48 |
49 | {isSuccess && (
50 |
51 |
52 |
53 | facebook / create-react-app
54 |
55 |
56 |
64 |
72 |
73 |
74 |
75 | {issues.map(issue => (
76 |
77 |
78 | {issue.state === 'open' &&
}
79 | {issue.state === 'closed' &&
}
80 |
81 |
{issue.title}
82 |
83 | #{issue.number} opened{' '}
84 | {formatDistance(new Date(issue.created_at), new Date(), {
85 | addSuffix: true,
86 | })}{' '}
87 | by {issue.user.login}
88 |
89 |
90 |
91 | {issue.comments > 0 && (
92 |
96 |
109 |
{issue.comments}
110 |
111 | )}
112 |
113 | ))}
114 |
115 |
116 | )}
117 |
118 | );
119 | }
120 |
--------------------------------------------------------------------------------
/src/components/pages/NoMatch.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function NoMatch() {
4 | return 404 Page Not Found!
;
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/pages/api.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import Reddit from '../api/Reddit';
3 | import Joke from '../api/Joke';
4 |
5 | export default function About() {
6 | const [redditVisible, setRedditVisible] = useState(false);
7 | const [jokeVisible, setJokeVisible] = useState(false);
8 |
9 | return (
10 |
11 |
12 |
18 |
24 |
25 |
26 | {redditVisible && (
27 |
28 |
29 |
30 | )}
31 | {jokeVisible && (
32 |
33 |
34 |
35 | )}
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/src/context/TodosContext.js:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react';
2 |
3 | export const TodosContext = createContext();
4 |
--------------------------------------------------------------------------------
/src/hooks/useLocalStorage.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | function useLocalStorage(key, initialValue) {
4 | const [value, setValue] = useState(() => {
5 | const item = localStorage.getItem(key);
6 | return item ? JSON.parse(item) : initialValue;
7 | });
8 |
9 | useEffect(() => {
10 | localStorage.setItem(key, JSON.stringify(value));
11 | }, [key, value]);
12 | return [value, setValue];
13 | }
14 |
15 | export default useLocalStorage;
16 |
--------------------------------------------------------------------------------
/src/hooks/useToggle.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | function useToggle(initialState = true) {
4 | const [visible, setVisible] = useState(initialState);
5 |
6 | function toggle() {
7 | setVisible(prevVisible => !prevVisible);
8 | }
9 | return [visible, toggle];
10 | }
11 |
12 | export default useToggle;
13 |
--------------------------------------------------------------------------------
/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 { createRoot } from 'react-dom/client';
3 | import './index.css';
4 | import Root from './components/Root';
5 | import { QueryClient, QueryClientProvider } from 'react-query';
6 | import { ReactQueryDevtools } from 'react-query/devtools';
7 |
8 | const root = document.getElementById('root');
9 | const queryClient = new QueryClient();
10 | createRoot(root).render(
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html, body, div, span, applet, object, iframe,
7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8 | a, abbr, acronym, address, big, cite, code,
9 | del, dfn, em, img, ins, kbd, q, s, samp,
10 | small, strike, strong, sub, sup, tt, var,
11 | b, u, i, center,
12 | dl, dt, dd, ol, ul, li,
13 | fieldset, form, label, legend,
14 | table, caption, tbody, tfoot, thead, tr, th, td,
15 | article, aside, canvas, details, embed,
16 | figure, figcaption, footer, header, hgroup,
17 | menu, nav, output, ruby, section, summary,
18 | time, mark, audio, video {
19 | margin: 0;
20 | padding: 0;
21 | border: 0;
22 | font-size: 100%;
23 | font: inherit;
24 | vertical-align: baseline;
25 | }
26 | /* HTML5 display-role reset for older browsers */
27 | article, aside, details, figcaption, figure,
28 | footer, header, hgroup, menu, nav, section {
29 | display: block;
30 | }
31 | body {
32 | line-height: 1;
33 | }
34 | ol, ul {
35 | list-style: none;
36 | }
37 | blockquote, q {
38 | quotes: none;
39 | }
40 | blockquote:before, blockquote:after,
41 | q:before, q:after {
42 | content: '';
43 | content: none;
44 | }
45 | table {
46 | border-collapse: collapse;
47 | border-spacing: 0;
48 | }
49 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------