mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-22 07:00:58 -05:00
feat(categories): basic dnd kanban board
This commit is contained in:
parent
314ecee95c
commit
da47348273
3 changed files with 217 additions and 18 deletions
|
@ -82,6 +82,7 @@
|
|||
"@types/node": "^15.12.4",
|
||||
"@types/preact-i18n": "^2.3.0",
|
||||
"@types/prismjs": "^1.16.5",
|
||||
"@types/react-beautiful-dnd": "^13.1.2",
|
||||
"@types/react-helmet": "^6.1.1",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"@types/react-scroll": "^1.8.2",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import isEqual from "lodash.isequal";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";
|
||||
import { Category } from "revolt-api/types/Servers";
|
||||
import { Server } from "revolt.js/dist/maps/Servers";
|
||||
import styled from "styled-components";
|
||||
|
@ -28,10 +29,25 @@ const KanbanEntry = styled.div`
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
gap: 4px;
|
||||
margin: 4px;
|
||||
height: 40px;
|
||||
padding: 8px;
|
||||
flex-shrink: 0;
|
||||
font-size: 0.9em;
|
||||
background: var(--primary-background);
|
||||
|
||||
img {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
min-width: 0;
|
||||
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
const KanbanList = styled.div`
|
||||
|
@ -42,15 +58,33 @@ const KanbanList = styled.div`
|
|||
overflow-y: auto;
|
||||
flex-direction: column;
|
||||
background: var(--secondary-background);
|
||||
|
||||
> [data-rbd-droppable-id] {
|
||||
min-height: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
const KanbanListTitle = styled.div`
|
||||
height: 42px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
`;
|
||||
|
||||
const KanbanBoard = styled.div`
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
overflow-x: scroll;
|
||||
flex-direction: row;
|
||||
`;
|
||||
|
||||
const FullSize = styled.div`
|
||||
flex-grow: 1;
|
||||
|
||||
> * {
|
||||
height: 100%;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
server: Server;
|
||||
}
|
||||
|
@ -61,24 +95,181 @@ export const Categories = observer(({ server }: Props) => {
|
|||
);
|
||||
|
||||
return (
|
||||
<DragDropContext
|
||||
onDragEnd={(target) => {
|
||||
const { destination, source, draggableId, type } = target;
|
||||
|
||||
if (
|
||||
!destination ||
|
||||
(destination.droppableId === source.droppableId &&
|
||||
destination.index === source.index)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === "column") {
|
||||
// Remove from array.
|
||||
const cat = categories.find((x) => x.id === draggableId);
|
||||
const arr = categories.filter((x) => x.id !== draggableId);
|
||||
|
||||
// Insert at new position.
|
||||
arr.splice(destination.index, 0, cat!);
|
||||
setCategories(arr);
|
||||
} else {
|
||||
setCategories(
|
||||
categories.map((category) => {
|
||||
if (category.id === destination.droppableId) {
|
||||
const channels = category.channels.filter(
|
||||
(x) => x !== draggableId,
|
||||
);
|
||||
|
||||
channels.splice(
|
||||
destination.index,
|
||||
0,
|
||||
draggableId,
|
||||
);
|
||||
|
||||
return {
|
||||
...category,
|
||||
channels,
|
||||
};
|
||||
} else if (category.id === source.droppableId) {
|
||||
return {
|
||||
...category,
|
||||
channels: category.channels.filter(
|
||||
(x) => x !== draggableId,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return category;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}}>
|
||||
<FullSize>
|
||||
<Droppable
|
||||
droppableId="categories"
|
||||
direction="horizontal"
|
||||
type="column">
|
||||
{(provided) =>
|
||||
(
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.droppableProps}>
|
||||
<KanbanBoard>
|
||||
{categories.map((category, i) => (
|
||||
<KanbanList key={category.id}>
|
||||
<h3>{category.title}</h3>
|
||||
{category.channels.map((x) => {
|
||||
const channel = server.client.channels.get(x);
|
||||
if (!channel) return null;
|
||||
{categories.map((category, index) => (
|
||||
<Draggable
|
||||
key={category.id}
|
||||
draggableId={category.id}
|
||||
index={index}>
|
||||
{(provided) =>
|
||||
(
|
||||
<div
|
||||
{...(provided.draggableProps as any)}
|
||||
ref={provided.innerRef}>
|
||||
<KanbanList
|
||||
key={category.id}>
|
||||
<KanbanListTitle
|
||||
{...(provided.dragHandleProps as any)}>
|
||||
<span>
|
||||
{
|
||||
category.title
|
||||
}
|
||||
</span>
|
||||
</KanbanListTitle>
|
||||
<Droppable
|
||||
droppableId={
|
||||
category.id
|
||||
}
|
||||
key={
|
||||
category.id
|
||||
}>
|
||||
{(provided) =>
|
||||
(
|
||||
<div
|
||||
ref={
|
||||
provided.innerRef
|
||||
}
|
||||
{...provided.droppableProps}>
|
||||
{category.channels.map(
|
||||
(
|
||||
x,
|
||||
index,
|
||||
) => {
|
||||
const channel =
|
||||
server.client.channels.get(
|
||||
x,
|
||||
);
|
||||
if (
|
||||
!channel
|
||||
)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<KanbanEntry key={x}>
|
||||
<ChannelIcon target={channel} size={24} />{" "}
|
||||
{channel.name}
|
||||
<Draggable
|
||||
key={
|
||||
x
|
||||
}
|
||||
draggableId={
|
||||
x
|
||||
}
|
||||
index={
|
||||
index
|
||||
}>
|
||||
{(
|
||||
provided,
|
||||
) =>
|
||||
(
|
||||
<div
|
||||
{...(provided.draggableProps as any)}
|
||||
{...provided.dragHandleProps}
|
||||
ref={
|
||||
provided.innerRef
|
||||
}>
|
||||
<KanbanEntry>
|
||||
<ChannelIcon
|
||||
target={
|
||||
channel
|
||||
}
|
||||
size={
|
||||
24
|
||||
}
|
||||
/>
|
||||
<span>
|
||||
{
|
||||
channel.name
|
||||
}
|
||||
</span>
|
||||
</KanbanEntry>
|
||||
</div>
|
||||
) as any
|
||||
}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
},
|
||||
)}
|
||||
{
|
||||
provided.placeholder
|
||||
}
|
||||
</div>
|
||||
) as any
|
||||
}
|
||||
</Droppable>
|
||||
</KanbanList>
|
||||
</div>
|
||||
) as any
|
||||
}
|
||||
</Draggable>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</KanbanBoard>
|
||||
</div>
|
||||
) as any
|
||||
}
|
||||
</Droppable>
|
||||
</FullSize>
|
||||
</DragDropContext>
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -1398,6 +1398,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
|
||||
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
|
||||
|
||||
"@types/react-beautiful-dnd@^13.1.2":
|
||||
version "13.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.2.tgz#510405abb09f493afdfd898bf83995dc6385c130"
|
||||
integrity sha512-+OvPkB8CdE/bGdXKyIhc/Lm2U7UAYCCJgsqmopFmh9gbAudmslkI8eOrPDjg4JhwSE6wytz4a3/wRjKtovHVJg==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-helmet@^6.1.1":
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.2.tgz#e9d7d16b29e4ec5716711c52c35c3cec45819eac"
|
||||
|
|
Loading…
Reference in a new issue