├── .gitattributes
├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── src
├── components
│ ├── card
│ │ ├── card.scss
│ │ └── index.jsx
│ └── kanban
│ │ ├── kanban.scss
│ │ └── index.jsx
├── index.js
├── App.scss
├── App.jsx
└── mockData.js
├── .gitignore
├── README.md
└── package.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trananhtuat/react-kanban-ui/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trananhtuat/react-kanban-ui/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trananhtuat/react-kanban-ui/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/src/components/card/card.scss:
--------------------------------------------------------------------------------
1 | .card {
2 | padding: 30px;
3 | background-color: #212121;
4 | border-radius: 10px;
5 | }
--------------------------------------------------------------------------------
/src/components/card/index.jsx:
--------------------------------------------------------------------------------
1 | import './card.scss'
2 |
3 | const Card = props => {
4 | return (
5 |
6 | {props.children}
7 |
8 | )
9 | }
10 |
11 | export default Card
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/src/App.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700;900&display=swap');
2 |
3 | * {
4 | padding: 0;
5 | margin: 0;
6 | box-sizing: border-box;
7 | }
8 |
9 | body {
10 | background-color: #212121;
11 | color: #E4E4E4;
12 | font-family: 'Lato', sans-serif;
13 | line-height: 1.5;
14 | }
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import './App.scss'
2 | import Kanban from './components/kanban'
3 |
4 | function App() {
5 | return (
6 |
7 |
8 | Kanban UI
9 |
10 |
11 |
12 | )
13 | }
14 |
15 | export default App
16 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Video tutorial
2 |
3 | https://youtu.be/AL7IJVhuGeA
4 |
5 | # Reference
6 |
7 | - Create react app: https://create-react-app.dev/
8 | - SASS: https://sass-lang.com/
9 | - Boxicons: https://boxicons.com/
10 | - Google font: https://fonts.google.com/
11 | - React beautiful dnd: https://github.com/atlassian/react-beautiful-dnd/
12 |
13 | # Preview
14 |
15 | 
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/kanban/kanban.scss:
--------------------------------------------------------------------------------
1 | .kanban {
2 | display: flex;
3 | align-items: flex-start;
4 |
5 | &__section {
6 | width: 400px;
7 | background-color: #383838;
8 | padding: 10px;
9 | border-radius: 10px;
10 |
11 | & ~ & {
12 | margin-left: 10px;
13 | }
14 |
15 | &__title {
16 | font-size: 1.2rem;
17 | font-weight: 700;
18 | margin: 10px 0 20px;
19 | }
20 |
21 | &__content {
22 | & > * ~ * {
23 | margin-top: 10px;
24 | }
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-kanban-ui",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.14.1",
7 | "@testing-library/react": "^12.0.0",
8 | "@testing-library/user-event": "^13.2.1",
9 | "react": "^17.0.2",
10 | "react-beautiful-dnd": "^13.1.0",
11 | "react-dom": "^17.0.2",
12 | "react-scripts": "5.0.0",
13 | "sass": "^1.49.9",
14 | "web-vitals": "^2.1.0"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": [
24 | "react-app",
25 | "react-app/jest"
26 | ]
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/mockData.js:
--------------------------------------------------------------------------------
1 | import { v4 as uuidv4 } from 'uuid'
2 |
3 | const mockData = [
4 | {
5 | id: uuidv4(),
6 | title: ' 📃 To do',
7 | tasks: [
8 | {
9 | id: uuidv4(),
10 | title: 'Learn JavaScript'
11 | },
12 | {
13 | id: uuidv4(),
14 | title: 'Learn Git'
15 | },
16 | {
17 | id: uuidv4(),
18 | title: 'Learn Python'
19 | },
20 | ]
21 | },
22 | {
23 | id: uuidv4(),
24 | title: ' ✏️ In progress',
25 | tasks: [
26 | {
27 | id: uuidv4(),
28 | title: 'Learn CSS'
29 | },
30 | {
31 | id: uuidv4(),
32 | title: 'Learn Golang'
33 | }
34 | ]
35 | },
36 | {
37 | id: uuidv4(),
38 | title: ' ✔️ Completed',
39 | tasks: [
40 | {
41 | id: uuidv4(),
42 | title: 'Learn HTML'
43 | }
44 | ]
45 | }
46 | ]
47 |
48 | export default mockData
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/kanban/index.jsx:
--------------------------------------------------------------------------------
1 | import './kanban.scss'
2 | import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'
3 | import mockData from '../../mockData'
4 | import { useState } from 'react'
5 | import Card from '../card'
6 |
7 | const Kanban = () => {
8 | const [data, setData] = useState(mockData)
9 |
10 | const onDragEnd = result => {
11 | if (!result.destination) return
12 | const { source, destination } = result
13 |
14 | if (source.droppableId !== destination.droppableId) {
15 | const sourceColIndex = data.findIndex(e => e.id === source.droppableId)
16 | const destinationColIndex = data.findIndex(e => e.id === destination.droppableId)
17 |
18 | const sourceCol = data[sourceColIndex]
19 | const destinationCol = data[destinationColIndex]
20 |
21 | const sourceTask = [...sourceCol.tasks]
22 | const destinationTask = [...destinationCol.tasks]
23 |
24 | const [removed] = sourceTask.splice(source.index, 1)
25 | destinationTask.splice(destination.index, 0, removed)
26 |
27 | data[sourceColIndex].tasks = sourceTask
28 | data[destinationColIndex].tasks = destinationTask
29 |
30 | setData(data)
31 | }
32 | }
33 |
34 | return (
35 |
36 |
37 | {
38 | data.map(section => (
39 |
43 | {(provided) => (
44 |
49 |
50 | {section.title}
51 |
52 |
53 | {
54 | section.tasks.map((task, index) => (
55 |
60 | {(provided, snapshot) => (
61 |
70 |
71 | {task.title}
72 |
73 |
74 | )}
75 |
76 | ))
77 | }
78 | {provided.placeholder}
79 |
80 |
81 | )}
82 |
83 | ))
84 | }
85 |
86 |
87 | )
88 | }
89 |
90 | export default Kanban
--------------------------------------------------------------------------------