├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── day001
└── counter-game
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.js
│ ├── components
│ └── HomePage
│ │ ├── HomePage.css
│ │ └── HomePage.js
│ ├── index.css
│ └── index.js
├── day002
└── custom-form
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.js
│ ├── components
│ └── CustomForm
│ │ ├── CustomForm.css
│ │ └── CustomForm.js
│ ├── index.css
│ └── index.js
├── day003
└── tik-tac-toe
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ ├── Board.js
│ ├── Game.js
│ ├── Square.js
│ └── common
│ │ └── Utils.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── day004
└── stopwatch
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── components
│ ├── App.css
│ ├── App.js
│ ├── common
│ │ ├── button
│ │ │ ├── Button.css
│ │ │ └── Button.js
│ │ └── header
│ │ │ ├── Header.css
│ │ │ └── Header.js
│ ├── stopwatch
│ │ ├── Stopwatch.css
│ │ └── Stopwatch.js
│ └── timer
│ │ ├── Timer.css
│ │ └── Timer.js
│ ├── index.css
│ └── index.js
├── day005
└── snake-game
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── components
│ ├── Food.js
│ └── Snake.js
│ ├── index.css
│ └── index.js
├── day006
└── dino-game
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── components
│ └── Dino
│ │ ├── Dino.css
│ │ ├── Dino.js
│ │ └── img
│ │ ├── cactus.png
│ │ └── trex.png
│ ├── index.css
│ └── index.js
├── day007
└── drag-drop
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── index.css
│ └── index.js
├── day008
└── tags-input
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── component
│ └── TagInput.js
│ ├── index.css
│ └── index.js
├── day009
└── shoppinn
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── assets
│ │ ├── benjamin-rascoe-KF-q_SGqswg-unsplash.jpg
│ │ ├── caio-coelho-xFmXLq_KJxg-unsplash.jpg
│ │ ├── chris-lynch-pRdi2no2fvs-unsplash.jpg
│ │ ├── dami-adebayo-k6aQzmIbR1s-unsplash.jpg
│ │ ├── dom-hill-nimElTcTNyY-unsplash.jpg
│ │ ├── faith-yarn-Wr0TpKqf26s-unsplash.jpg
│ │ ├── freestocks-8hAsLeE6Fbo-unsplash.jpg
│ │ ├── irene-kredenets-dwKiHoqqxk8-unsplash.jpg
│ │ ├── paul-gaudriault-a-QH9MAAVNI-unsplash.jpg
│ │ ├── phil-monte-4V4t0JcOM5E-unsplash.jpg
│ │ └── revolt-164_6wVEHfI-unsplash.jpg
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── components
│ ├── cart
│ │ ├── Cart.css
│ │ └── Cart.js
│ ├── checkout
│ │ ├── Checkout.css
│ │ └── Checkout.js
│ ├── home
│ │ ├── HomePage.css
│ │ └── HomePage.js
│ ├── item
│ │ ├── Item.css
│ │ └── Item.js
│ ├── itemDetail
│ │ ├── ItemDetail.css
│ │ └── ItemDetail.js
│ ├── itemList
│ │ ├── ItemList.css
│ │ └── ItemList.js
│ ├── navbar
│ │ ├── Navbar.css
│ │ └── Navbar.js
│ └── orders
│ │ ├── Orders.css
│ │ └── Orders.js
│ ├── context
│ ├── AppReducer.js
│ └── GlobalState.js
│ ├── index.css
│ ├── index.js
│ └── mockData
│ └── items.json
├── day010
└── personal-portfolio
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon-VA.svg
│ ├── index.html
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── components
│ ├── AboutPage.css
│ ├── AboutPage.js
│ ├── ContactPage.css
│ ├── ContactPage.js
│ ├── EducationPage.css
│ ├── EducationPage.js
│ ├── HomePage.css
│ ├── HomePage.js
│ ├── Navbar.css
│ ├── Navbar.js
│ ├── ProjectPage.css
│ ├── ProjectPage.js
│ ├── SkillPage.css
│ └── SkillPage.js
│ ├── index.css
│ └── index.js
├── day011
└── shopping_cart
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── api
│ └── index.js
│ ├── components
│ └── Card.jsx
│ ├── jsconfig.json
│ ├── next.config.js
│ ├── package-lock.json
│ ├── package.json
│ ├── pages
│ ├── Page.jsx
│ ├── _app.js
│ ├── _document.js
│ ├── api
│ │ └── hello.js
│ └── index.js
│ ├── postcss.config.js
│ ├── public
│ ├── favicon.ico
│ ├── machines
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ ├── 6.png
│ │ └── 7.png
│ ├── next.svg
│ ├── thirteen.svg
│ └── vercel.svg
│ ├── slices
│ └── cartSlice.js
│ ├── store
│ └── store.js
│ ├── styles
│ └── globals.css
│ └── tailwind.config.js
├── day012
└── sticky-notes
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── add_new_note.png
│ ├── favicon.ico
│ ├── index.html
│ ├── manifest.json
│ ├── robots.txt
│ └── sticky-note-logo.png
│ └── src
│ ├── App.js
│ ├── components
│ ├── ColorCustomizer.css
│ ├── ColorCustomizer.js
│ ├── EmptyNotes.css
│ ├── EmptyNotes.js
│ ├── Header.css
│ ├── Header.js
│ ├── StickyNote.css
│ ├── StickyNote.js
│ ├── StickyNotesGrid.css
│ ├── StickyNotesGrid.js
│ └── SvgIcons.js
│ ├── context
│ └── StickyNotesContext.js
│ ├── hooks
│ └── useDebounce.js
│ ├── index.css
│ └── index.js
├── day013
└── music-player
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ ├── robots.txt
│ └── zunzun.png
│ └── src
│ ├── App.js
│ ├── components
│ ├── Controls.css
│ ├── Controls.js
│ ├── DisplayTrack.css
│ ├── DisplayTrack.js
│ ├── Header.css
│ ├── Header.js
│ ├── MusicPlayer.css
│ ├── MusicPlayer.js
│ ├── PlayList.css
│ ├── PlayList.js
│ ├── Player.css
│ ├── Player.js
│ ├── ProgressBar.css
│ └── ProgressBar.js
│ ├── context
│ └── TrackContext.js
│ ├── data.js
│ ├── helpers.js
│ ├── hooks
│ └── useTrack.js
│ ├── index.css
│ └── index.js
├── day014
└── Random-Dog-Pic-Generate
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ └── dog.svg
│ ├── src
│ ├── App.jsx
│ ├── Assets
│ │ └── dog.jpg
│ ├── Components
│ │ ├── BreedSelector.jsx
│ │ ├── Dog.jsx
│ │ ├── DogImage.jsx
│ │ ├── Footer.jsx
│ │ └── Header.jsx
│ ├── index.css
│ └── main.jsx
│ └── vite.config.js
├── package-lock.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | *node_modules
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to react-project-ideas
2 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
3 |
4 | - Add a new project
5 | - Reporting a bug
6 | - Submitting a fix
7 | - Proposing new features
8 | - Becoming a maintainer
9 |
10 | ## We Develop with Github
11 | We use github to host code, to track issues and feature requests, as well as accept pull requests.
12 |
13 | ## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests
14 | Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests:
15 |
16 | 1. Fork the repo and create your branch from `master`.
17 | 2. Add your project or make any change to your branch.
18 | 3. Issue that pull request!
19 |
20 | ## Any contributions you make will be under the MIT Software License
21 | In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern.
22 |
23 | ## Report bugs using Github's [issues](https://github.com/Vasu7389/react-project-ideas/issues)
24 | We use GitHub issues to track public bugs. Report a bug by opening a new issue, it's that easy!
25 |
26 | ## License
27 | By contributing, you agree that your contributions will be licensed under its MIT License.
28 |
29 | ## References
30 | This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md)
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Vasu7389
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Vulnerability
4 |
5 | If you find a security vulnerability please contact awasthi.vasu65@gmail.com with details.
6 |
--------------------------------------------------------------------------------
/day001/counter-game/.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 |
--------------------------------------------------------------------------------
/day001/counter-game/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "counter-game",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/day001/counter-game/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/react-project-ideas/fb3bf45a86a2a2556c85c6252da0873886cf8135/day001/counter-game/public/favicon.ico
--------------------------------------------------------------------------------
/day001/counter-game/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 | {Object.keys(props).map((key) =>
8 | props[key].isInput ? (
9 | <>
10 | {props[key].label}
11 |
19 | >
20 | ) : (
21 |
27 | {props[key].buttonText}
28 |
29 | )
30 | )}
31 |
32 | );
33 | }
34 |
35 | export default CustomForm;
36 |
--------------------------------------------------------------------------------
/day002/custom-form/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 |
--------------------------------------------------------------------------------
/day002/custom-form/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App";
4 |
5 | const root = ReactDOM.createRoot(document.getElementById("root"));
6 | root.render(
7 |
18 | {Object.keys(COLOR_MAP).map(color => (
19 |
props.onColorChange(COLOR_MAP[color])}
23 | />
24 | ))}
25 |
26 | )
27 | }
28 |
29 | export default ColorCustomizer
--------------------------------------------------------------------------------
/day012/sticky-notes/src/components/EmptyNotes.css:
--------------------------------------------------------------------------------
1 | .empty-notes-container {
2 | display: grid;
3 | grid-template-columns: 1fr;
4 | grid-template-rows: 1fr;
5 | gap: 1rem;
6 | margin: 5rem;
7 | place-items: center;
8 | }
9 |
10 | .empty-notes {
11 | display: flex;
12 | justify-content: center;
13 | align-items: center;
14 | font-size: 1rem;
15 | color: #706c60;
16 | }
17 |
18 | .empty-notes-image {
19 | width: 280px;
20 | height: 250px;
21 | }
22 |
23 | .add-note-button {
24 | display: flex;
25 | justify-content: space-between;
26 | align-items: center;
27 | margin-top: 1rem;
28 | padding: 0.7rem 1rem;
29 | border: none;
30 | border-radius: 5px;
31 | background-color: #ffd500;
32 | color: #000;
33 | font-size: 1rem;
34 | font-weight: 600;
35 | cursor: pointer;
36 | transition: all 0.2s ease-in-out;
37 |
38 | &:hover {
39 | background-color: #f5ba13;
40 | opacity: 0.8;
41 | scale: 1.1;
42 | }
43 | }
--------------------------------------------------------------------------------
/day012/sticky-notes/src/components/EmptyNotes.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useStickyNotes } from '../context/StickyNotesContext';
3 | import { PlusSvgIcon } from './SvgIcons';
4 | import "./EmptyNotes.css"
5 |
6 | const EmptyNotes = () => {
7 | const { notes, addNewNote } = useStickyNotes();
8 |
9 | // If there are notes, return null
10 | if (notes.length > 0) return null
11 |
12 | return (
13 |
14 |
15 |
Not notes yet. Please add a new note clicking on the button below.
16 |
17 |
18 | Add new note
19 |
20 |
21 | )
22 | }
23 |
24 | export default EmptyNotes
--------------------------------------------------------------------------------
/day012/sticky-notes/src/components/Header.css:
--------------------------------------------------------------------------------
1 | .header {
2 | display: flex;
3 | justify-content: space-between;
4 | align-items: center;
5 | width: 100%;
6 | padding: 0 1rem;
7 | background-color: rgb(107 99 255);
8 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
9 | height: 80px;
10 | position: fixed;
11 | top: 0;
12 | z-index: 1;
13 | }
14 |
15 | .logo-container {
16 | display: flex;
17 | align-items: center;
18 | margin-left: 50px;
19 | }
20 |
21 | .app-name {
22 | color: white;
23 | font-size: 1.5rem;
24 | font-weight: 600;
25 | }
26 |
27 | .new-button {
28 | border: none;
29 | border-radius: 5px;
30 | background-color: #ffd500;
31 | color: #000;
32 | font-size: 1rem;
33 | font-weight: 600;
34 | cursor: pointer;
35 | transition: all 0.2s ease-in-out;
36 | padding: 0.7rem 1rem;
37 | outline: none;
38 | margin-right: 50px;
39 | display: flex;
40 | align-items: center;
41 | justify-content: space-between;
42 | }
--------------------------------------------------------------------------------
/day012/sticky-notes/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useStickyNotes } from '../context/StickyNotesContext'
3 | import { PlusSvgIcon } from './SvgIcons'
4 | import "./Header.css"
5 |
6 | const Header = () => {
7 | const { addNewNote } = useStickyNotes()
8 |
9 | return (
10 |
11 |
12 |
13 |
Sticky Notes
14 |
15 |
16 |
17 | Add new note
18 |
19 |
20 | )
21 | }
22 |
23 | export default Header
--------------------------------------------------------------------------------
/day012/sticky-notes/src/components/StickyNote.css:
--------------------------------------------------------------------------------
1 | .sticky-note {
2 | background-color: #ffd500;
3 | border-radius: 7px;
4 | padding: 20px;
5 | margin: 8px;
6 | height: 300px;
7 | position: relative;
8 |
9 | &:hover {
10 | background-color: #f5ba13;
11 | opacity: 0.8;
12 | }
13 |
14 | &::after {
15 | content: '';
16 | position: absolute;
17 | bottom: 0;
18 | right: 0;
19 | width: 0;
20 | height: 0;
21 | border-style: solid;
22 | border-width: 0 20px 20px 0;
23 | border-color: transparent rgb(167 166 158) #fff transparent;
24 | transform: rotate(270deg);
25 | }
26 | }
27 |
28 | .sticky-header {
29 | display: flex;
30 | justify-content: flex-end;
31 | align-items: center;
32 | margin-bottom: 10px;
33 | }
34 |
35 | .sticky-note-circular-button {
36 | width: 30px;
37 | height: 30px;
38 | border-radius: 50%;
39 | background-color: #fff;
40 | border: none;
41 | outline: none;
42 | cursor: pointer;
43 | margin-left: 5px;
44 | margin-right: 5px;
45 | display: flex;
46 | justify-content: center;
47 | align-items: center;
48 | box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.1);
49 |
50 | &:hover {
51 | scale: 1.1;
52 | }
53 | }
54 |
55 | .sticky-note-title {
56 | font-size: 1.2em;
57 | font-weight: bold;
58 | margin-bottom: 10px;
59 | color: #fff;
60 | }
61 |
62 | .sticky-note-content {
63 | font-size: 1em;
64 | font-weight: normal;
65 | margin-bottom: 10px;
66 | max-height: 165px;
67 | overflow-y: auto;
68 | color: #fff;
69 | }
70 |
71 | .sticky-note-title-input {
72 | border: none;
73 | outline: none;
74 | font-size: 1.2em;
75 | font-weight: bold;
76 | margin-bottom: 10px;
77 | padding: 5px;
78 | background-color: #fff;
79 | border-radius: 3px;
80 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
81 | }
82 |
83 | .sticky-note-content-input {
84 | border: none;
85 | outline: none;
86 | font-size: 1em;
87 | font-weight: normal;
88 | margin-bottom: 10px;
89 | padding: 5px;
90 | background-color: #fff;
91 | border-radius: 3px;
92 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
93 | }
94 |
95 | textarea {
96 | resize: none;
97 | border: none;
98 | outline: none;
99 | font-size: 1.2em;
100 | font-weight: bold;
101 | margin-bottom: 10px;
102 | padding: 5px;
103 | background-color: #fff;
104 | border-radius: 3px;
105 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
106 | }
--------------------------------------------------------------------------------
/day012/sticky-notes/src/components/StickyNotesGrid.css:
--------------------------------------------------------------------------------
1 | .grid-container {
2 | display: grid;
3 | grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
4 | width: 100%;
5 | height: 100vh;
6 | gap: 30px;
7 | margin-top: 80px;
8 | }
9 |
--------------------------------------------------------------------------------
/day012/sticky-notes/src/components/StickyNotesGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import "./StickyNotesGrid.css"
3 | import StickyNote from './StickyNote'
4 | import { useStickyNotes } from '../context/StickyNotesContext';
5 |
6 | const StickyNotesGrid = () => {
7 | const { notes, updateNote, deleteNote } = useStickyNotes();
8 | if (notes.length === 0) return null
9 |
10 | return (
11 |
12 | {notes.map(note => (
13 | updateNote(note)}
17 | onDelete={id => deleteNote(id)}
18 | />
19 | ))}
20 |
21 | )
22 | }
23 |
24 | export default StickyNotesGrid
--------------------------------------------------------------------------------
/day012/sticky-notes/src/components/SvgIcons.js:
--------------------------------------------------------------------------------
1 | export const CrossSvgIcon = () => (
2 |
3 |
4 |
5 |
6 | )
7 |
8 | export const ColorSvgIcon = () => (
9 |
10 | )
11 |
12 | export const PlusSvgIcon = () => (
13 |
14 |
15 |
16 | )
--------------------------------------------------------------------------------
/day012/sticky-notes/src/context/StickyNotesContext.js:
--------------------------------------------------------------------------------
1 | import { createContext, useContext, useEffect, useState } from "react";
2 |
3 | const StickyNotesContext = createContext();
4 |
5 | // StickyNotesContext.Provider is a component that wraps around the entire app
6 | const StickyNotesProvider = ({ children }) => {
7 | const [notes, setNotes] = useState(JSON.parse(localStorage.getItem('notes')) || []);
8 |
9 | // Save notes to localStorage every time the notes array changes
10 | useEffect(() => {
11 | localStorage.setItem('notes', JSON.stringify(notes));
12 | }, [notes]);
13 |
14 | function addNewNote() {
15 | setNotes(prevNotes => {
16 | return [...prevNotes, {
17 | id: Date.now(),
18 | title: 'Click to edit title',
19 | content: 'Click to edit content',
20 | color: '#ffd500'
21 | }];
22 | });
23 | }
24 |
25 | function updateNote(note) {
26 | setNotes(prevNotes => {
27 | const updatedNotes = prevNotes.map(prevNote => {
28 | if (prevNote.id === note.id) {
29 | return { ...prevNote, ...note };
30 | }
31 | return prevNote;
32 | });
33 | return updatedNotes;
34 | });
35 | }
36 |
37 | function deleteNote(id) {
38 | setNotes(prevNotes => {
39 | return prevNotes.filter(note => note.id !== id);
40 | });
41 | }
42 |
43 | return (
44 |
50 | {children}
51 |
52 | );
53 | }
54 |
55 | export default StickyNotesProvider;
56 |
57 | // Custom hook to use the StickyNotesContext
58 | export const useStickyNotes = () => {
59 | return useContext(StickyNotesContext);
60 | }
--------------------------------------------------------------------------------
/day012/sticky-notes/src/hooks/useDebounce.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 |
3 | function useDebounce(value, delay) {
4 | const [debouncedValue, setDebouncedValue] = useState(value);
5 |
6 | useEffect(() => {
7 | // Set debouncedValue to value (passed in) after the specified delay
8 | const handler = setTimeout(() => {
9 | setDebouncedValue(value);
10 | }, delay);
11 |
12 | // Return a cleanup function that will be called every time ...
13 | return () => {
14 | clearTimeout(handler);
15 | }
16 | }, [value, delay]);
17 |
18 | return debouncedValue;
19 | }
20 |
21 | export default useDebounce
--------------------------------------------------------------------------------
/day012/sticky-notes/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: 'Lato',
4 | sans-serif;
5 | -webkit-font-smoothing: antialiased;
6 | -moz-osx-font-smoothing: grayscale;
7 | }
8 |
9 | code {
10 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
11 | monospace;
12 | }
13 |
14 | .app-container {
15 | display: flex;
16 | flex-direction: column;
17 | align-items: center;
18 | justify-content: center;
19 | height: 100vh;
20 | padding: 50px;
21 | }
--------------------------------------------------------------------------------
/day012/sticky-notes/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './index.css';
4 | import App from './App';
5 |
6 | const root = ReactDOM.createRoot(document.getElementById('root'));
7 | root.render(
8 |
9 |
10 |
11 | );
12 |
13 |
--------------------------------------------------------------------------------
/day013/music-player/.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 |
--------------------------------------------------------------------------------
/day013/music-player/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "music-player",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.17.0",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/day013/music-player/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/react-project-ideas/fb3bf45a86a2a2556c85c6252da0873886cf8135/day013/music-player/public/favicon.ico
--------------------------------------------------------------------------------
/day013/music-player/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 |
Music Player
28 |
29 |
30 |
You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/day013/music-player/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/react-project-ideas/fb3bf45a86a2a2556c85c6252da0873886cf8135/day013/music-player/public/logo192.png
--------------------------------------------------------------------------------
/day013/music-player/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/react-project-ideas/fb3bf45a86a2a2556c85c6252da0873886cf8135/day013/music-player/public/logo512.png
--------------------------------------------------------------------------------
/day013/music-player/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 |
--------------------------------------------------------------------------------
/day013/music-player/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/day013/music-player/public/zunzun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/react-project-ideas/fb3bf45a86a2a2556c85c6252da0873886cf8135/day013/music-player/public/zunzun.png
--------------------------------------------------------------------------------
/day013/music-player/src/App.js:
--------------------------------------------------------------------------------
1 | import MusicPlayer from "./components/MusicPlayer";
2 |
3 | function App() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
11 | export default App;
12 |
--------------------------------------------------------------------------------
/day013/music-player/src/components/Controls.css:
--------------------------------------------------------------------------------
1 | .controls-container {
2 | display: flex;
3 | justify-content: space-between;
4 | align-items: center;
5 | flex-direction: column;
6 | margin-top: 10px;
7 | }
8 |
9 | .controls-icon {
10 | font-size: 1.5rem;
11 | cursor: pointer;
12 | }
13 |
14 | .volume{
15 | display: flex;
16 | justify-content: space-between;
17 | align-items: center;
18 | width: 100%;
19 | margin-top: 20px;
20 | }
21 |
22 | .controls {
23 | display: flex;
24 | justify-content: space-between;
25 | align-items: center;
26 | width: 100%;
27 | margin-top: 10px;
28 | }
29 |
30 | button {
31 | border: none;
32 | cursor: pointer;
33 | outline: none;
34 | display: flex;
35 | justify-content: center;
36 | align-items: center;
37 | background: none;
38 | color: rgb(31, 180, 130);
39 | }
40 |
41 | .play-pause {
42 | background-color: rgb(31, 180, 130);
43 | width: 50px;
44 | height: 50px;
45 | border-radius: 50%;
46 | color: white;
47 | }
48 |
49 | .volume-button {
50 | color: rgb(170, 173, 172);
51 |
52 | > svg {
53 | width: 22px;
54 | height: 22px;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/day013/music-player/src/components/DisplayTrack.css:
--------------------------------------------------------------------------------
1 | .display-track-container {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | justify-content: center;
6 | }
7 |
8 | .track-image-preview-rounded {
9 | width: 200px;
10 | height: 200px;
11 | border-radius: 15px;
12 | margin-bottom: 20px;
13 |
14 | border: 1px solid #ccc;
15 | background-color: #fff;
16 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
17 | }
18 |
19 | .track-info {
20 | display: flex;
21 | flex-direction: column;
22 | align-items: center;
23 | justify-content: center;
24 | }
25 |
26 | .track-title {
27 | font-size: 1.2rem;
28 | font-weight: 600;
29 | margin: 0;
30 | }
31 |
32 | .track-artist {
33 | font-size: 1rem;
34 | font-weight: lighter;
35 | margin-top: 10px;
36 | }
--------------------------------------------------------------------------------
/day013/music-player/src/components/DisplayTrack.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import './DisplayTrack.css'
3 | import { useTrack } from '../context/TrackContext'
4 |
5 | const DisplayTrack = () => {
6 | const { track, audioRef, setDuration, progressBarRef, handleNext } = useTrack()
7 |
8 | function onLoadedMetadata() {
9 | const duration = audioRef.current.duration
10 | setDuration(duration)
11 | progressBarRef.current.max = duration
12 | }
13 |
14 | return (
15 |
16 |
17 |
18 |
{track.displayName}
19 |
{track.artist}
20 |
21 |
22 |
23 | )
24 | }
25 |
26 | export default DisplayTrack
--------------------------------------------------------------------------------
/day013/music-player/src/components/Header.css:
--------------------------------------------------------------------------------
1 | .player-header {
2 | height: 50px;
3 | width: 100%;
4 | display: flex;
5 | justify-content: space-between;
6 | align-items: center;
7 | margin-bottom: 20px;
8 | }
9 |
10 | .player-title {
11 | display: flex;
12 | align-items: center;
13 |
14 | > img {
15 | width: 50px;
16 | height: 50px;
17 | margin-right: 10px;
18 | }
19 | }
20 |
21 | .playlist-button {
22 | border: none;
23 | background-color: transparent;
24 | cursor: pointer;
25 |
26 | > svg {
27 | width: 28px;
28 | height: 28px;
29 | }
30 | }
--------------------------------------------------------------------------------
/day013/music-player/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { TbPlaylist } from "react-icons/tb";
3 |
4 | import './Header.css'
5 |
6 | const Header = ({ onClickPlayList }) => {
7 | return (
8 |
9 |
10 |
11 | Zunzun Player
12 |
13 |
14 |
15 |
16 |
17 |
18 | )
19 | }
20 |
21 | export default Header
--------------------------------------------------------------------------------
/day013/music-player/src/components/MusicPlayer.css:
--------------------------------------------------------------------------------
1 | .music-player {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | justify-content: center;
6 | border: 1px solid #ccc;
7 | border-radius: 10px;
8 | padding: 30px;
9 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
10 | }
11 |
12 | .music-player-body {
13 | display: flex;
14 | width: 100%;
15 | gap: 20px;
16 | }
17 |
18 | .player {
19 | width: 350px;
20 | }
21 |
22 | .animate__animated.animate__slideInLeft {
23 | --animate-duration: 0.4s;
24 | }
--------------------------------------------------------------------------------
/day013/music-player/src/components/MusicPlayer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import DisplayTrack from './DisplayTrack'
3 | import Controls from './Controls'
4 | import ProgressBar from './ProgressBar'
5 | // import { tracks } from '../data'
6 | import Header from './Header'
7 | import PlayList from './PlayList'
8 | import 'animate.css';
9 | import './MusicPlayer.css'
10 | import { useTrack } from '../context/TrackContext'
11 |
12 | const MusicPlayer = () => {
13 | const { trackIndex, handlePlayListItemClick, showPlayList } = useTrack()
14 |
15 | return (
16 |
29 | )
30 | }
31 |
32 | export default MusicPlayer
--------------------------------------------------------------------------------
/day013/music-player/src/components/PlayList.css:
--------------------------------------------------------------------------------
1 | .playlist {
2 | display: flex;
3 | flex-direction: column;
4 | width: 350px;
5 | height: 430px;
6 | overflow: auto;
7 | padding: 0;
8 | margin: 0;
9 | list-style: none;
10 | padding-right: 5px;
11 |
12 | &::-webkit-scrollbar {
13 | width: 5px;
14 | }
15 |
16 | &::-webkit-scrollbar-track {
17 | background: #f1f1f1;
18 | }
19 |
20 | &::-webkit-scrollbar-thumb {
21 | background: rgb(31, 180, 130);
22 | }
23 | }
24 |
25 | .playlist-item {
26 | display: flex;
27 | align-items: center;
28 | justify-content: space-between;
29 | cursor: pointer;
30 | margin-bottom: 5px;
31 | }
32 |
33 | .playlist-item-info-wrapper {
34 | display: flex;
35 | align-items: center;
36 |
37 | > img {
38 | width: 50px;
39 | height: 50px;
40 | border-radius: 7px;
41 | margin-right: 10px;
42 | }
43 | }
44 |
45 | .playlist-item-info {
46 | display: flex;
47 | flex-direction: column;
48 | align-items: baseline;
49 |
50 | > h3 {
51 | margin: 0;
52 | font-size: 1rem;
53 | }
54 |
55 | > p {
56 | margin: 0;
57 | font-size: 0.8rem;
58 | color: #666;
59 | }
60 | }
61 |
62 | .playlist-item-duration {
63 | display: flex;
64 | flex-direction: column;
65 | align-items: flex-end;
66 |
67 | > p {
68 | margin: 0;
69 | font-size: 0.8rem;
70 | color: #666;
71 | }
72 |
73 | > span {
74 | font-size: 0.7rem;
75 | color: #666;
76 | }
77 | }
78 |
79 | .equalizer {
80 | > svg {
81 | width: 20px;
82 | height: 20px;
83 | margin-right: 3px;
84 | }
85 | }
--------------------------------------------------------------------------------
/day013/music-player/src/components/PlayList.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { tracks } from '../data'
3 | import './PlayList.css'
4 | import { useTrack } from '../context/TrackContext'
5 | import useTracksDuration from '../hooks/useTrack'
6 | import { BiEqualizer } from "react-icons/bi";
7 |
8 | const PlayList = ({ trackIndex, onClick }) => {
9 | const { isPlayListOpen } = useTrack()
10 | const { durations } = useTracksDuration(tracks);
11 |
12 | if (!isPlayListOpen) return null
13 |
14 | return (
15 |
16 |
17 |
18 | {tracks.map((track, index) => (
19 |
onClick(index)}>
20 |
21 |
22 |
23 |
{track.displayName}
24 |
{track.artist}
25 |
26 |
27 |
28 |
{durations[index]}
29 | {trackIndex === index &&
}
30 |
31 |
32 | ))}
33 |
34 |
35 |
36 | )
37 | }
38 |
39 | export default PlayList
--------------------------------------------------------------------------------
/day013/music-player/src/components/Player.css:
--------------------------------------------------------------------------------
1 | .player {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | justify-content: center;
6 | border: 1px solid #ccc;
7 | border-radius: 10px;
8 | width: 350px;
9 | padding: 30px;
10 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
11 | }
--------------------------------------------------------------------------------
/day013/music-player/src/components/Player.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import Header from './Header'
3 | import MusicPlayer from './MusicPlayer'
4 | import './Player.css'
5 | import PlayList from './PlayList'
6 |
7 | const Player = () => {
8 | const [view, setView] = useState('player')
9 |
10 | return (
11 |
12 |
setView('playlist')} />
13 | {view === 'player' ? : view === 'playlist' ? : null}
14 |
15 | )
16 | }
17 |
18 | export default Player
--------------------------------------------------------------------------------
/day013/music-player/src/components/ProgressBar.css:
--------------------------------------------------------------------------------
1 | .progress-bar-container {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | margin-top: 10px;
6 | width: 100%;
7 | gap: 10px;
8 | }
9 |
10 | input[type="range"] {
11 | --range-progress: 0;
12 | -webkit-appearance: none;
13 | position: relative;
14 | background:#ccc;
15 | width: 100%;
16 | height: 5px;
17 | border-radius: 7px;
18 | cursor: pointer;
19 | }
20 |
21 | /* Input range - firefox */
22 | input[type="range"]::-moz-range-track {
23 | position: relative;
24 | background:#ccc;
25 | width: 100%;
26 | height: 5px;
27 | border-radius: 7px;
28 | cursor: pointer;
29 | }
30 |
31 | /* played progress length - Chrome & safari*/
32 | input[type="range"]::before {
33 | content: '';
34 | height: 5px;
35 | background: rgb(31, 180, 130);
36 | width: var(--range-progress);
37 | border-bottom-left-radius: 7px;
38 | border-top-left-radius: 7px;
39 | position: absolute;
40 | top: 0;
41 | left: 0;
42 | }
43 |
44 | /* played progress length - firefox */
45 | input[type="range"]::-moz-range-progress {
46 | background: rgb(31, 180, 130);
47 | border-bottom-left-radius: 7px;
48 | border-top-left-radius: 7px;
49 | height: 5px;
50 | }
51 |
52 | /* slider thumb - chrome and safari */
53 | input[type="range"]::-webkit-slider-thumb {
54 | -webkit-appearance: none;
55 | height: 15px;
56 | width: 15px;
57 | border-radius: 50%;
58 | border: none;
59 | background-color: rgb(31, 180, 130);
60 | cursor: pointer;
61 | position: relative;
62 | }
63 |
64 | /* dragging thumb - chrome and safari */
65 | input[type="range"]:active::-webkit-slider-thumb {
66 | transform: scale(1.2);
67 | }
68 |
69 | /* slider thumb - firefox */
70 | input[type="range"]::-moz-range-thumb {
71 | height: 8px;
72 | width: 8px;
73 | border-radius: 50%;
74 | background: rgb(31, 180, 130);
75 | cursor: pointer;
76 | border: transparent;
77 | position: relative;
78 | }
79 | /* dragging thumb - firefox */
80 | input[type="range"]:active::-moz-range-thumb {
81 | transform: scale(1.2);
82 | }
83 |
84 | .time {
85 | font-weight: lighter;
86 | width: 70px;
87 | }
--------------------------------------------------------------------------------
/day013/music-player/src/components/ProgressBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { formatTime } from '../helpers'
3 | import './ProgressBar.css'
4 | import { useTrack } from '../context/TrackContext'
5 |
6 | const ProgressBar = () => {
7 | const { progressBarRef, audioRef, timeProgress, duration } = useTrack()
8 |
9 | function handleProgressChange() {
10 | audioRef.current.currentTime = progressBarRef.current.value
11 | }
12 |
13 | return (
14 |
15 | {formatTime(timeProgress)}
16 |
17 | {formatTime(duration)}
18 |
19 | )
20 | }
21 |
22 | export default ProgressBar
--------------------------------------------------------------------------------
/day013/music-player/src/context/TrackContext.js:
--------------------------------------------------------------------------------
1 | import { createContext, useContext, useRef, useState } from "react";
2 | import useTracksDuration from "../hooks/useTrack";
3 | import { tracks } from "../data";
4 |
5 | const TrackContext = createContext();
6 |
7 | const TrackProvider = ({ children }) => {
8 | // References
9 | const audioRef = useRef(null)
10 | const progressBarRef = useRef(null)
11 |
12 |
13 | // States
14 | const [trackIndex, setTrackIndex] = useState(0)
15 | const [trackIndexFromList, setTrackIndexFromList] = useState(trackIndex)
16 | const [track, setTrack] = useState(tracks[trackIndex])
17 | const [timeProgress, setTimeProgress] = useState(0)
18 | const [duration, setDuration] = useState(0)
19 | const [isPlayListOpen, setIsPlayListOpen] = useState(false)
20 |
21 | // Custom hook
22 | const { tracksDuration, isLoadingTrackDuration } = useTracksDuration(tracks);
23 |
24 |
25 | function showPlayList() {
26 | setIsPlayListOpen(!isPlayListOpen)
27 | }
28 |
29 | function handleNext() {
30 | if (trackIndex >= tracks.length - 1) {
31 | setTrackIndex(0)
32 | setTrackIndexFromList(0)
33 | setTrack(tracks[0])
34 | } else {
35 | setTrackIndex(trackIndex + 1)
36 | setTrackIndexFromList(trackIndex + 1)
37 | setTrack(tracks[trackIndex + 1])
38 | }
39 | }
40 |
41 | function handlePlayListItemClick(index) {
42 | setTrackIndexFromList(index)
43 | }
44 |
45 | return (
46 |
67 | {children}
68 |
69 | )
70 | }
71 |
72 | export const useTrack = () => {
73 | return useContext(TrackContext);
74 | }
75 |
76 | export default TrackProvider
77 |
78 |
--------------------------------------------------------------------------------
/day013/music-player/src/helpers.js:
--------------------------------------------------------------------------------
1 | export const formatTime = (time) => {
2 | if (time && !isNaN(time)) {
3 | const minutes = Math.floor(time / 60);
4 | const formatMinutes =
5 | minutes < 10 ? `0${minutes}` : `${minutes}`;
6 | const seconds = Math.floor(time % 60);
7 | const formatSeconds =
8 | seconds < 10 ? `0${seconds}` : `${seconds}`;
9 | return `${formatMinutes}:${formatSeconds}`;
10 | }
11 | return '00:00';
12 | };
--------------------------------------------------------------------------------
/day013/music-player/src/hooks/useTrack.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react"
2 | import { formatTime } from "../helpers"
3 |
4 |
5 | function useTracksDuration(tracks) {
6 | const [isLoadingTrackDuration, setIsLoadingTrackDuration] = useState(true);
7 | const [durations, setDurations] = useState([]);
8 |
9 | useEffect(() => {
10 | const fetchDurations = async () => {
11 | try {
12 | setIsLoadingTrackDuration(true)
13 | const durationsArray = [];
14 |
15 | tracks.forEach(track => {
16 | const audio = new Audio(track.audioSrc);
17 | audio.addEventListener('loadedmetadata', () => {
18 | const duration = formatTime(audio.duration);
19 | durationsArray.push(duration);
20 | });
21 | });
22 |
23 | setDurations(durationsArray);
24 | } catch (err) {
25 | console.log(err);
26 | } finally {
27 | setIsLoadingTrackDuration(false);
28 | }
29 | };
30 |
31 | fetchDurations();
32 | }, [tracks])
33 |
34 | return { durations, isLoadingTrackDuration };
35 | }
36 |
37 | export default useTracksDuration
--------------------------------------------------------------------------------
/day013/music-player/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 |
15 | .App {
16 | text-align: center;
17 | display: grid;
18 | grid-template-columns: 1fr;
19 | place-items: center;
20 | height: 100vh;
21 | }
--------------------------------------------------------------------------------
/day013/music-player/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './index.css';
4 | import App from './App';
5 | import TrackProvider from './context/TrackContext';
6 |
7 | const root = ReactDOM.createRoot(document.getElementById('root'));
8 | root.render(
9 |
10 |
11 |
12 |
13 |
14 | );
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:react/recommended',
7 | 'plugin:react/jsx-runtime',
8 | 'plugin:react-hooks/recommended',
9 | ],
10 | ignorePatterns: ['dist', '.eslintrc.cjs'],
11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12 | settings: { react: { version: '18.2' } },
13 | plugins: ['react-refresh'],
14 | rules: {
15 | 'react-refresh/only-export-components': [
16 | 'warn',
17 | { allowConstantExport: true },
18 | ],
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/README.md:
--------------------------------------------------------------------------------
1 | # React + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Random Dog Pic
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dogpicapi",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.15",
18 | "@types/react-dom": "^18.2.7",
19 | "@vitejs/plugin-react": "^4.0.3",
20 | "eslint": "^8.45.0",
21 | "eslint-plugin-react": "^7.32.2",
22 | "eslint-plugin-react-hooks": "^4.6.0",
23 | "eslint-plugin-react-refresh": "^0.4.3",
24 | "vite": "^4.4.5"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/public/dog.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
16 |
17 |
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/src/App.jsx:
--------------------------------------------------------------------------------
1 | import Dog from "./Components/Dog"
2 |
3 | function App() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
11 | export default App;
12 |
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/src/Assets/dog.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/react-project-ideas/fb3bf45a86a2a2556c85c6252da0873886cf8135/day014/Random-Dog-Pic-Generate/src/Assets/dog.jpg
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/src/Components/BreedSelector.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function BreedSelector({ breeds, selectedBreed, setSelectedBreed, getDogs }) {
4 | return (
5 |
6 | setSelectedBreed(e.target.value)}
9 | >
10 | Select a breed
11 | {breeds.map((breed) => (
12 |
13 | {breed}
14 |
15 | ))}
16 |
17 |
18 |
19 | Generate Dog
20 |
21 |
22 | );
23 | }
24 |
25 | export default BreedSelector;
26 |
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/src/Components/DogImage.jsx:
--------------------------------------------------------------------------------
1 | import Dogpic from "../Assets/dog.jpg";
2 | import React from 'react';
3 |
4 | function DogImage({ imgLinks, error }) {
5 | return (
6 |
7 |
8 | {error ? (
9 |
10 |
11 |
{error}
12 |
13 | ) : (
14 | imgLinks.length > 0 && (
15 |
16 |
17 |
Breed : {imgLinks[0].breed}
18 |
19 | )
20 | )}
21 |
22 | );
23 | }
24 |
25 | export default DogImage;
26 |
27 |
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/src/Components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Footer = () => {
4 | return (
5 |
8 | )
9 | }
10 |
11 | export default Footer
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/src/Components/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Header = () => {
4 | return (
5 |
6 | Random Dog Images
7 |
8 | )
9 | }
10 |
11 | export default Header
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')).render(
7 |
8 |
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/day014/Random-Dog-Pic-Generate/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-project-ideas",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "dependencies": {
8 | "animate.css": "^4.1.1",
9 | "react-icons": "^4.12.0"
10 | }
11 | },
12 | "node_modules/animate.css": {
13 | "version": "4.1.1",
14 | "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz",
15 | "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="
16 | },
17 | "node_modules/js-tokens": {
18 | "version": "4.0.0",
19 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
20 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
21 | "peer": true
22 | },
23 | "node_modules/loose-envify": {
24 | "version": "1.4.0",
25 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
26 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
27 | "peer": true,
28 | "dependencies": {
29 | "js-tokens": "^3.0.0 || ^4.0.0"
30 | },
31 | "bin": {
32 | "loose-envify": "cli.js"
33 | }
34 | },
35 | "node_modules/react": {
36 | "version": "18.2.0",
37 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
38 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
39 | "peer": true,
40 | "dependencies": {
41 | "loose-envify": "^1.1.0"
42 | },
43 | "engines": {
44 | "node": ">=0.10.0"
45 | }
46 | },
47 | "node_modules/react-icons": {
48 | "version": "4.12.0",
49 | "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz",
50 | "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==",
51 | "peerDependencies": {
52 | "react": "*"
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "animate.css": "^4.1.1",
4 | "react-icons": "^4.12.0"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------