├── .gitignore
├── 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
├── HeaderBar.jsx
├── Home.jsx
├── NavList.jsx
├── SwipeDrawer.jsx
├── archives
│ ├── Archive.jsx
│ └── Archives.jsx
├── delete
│ ├── DeleteNote.jsx
│ └── DeleteNotes.jsx
└── notes
│ ├── EmptyNotes.jsx
│ ├── Form.jsx
│ ├── Note.jsx
│ └── Notes.jsx
├── context
└── DataProvider.jsx
├── index.css
├── index.js
├── logo.svg
├── reportWebVitals.js
├── setupTests.js
└── utils
└── common-utils.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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "google-keep-clone-use-react",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@emotion/react": "^11.7.1",
7 | "@emotion/styled": "^11.6.0",
8 | "@material-ui/core": "^4.12.4",
9 | "@mui/icons-material": "^5.3.1",
10 | "@mui/material": "^5.3.1",
11 | "@testing-library/jest-dom": "^5.16.1",
12 | "@testing-library/react": "^12.1.2",
13 | "@testing-library/user-event": "^13.5.0",
14 | "react": "^17.0.2",
15 | "react-beautiful-dnd": "^13.1.0",
16 | "react-dom": "^17.0.2",
17 | "react-router-dom": "^6.2.1",
18 | "react-scripts": "5.0.0",
19 | "uuid": "^8.3.2",
20 | "web-vitals": "^2.1.4"
21 | },
22 | "scripts": {
23 | "start": "react-scripts start",
24 | "build": "react-scripts build",
25 | "test": "react-scripts test",
26 | "eject": "react-scripts eject"
27 | },
28 | "eslintConfig": {
29 | "extends": [
30 | "react-app",
31 | "react-app/jest"
32 | ]
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.2%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 1 chrome version",
42 | "last 1 firefox version",
43 | "last 1 safari version"
44 | ]
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mr-dev-dragon/google-keep-clone-use-react/08845fb8fe574de9a414f6f68d67f345fe0bd637/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/mr-dev-dragon/google-keep-clone-use-react/08845fb8fe574de9a414f6f68d67f345fe0bd637/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mr-dev-dragon/google-keep-clone-use-react/08845fb8fe574de9a414f6f68d67f345fe0bd637/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 | .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 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 |
2 | //components
3 | import Home from './components/Home';
4 | import DataProvider from './context/DataProvider';
5 |
6 | function App() {
7 | return (
8 |
9 |
10 |
11 | );
12 | }
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/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/components/HeaderBar.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { AppBar, Toolbar, Typography, IconButton } from '@mui/material';
3 | import { Menu } from '@mui/icons-material';
4 | import { styled } from '@mui/material/styles';
5 |
6 | const Header = styled(AppBar)`
7 | z-index: 1201;
8 | background: #fff;
9 | height: 70px;
10 | box-shadow: inset 0 -1px 0 0 #dadce0;
11 | `
12 |
13 | const Heading = styled(Typography)`
14 | color: #5F6368;
15 | font-size: 24px;
16 | margin-left: 25px;
17 | `
18 |
19 |
20 | const HeaderBar = ({ open, handleDrawer }) => {
21 | const logo = 'https://seeklogo.com/images/G/google-keep-logo-0BC92EBBBD-seeklogo.com.png';
22 |
23 | return (
24 |
25 |
26 | handleDrawer()}
28 | sx={{ marginRight: '20px'}}
29 | edge="start"
30 | >
31 |
32 |
33 |
34 | Keep
35 |
36 |
37 | )
38 | }
39 |
40 | export default HeaderBar;
--------------------------------------------------------------------------------
/src/components/Home.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
3 | import { Box } from '@mui/material';
4 |
5 | //components
6 | import SwipeDrawer from './SwipeDrawer';
7 | import Notes from './notes/Notes';
8 | import Archives from './archives/Archives';
9 | import DeleteNotes from './delete/DeleteNotes';
10 |
11 | const Home = () => {
12 | return (
13 |
14 |
15 |
16 |
17 | } />
18 | } />
19 | } />
20 |
21 |
22 |
23 | )
24 | }
25 |
26 | export default Home;
--------------------------------------------------------------------------------
/src/components/NavList.jsx:
--------------------------------------------------------------------------------
1 |
2 |
3 | import { List, ListItem, ListItemIcon, ListItemText } from '@mui/material';
4 | import { LightbulbOutlined as Lightbulb, ArchiveOutlined as Archive, DeleteOutlineOutlined as Delete } from '@mui/icons-material';
5 | import { Link } from 'react-router-dom';
6 |
7 | const NavList = () => {
8 |
9 | const navList = [
10 | { id: 1, name: 'Notes', icon: , route: '/' },
11 | { id: 2, name: 'Archives', icon: , route: '/archive' },
12 | { id: 3, name: 'Trash', icon: , route: '/delete' },
13 | ]
14 |
15 | return (
16 |
17 | {
18 | navList.map(list => (
19 |
20 |
21 |
22 | {list.icon}
23 |
24 |
25 |
26 |
27 | ))
28 | }
29 |
30 | )
31 | }
32 |
33 | export default NavList;
--------------------------------------------------------------------------------
/src/components/SwipeDrawer.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { styled, useTheme } from '@mui/material/styles';
3 | import Box from '@mui/material/Box';
4 | import MuiDrawer from '@mui/material/Drawer';
5 |
6 | //components
7 | import HeaderBar from './HeaderBar';
8 | import NavList from './NavList';
9 |
10 | const drawerWidth = 240;
11 |
12 | const openedMixin = (theme) => ({
13 | width: drawerWidth,
14 | transition: theme.transitions.create('width', {
15 | easing: theme.transitions.easing.sharp,
16 | duration: theme.transitions.duration.enteringScreen,
17 | }),
18 | overflowX: 'hidden',
19 | });
20 |
21 | const closedMixin = (theme) => ({
22 | transition: theme.transitions.create('width', {
23 | easing: theme.transitions.easing.sharp,
24 | duration: theme.transitions.duration.leavingScreen,
25 | }),
26 | overflowX: 'hidden',
27 | width: `calc(${theme.spacing(7)} + 1px)`,
28 | [theme.breakpoints.up('sm')]: {
29 | width: `calc(${theme.spacing(9)} + 1px)`,
30 | },
31 | });
32 |
33 | const DrawerHeader = styled('div')(({ theme }) => ({
34 | ...theme.mixins.toolbar,
35 | }));
36 |
37 |
38 | const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
39 | ({ theme, open }) => ({
40 | width: drawerWidth,
41 | flexShrink: 0,
42 | whiteSpace: 'nowrap',
43 | boxSizing: 'border-box',
44 | ...(open && {
45 | ...openedMixin(theme),
46 | '& .MuiDrawer-paper': openedMixin(theme),
47 | }),
48 | ...(!open && {
49 | ...closedMixin(theme),
50 | '& .MuiDrawer-paper': closedMixin(theme),
51 | }),
52 | }),
53 | );
54 |
55 | function SwipeDrawer() {
56 | const theme = useTheme();
57 | const [open, setOpen] = React.useState(true);
58 |
59 | const handleDrawer = () => {
60 | setOpen(prevState => !prevState);
61 | };
62 |
63 | return (
64 |
65 |
69 |
70 |
71 |
72 |
73 |
74 | );
75 | }
76 |
77 | export default SwipeDrawer;
--------------------------------------------------------------------------------
/src/components/archives/Archive.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 |
3 | import { Card, CardContent, CardActions, Typography } from '@mui/material';
4 | import { styled } from '@mui/material/styles';
5 | import { UnarchiveOutlined as Unarchive, DeleteOutlineOutlined as Delete } from '@mui/icons-material';
6 |
7 | import { DataContext } from '../../context/DataProvider';
8 |
9 | const StyledCard = styled(Card)`
10 | border: 1px solid #e0e0e0;
11 | border-radius: 8px;
12 | width: 240px;
13 | margin: 8px;
14 | box-shadow: none;
15 | `
16 |
17 | const Archive = ({ archive }) => {
18 |
19 | const { archiveNotes, setNotes, setAcrchiveNotes, setDeleteNotes } = useContext(DataContext);
20 |
21 | const unArchiveNote = (archive) => {
22 | const updatedNotes = archiveNotes.filter(data => data.id !== archive.id);
23 | setAcrchiveNotes(updatedNotes);
24 | setNotes(prevArr => [archive, ...prevArr]);
25 | }
26 |
27 | const deleteNote = (archive) => {
28 | const updatedNotes = archiveNotes.filter(data => data.id !== archive.id);
29 | setAcrchiveNotes(updatedNotes);
30 | setDeleteNotes(prevArr => [archive, ...prevArr]);
31 | }
32 |
33 | return (
34 |
35 |
36 | {archive.heading}
37 | {archive.text}
38 |
39 |
40 | unArchiveNote(archive)}
44 | />
45 | deleteNote(archive)}
48 | />
49 |
50 |
51 | )
52 | }
53 |
54 | export default Archive;
--------------------------------------------------------------------------------
/src/components/archives/Archives.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 |
3 | import { Box, Grid } from '@mui/material';
4 | import { styled } from '@mui/material/styles';
5 |
6 | import { DataContext } from '../../context/DataProvider';
7 |
8 | //components
9 | import Archive from './Archive';
10 |
11 | const DrawerHeader = styled('div')(({ theme }) => ({
12 | ...theme.mixins.toolbar,
13 | }));
14 |
15 | const Archives = () => {
16 |
17 | const { archiveNotes } = useContext(DataContext);
18 |
19 | return (
20 |
21 |
22 |
23 |
24 | {
25 | archiveNotes.map(archive => (
26 |
27 |
28 |
29 | ))
30 | }
31 |
32 |
33 |
34 | )
35 | }
36 |
37 | export default Archives;
--------------------------------------------------------------------------------
/src/components/delete/DeleteNote.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 |
3 | import { Card, CardContent, CardActions, Typography } from '@mui/material';
4 | import { styled } from '@mui/material/styles';
5 | import { RestoreFromTrashOutlined as Restore, DeleteForeverOutlined as Delete } from '@mui/icons-material';
6 | import { DataContext } from '../../context/DataProvider';
7 |
8 | const StyledCard = styled(Card)`
9 | border: 1px solid #e0e0e0;
10 | border-radius: 8px;
11 | width: 240px;
12 | margin: 8px;
13 | box-shadow: none;
14 | `
15 |
16 | const DeleteNote = ({ deleteNote }) => {
17 |
18 | const { deleteNotes, setNotes, setAcrchiveNotes, setDeleteNotes } = useContext(DataContext);
19 |
20 | const restoreNote = (deleteNote) => {
21 | const updatedNotes = deleteNotes.filter(data => data.id !== deleteNote.id);
22 | setDeleteNotes(updatedNotes);
23 | setNotes(prevArr => [deleteNote, ...prevArr]);
24 | }
25 |
26 | const removeNote = (deleteNote) => {
27 | const updatedNotes = deleteNotes.filter(data => data.id !== deleteNote.id);
28 | setDeleteNotes(updatedNotes);
29 | }
30 |
31 | return (
32 |
33 |
34 | {deleteNote.heading}
35 | {deleteNote.text}
36 |
37 |
38 | removeNote(deleteNote)}
42 | />
43 | restoreNote(deleteNote)}
46 | />
47 |
48 |
49 | )
50 | }
51 |
52 | export default DeleteNote;
--------------------------------------------------------------------------------
/src/components/delete/DeleteNotes.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 |
3 | import { Box, Grid } from '@mui/material';
4 | import { styled } from '@mui/material/styles';
5 |
6 | import { DataContext } from '../../context/DataProvider';
7 |
8 | //components
9 | import DeleteNote from './DeleteNote';
10 |
11 | const DrawerHeader = styled('div')(({ theme }) => ({
12 | ...theme.mixins.toolbar,
13 | }));
14 |
15 | const DeleteNotes = () => {
16 |
17 | const { deleteNotes } = useContext(DataContext);
18 |
19 | return (
20 |
21 |
22 |
23 |
24 | {
25 | deleteNotes.map(deleteNote => (
26 |
27 |
28 |
29 | ))
30 | }
31 |
32 |
33 |
34 | )
35 | }
36 |
37 | export default DeleteNotes;
--------------------------------------------------------------------------------
/src/components/notes/EmptyNotes.jsx:
--------------------------------------------------------------------------------
1 |
2 |
3 | import { LightbulbOutlined as Lightbulb } from '@mui/icons-material';
4 | import { Typography, Box, styled } from '@mui/material';
5 |
6 | const Light = styled(Lightbulb)`
7 | font-size: 120px;
8 | color: #F5F5F5;
9 | `
10 |
11 | const Text = styled(Typography)`
12 | color: #80868b;
13 | font-size: 22px
14 | `
15 |
16 | const Container = styled(Box)`
17 | display: flex;
18 | flex-direction: column;
19 | align-items: center;
20 | margin-top: 20vh
21 | `
22 |
23 | const EmptyNotes = () => {
24 | return (
25 |
26 |
27 | Notes you add appear here
28 |
29 | )
30 | }
31 |
32 | export default EmptyNotes;
--------------------------------------------------------------------------------
/src/components/notes/Form.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useRef, useContext } from 'react';
2 |
3 | import { Box, TextField, ClickAwayListener } from '@mui/material';
4 | import { styled } from '@mui/material/styles';
5 | import { v4 as uuid } from 'uuid';
6 |
7 | import { DataContext } from '../../context/DataProvider';
8 |
9 | const Container = styled(Box)`
10 | display: flex;
11 | flex-direction: column;
12 | margin: auto;
13 | box-shadow: 0 1px 2px 0 rgb(60 64 67 / 30%), 0 2px 6px 2px rgb(60 64 67 / 15%);
14 | border-color: #e0e0e0;
15 | width: 600px;
16 | border-radius: 8px;
17 | min-height: 30px;
18 | padding: 10px 15px;
19 | `
20 |
21 | const note = {
22 | id: '',
23 | heading: '',
24 | text: ''
25 | }
26 |
27 | const Form = () => {
28 |
29 | const [showTextField, setShowTextField] = useState(false);
30 | const [addNote, setAddNote] = useState({ ...note, id: uuid() });
31 |
32 | const { setNotes } = useContext(DataContext);
33 |
34 | const containerRef = useRef();
35 |
36 | const handleClickAway = () => {
37 | setShowTextField(false);
38 | containerRef.current.style.minheight = '30px'
39 | setAddNote({ ...note, id: uuid() });
40 |
41 | if (addNote.heading || addNote.text) {
42 | setNotes(prevArr => [addNote, ...prevArr])
43 | }
44 | }
45 |
46 | const onTextAreaClick = () => {
47 | setShowTextField(true);
48 | containerRef.current.style.minheight = '70px'
49 | }
50 |
51 | const onTextChange = (e) => {
52 | let changedNote = { ...addNote, [e.target.name]: e.target.value };
53 | setAddNote(changedNote);
54 | }
55 |
56 | return (
57 |
58 |
59 | { showTextField &&
60 | onTextChange(e)}
66 | name='heading'
67 | value={addNote.heading}
68 | />
69 | }
70 | onTextChange(e)}
78 | name='text'
79 | value={addNote.text}
80 | />
81 |
82 |
83 | )
84 | }
85 |
86 | export default Form;
--------------------------------------------------------------------------------
/src/components/notes/Note.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 |
3 | import { Card, CardContent, CardActions, Typography } from '@mui/material';
4 | import { styled } from '@mui/material/styles';
5 | import { ArchiveOutlined as Archive, DeleteOutlineOutlined as Delete } from '@mui/icons-material';
6 |
7 | import { DataContext } from '../../context/DataProvider';
8 |
9 | const StyledCard = styled(Card)`
10 | border: 1px solid #e0e0e0;
11 | border-radius: 8px;
12 | width: 240px;
13 | margin: 8px;
14 | box-shadow: none;
15 | `
16 |
17 | const Note = ({ note }) => {
18 |
19 | const { notes, setNotes, setAcrchiveNotes, setDeleteNotes } = useContext(DataContext);
20 |
21 | const archiveNote = (note) => {
22 | const updatedNotes = notes.filter(data => data.id !== note.id);
23 | setNotes(updatedNotes);
24 | setAcrchiveNotes(prevArr => [note, ...prevArr]);
25 | }
26 |
27 | const deleteNote = (note) => {
28 | const updatedNotes = notes.filter(data => data.id !== note.id);
29 | setNotes(updatedNotes);
30 | setDeleteNotes(prevArr => [note, ...prevArr]);
31 | }
32 |
33 | return (
34 |
35 |
36 | {note.heading}
37 | {note.text}
38 |
39 |
40 | archiveNote(note)}
44 | />
45 | deleteNote(note)}
48 | />
49 |
50 |
51 | )
52 | }
53 |
54 | export default Note;
--------------------------------------------------------------------------------
/src/components/notes/Notes.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 |
3 | import { Box, Grid } from '@mui/material';
4 | import { styled } from '@mui/material/styles';
5 | import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
6 |
7 | import { DataContext } from '../../context/DataProvider';
8 | import { reorder } from '../../utils/common-utils';
9 |
10 | //components
11 | import Form from './Form';
12 | import Note from './Note';
13 | import EmptyNotes from './EmptyNotes';
14 |
15 | const DrawerHeader = styled('div')(({ theme }) => ({
16 | ...theme.mixins.toolbar,
17 | }));
18 |
19 | const Notes = () => {
20 |
21 | const { notes, setNotes } = useContext(DataContext);
22 |
23 | const onDragEnd = (result) => {
24 | if (!result.destination)
25 | return;
26 |
27 | const items = reorder(notes, result.source.index, result.destination.index);
28 | setNotes(items);
29 | }
30 |
31 | return (
32 |
33 |
34 |
35 |
36 | { notes.length > 0 ?
37 |
38 |
39 | {(provided, snapshot) => (
40 |
44 | {
45 | notes.map((note, index) => (
46 |
47 | {(provided, snapshot) => (
48 |
53 |
54 |
55 | )}
56 |
57 | ))
58 | }
59 |
60 | )}
61 |
62 |
63 | : }
64 |
65 |
66 | )
67 | }
68 |
69 | export default Notes;
--------------------------------------------------------------------------------
/src/context/DataProvider.jsx:
--------------------------------------------------------------------------------
1 | import { createContext, useState } from 'react';
2 |
3 | export const DataContext = createContext(null);
4 |
5 | const DataProvider = ({ children }) => {
6 |
7 | const [notes, setNotes] = useState([]);
8 | const [archiveNotes, setAcrchiveNotes] = useState([]);
9 | const [deleteNotes, setDeleteNotes] = useState([]);
10 |
11 | return (
12 |
21 | {children}
22 |
23 | )
24 | }
25 |
26 | export default DataProvider;
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
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/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 |
--------------------------------------------------------------------------------
/src/utils/common-utils.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | export const reorder = (list, startIndex, endIndex) => {
4 | const result = Array.from(list);
5 | const [removed] = result.splice(startIndex, 1);
6 | result.splice(endIndex, 0, removed);
7 |
8 | return result;
9 | };
--------------------------------------------------------------------------------