mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-29 15:25:45 +03:00
Sammy/t 394 aadev i have a storybook with drag and drop features (#255)
* feature: add Board component * feature: add BoardColumn * feature: add BoardCard * chore: install react beautiful dnd * refactor: use children instead of item parameter * feature: wrap board in DragDropContext * feature: wrap columns in dropable * feature: wrap boardCard in draggable * feature: add a second column * refactor: rename columns to initialBoard * refactor: use itemKeys instead if item directly * feature: add key for react to render * refactor: type the onDragEnd callback * feature: drag and drop elements between columns
This commit is contained in:
parent
ce4ba10f7b
commit
b827716d1b
117
front/package-lock.json
generated
117
front/package-lock.json
generated
@ -23,6 +23,7 @@
|
|||||||
"libphonenumber-js": "^1.10.26",
|
"libphonenumber-js": "^1.10.26",
|
||||||
"luxon": "^3.3.0",
|
"luxon": "^3.3.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-datepicker": "^4.11.0",
|
"react-datepicker": "^4.11.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hotkeys-hook": "^4.4.0",
|
"react-hotkeys-hook": "^4.4.0",
|
||||||
@ -58,6 +59,7 @@
|
|||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"@types/jest": "^27.5.2",
|
"@types/jest": "^27.5.2",
|
||||||
"@types/luxon": "^3.3.0",
|
"@types/luxon": "^3.3.0",
|
||||||
|
"@types/react-beautiful-dnd": "^13.1.4",
|
||||||
"@types/react-datepicker": "^4.11.2",
|
"@types/react-datepicker": "^4.11.2",
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
@ -10961,6 +10963,15 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/hoist-non-react-statics": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"hoist-non-react-statics": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/html-minifier-terser": {
|
"node_modules/@types/html-minifier-terser": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
|
||||||
@ -11174,6 +11185,15 @@
|
|||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react-beautiful-dnd": {
|
||||||
|
"version": "13.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.4.tgz",
|
||||||
|
"integrity": "sha512-4bIBdzOr0aavN+88q3C7Pgz+xkb7tz3whORYrmSj77wfVEMfiWiooIwVWFR7KM2e+uGTe5BVrXqSfb0aHeflJA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/react-datepicker": {
|
"node_modules/@types/react-datepicker": {
|
||||||
"version": "4.11.2",
|
"version": "4.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-4.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-4.11.2.tgz",
|
||||||
@ -11194,6 +11214,17 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react-redux": {
|
||||||
|
"version": "7.1.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.25.tgz",
|
||||||
|
"integrity": "sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hoist-non-react-statics": "^3.3.0",
|
||||||
|
"@types/react": "*",
|
||||||
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
|
"redux": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/resolve": {
|
"node_modules/@types/resolve": {
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
||||||
@ -16071,6 +16102,14 @@
|
|||||||
"postcss": "^8.4"
|
"postcss": "^8.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-box-model": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tiny-invariant": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/css-declaration-sorter": {
|
"node_modules/css-declaration-sorter": {
|
||||||
"version": "6.4.0",
|
"version": "6.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz",
|
||||||
@ -28433,6 +28472,11 @@
|
|||||||
"node": ">= 4.0.0"
|
"node": ">= 4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/memoize-one": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
||||||
|
},
|
||||||
"node_modules/memoizerific": {
|
"node_modules/memoizerific": {
|
||||||
"version": "1.11.3",
|
"version": "1.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz",
|
||||||
@ -32085,6 +32129,11 @@
|
|||||||
"performance-now": "^2.1.0"
|
"performance-now": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/raf-schd": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="
|
||||||
|
},
|
||||||
"node_modules/ramda": {
|
"node_modules/ramda": {
|
||||||
"version": "0.29.0",
|
"version": "0.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.0.tgz",
|
||||||
@ -32165,6 +32214,24 @@
|
|||||||
"asap": "~2.0.6"
|
"asap": "~2.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-beautiful-dnd": {
|
||||||
|
"version": "13.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
|
||||||
|
"integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.9.2",
|
||||||
|
"css-box-model": "^1.2.0",
|
||||||
|
"memoize-one": "^5.1.1",
|
||||||
|
"raf-schd": "^4.0.2",
|
||||||
|
"react-redux": "^7.2.0",
|
||||||
|
"redux": "^4.0.4",
|
||||||
|
"use-memo-one": "^1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.5 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-colorful": {
|
"node_modules/react-colorful": {
|
||||||
"version": "5.6.1",
|
"version": "5.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
|
||||||
@ -32559,6 +32626,35 @@
|
|||||||
"react-dom": "^16.8.0 || ^17 || ^18"
|
"react-dom": "^16.8.0 || ^17 || ^18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-redux": {
|
||||||
|
"version": "7.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
|
||||||
|
"integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.15.4",
|
||||||
|
"@types/react-redux": "^7.1.20",
|
||||||
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^17.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.3 || ^17 || ^18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-redux/node_modules/react-is": {
|
||||||
|
"version": "17.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.14.0",
|
"version": "0.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
|
||||||
@ -34080,6 +34176,14 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/redux": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.9.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/regenerate": {
|
"node_modules/regenerate": {
|
||||||
"version": "1.4.2",
|
"version": "1.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||||
@ -36694,6 +36798,11 @@
|
|||||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-invariant": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw=="
|
||||||
|
},
|
||||||
"node_modules/title-case": {
|
"node_modules/title-case": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz",
|
||||||
@ -37560,6 +37669,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-memo-one": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/use-resize-observer": {
|
"node_modules/use-resize-observer": {
|
||||||
"version": "9.1.0",
|
"version": "9.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz",
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"libphonenumber-js": "^1.10.26",
|
"libphonenumber-js": "^1.10.26",
|
||||||
"luxon": "^3.3.0",
|
"luxon": "^3.3.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-datepicker": "^4.11.0",
|
"react-datepicker": "^4.11.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hotkeys-hook": "^4.4.0",
|
"react-hotkeys-hook": "^4.4.0",
|
||||||
@ -102,6 +103,7 @@
|
|||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"@types/jest": "^27.5.2",
|
"@types/jest": "^27.5.2",
|
||||||
"@types/luxon": "^3.3.0",
|
"@types/luxon": "^3.3.0",
|
||||||
|
"@types/react-beautiful-dnd": "^13.1.4",
|
||||||
"@types/react-datepicker": "^4.11.2",
|
"@types/react-datepicker": "^4.11.2",
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
|
127
front/src/modules/ui/components/board/Board.tsx
Normal file
127
front/src/modules/ui/components/board/Board.tsx
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import {
|
||||||
|
DragDropContext,
|
||||||
|
Draggable,
|
||||||
|
Droppable,
|
||||||
|
OnDragEndResponder,
|
||||||
|
} from 'react-beautiful-dnd';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { BoardCard } from './BoardCard';
|
||||||
|
import { BoardColumn } from './BoardColumn';
|
||||||
|
|
||||||
|
const StyledBoard = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type ItemKey = `item-${number}`;
|
||||||
|
interface Item {
|
||||||
|
id: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
interface Items {
|
||||||
|
[key: string]: Item;
|
||||||
|
}
|
||||||
|
interface Column {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
itemKeys: ItemKey[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const items: Items = {
|
||||||
|
'item-1': { id: 'item-1', content: 'Item 1' },
|
||||||
|
'item-2': { id: 'item-2', content: 'Item 2' },
|
||||||
|
'item-3': { id: 'item-3', content: 'Item 3' },
|
||||||
|
'item-4': { id: 'item-4', content: 'Item 4' },
|
||||||
|
'item-5': { id: 'item-5', content: 'Item 5' },
|
||||||
|
'item-6': { id: 'item-6', content: 'Item 6' },
|
||||||
|
} satisfies Record<ItemKey, { id: ItemKey; content: string }>;
|
||||||
|
|
||||||
|
const initialBoard = [
|
||||||
|
{
|
||||||
|
id: 'column-1',
|
||||||
|
title: 'Column 1',
|
||||||
|
itemKeys: ['item-1', 'item-2', 'item-3', 'item-4'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'column-2',
|
||||||
|
title: 'Column 2',
|
||||||
|
itemKeys: ['item-5', 'item-6'],
|
||||||
|
},
|
||||||
|
] satisfies Column[];
|
||||||
|
|
||||||
|
export const Board = () => {
|
||||||
|
const [board, setBoard] = useState<Column[]>(initialBoard);
|
||||||
|
|
||||||
|
const onDragEnd: OnDragEndResponder = useCallback(
|
||||||
|
(result) => {
|
||||||
|
const { destination, source } = result;
|
||||||
|
if (!destination) return;
|
||||||
|
const sourceColumnIndex = board.findIndex(
|
||||||
|
(column) => column.id === source.droppableId,
|
||||||
|
);
|
||||||
|
const sourceColumn = board[sourceColumnIndex];
|
||||||
|
const destinationColumnIndex = board.findIndex(
|
||||||
|
(column) => column.id === destination.droppableId,
|
||||||
|
);
|
||||||
|
const destinationColumn = board[destinationColumnIndex];
|
||||||
|
if (!destinationColumn || !sourceColumn) return;
|
||||||
|
const sourceItems = sourceColumn.itemKeys;
|
||||||
|
const destinationItems = destinationColumn.itemKeys;
|
||||||
|
|
||||||
|
const [removed] = sourceItems.splice(source.index, 1);
|
||||||
|
destinationItems.splice(destination.index, 0, removed);
|
||||||
|
|
||||||
|
const newSourceColumn = {
|
||||||
|
...sourceColumn,
|
||||||
|
itemKeys: sourceItems,
|
||||||
|
};
|
||||||
|
|
||||||
|
const newDestinationColumn = {
|
||||||
|
...destinationColumn,
|
||||||
|
itemKeys: destinationItems,
|
||||||
|
};
|
||||||
|
|
||||||
|
const newBoard = [...board];
|
||||||
|
newBoard.splice(sourceColumnIndex, 1, newSourceColumn);
|
||||||
|
newBoard.splice(destinationColumnIndex, 1, newDestinationColumn);
|
||||||
|
setBoard(newBoard);
|
||||||
|
},
|
||||||
|
[board],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
|
<StyledBoard>
|
||||||
|
{board.map((column) => (
|
||||||
|
<Droppable key={column.id} droppableId={column.id}>
|
||||||
|
{(provided) =>
|
||||||
|
provided && (
|
||||||
|
<BoardColumn title={column.title} droppableProvided={provided}>
|
||||||
|
{column.itemKeys.map((itemKey, index) => (
|
||||||
|
<Draggable
|
||||||
|
key={itemKey}
|
||||||
|
draggableId={itemKey}
|
||||||
|
index={index}
|
||||||
|
>
|
||||||
|
{(provided) =>
|
||||||
|
provided && (
|
||||||
|
<BoardCard
|
||||||
|
content={items[itemKey].content}
|
||||||
|
draggableProvided={provided}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Draggable>
|
||||||
|
))}
|
||||||
|
</BoardColumn>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Droppable>
|
||||||
|
))}
|
||||||
|
</StyledBoard>
|
||||||
|
</DragDropContext>
|
||||||
|
);
|
||||||
|
};
|
26
front/src/modules/ui/components/board/BoardCard.tsx
Normal file
26
front/src/modules/ui/components/board/BoardCard.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { DraggableProvided } from 'react-beautiful-dnd';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
const StyledCard = styled.div`
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type BoardCardProps = {
|
||||||
|
content: string;
|
||||||
|
draggableProvided: DraggableProvided;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BoardCard = ({ content, draggableProvided }: BoardCardProps) => {
|
||||||
|
return (
|
||||||
|
<StyledCard
|
||||||
|
ref={draggableProvided?.innerRef}
|
||||||
|
{...draggableProvided.dragHandleProps}
|
||||||
|
{...draggableProvided.draggableProps}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</StyledCard>
|
||||||
|
);
|
||||||
|
};
|
35
front/src/modules/ui/components/board/BoardColumn.tsx
Normal file
35
front/src/modules/ui/components/board/BoardColumn.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { DroppableProvided } from 'react-beautiful-dnd';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
const StyledColumn = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 300px;
|
||||||
|
margin-right: 16px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type BoardColumnProps = {
|
||||||
|
title: string;
|
||||||
|
children: any[];
|
||||||
|
droppableProvided: DroppableProvided;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BoardColumn = ({
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
droppableProvided,
|
||||||
|
}: BoardColumnProps) => {
|
||||||
|
return (
|
||||||
|
<StyledColumn
|
||||||
|
ref={droppableProvided.innerRef}
|
||||||
|
{...droppableProvided.droppableProps}
|
||||||
|
>
|
||||||
|
<h3>{title}</h3>
|
||||||
|
{children}
|
||||||
|
{droppableProvided.placeholder}
|
||||||
|
</StyledColumn>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,15 @@
|
|||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { Board } from '../Board';
|
||||||
|
|
||||||
|
const meta: Meta<typeof Board> = {
|
||||||
|
title: 'Components/Board',
|
||||||
|
component: Board,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof Board>;
|
||||||
|
|
||||||
|
export const OneColumnBoard: Story = {
|
||||||
|
render: () => <Board />,
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user