feat: boards on desktop as on mobile

This commit is contained in:
2025-08-18 11:35:52 +04:00
parent 49b1a235be
commit 83432b3f33
15 changed files with 117 additions and 255 deletions

View File

@ -0,0 +1,14 @@
.board {
min-width: 50px;
flex-wrap: nowrap;
gap: 3px;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
border-bottom: 2px solid gray;
}
.board-selected {
border: 2px solid gray;
border-bottom: 0;
}

View File

@ -0,0 +1,63 @@
import React, { FC, useState } from "react";
import classNames from "classnames";
import { Box, Group, Text } from "@mantine/core";
import BoardMenu from "@/app/deals/components/shared/BoardMenu/BoardMenu";
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
import useIsMobile from "@/hooks/useIsMobile";
import { BoardSchema } from "@/lib/client";
import styles from "./Board.module.css";
type Props = {
board: BoardSchema;
};
const Board: FC<Props> = ({ board }) => {
const { selectedBoard, onUpdateBoard } = useBoardsContext();
const isMobile = useIsMobile();
const [isHovered, setIsHovered] = useState(false);
return (
<Group
px={"md"}
py={"xs"}
className={classNames(
styles.board,
selectedBoard?.id === board.id && styles["board-selected"]
)}
justify={"space-between"}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}>
<InPlaceInput
defaultValue={board.name}
onComplete={value => onUpdateBoard(board.id, { name: value })}
inputStyles={{
input: {
height: 25,
minHeight: 25,
},
}}
getChildren={startEditing => (
<>
<Box>
<Text style={{ textWrap: "nowrap" }}>
{board.name}
</Text>
</Box>
<BoardMenu
isHovered={
!isMobile &&
(selectedBoard?.id === board.id || isHovered)
}
board={board}
startEditing={startEditing}
/>
</>
)}
modalTitle={"Редактирование доски"}
/>
</Group>
);
};
export default Board;

View File

@ -0,0 +1,52 @@
"use client";
import React from "react";
import { Group, ScrollArea } from "@mantine/core";
import Board from "@/app/deals/components/shared/Board/Board";
import CreateBoardButton from "@/app/deals/components/shared/CreateBoardButton/CreateBoardButton";
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
import SortableDnd from "@/components/dnd/SortableDnd";
import useIsMobile from "@/hooks/useIsMobile";
import { BoardSchema } from "@/lib/client";
const Boards = () => {
const { boards, setSelectedBoard, onUpdateBoard } = useBoardsContext();
const isMobile = useIsMobile();
const renderBoard = (board: BoardSchema) => <Board board={board} />;
const onDragEnd = (itemId: number, newLexorank: string) => {
onUpdateBoard(itemId, { lexorank: newLexorank });
};
const selectBoard = (board: BoardSchema) => {
setSelectedBoard(board);
};
return (
<ScrollArea
offsetScrollbars={"x"}
scrollbars={"x"}
scrollbarSize={0}
w={"100%"}
mt={"xs"}
mb={"sm"}>
<Group
wrap={"nowrap"}
gap={0}>
<SortableDnd
initialItems={boards}
renderItem={renderBoard}
onDragEnd={onDragEnd}
onItemClick={selectBoard}
containerStyle={{ flexWrap: "nowrap" }}
dragHandleStyle={{ cursor: "pointer" }}
disabled={isMobile}
/>
<CreateBoardButton />
</Group>
</ScrollArea>
);
};
export default Boards;

View File

@ -0,0 +1,11 @@
.create-button {
padding: 10px 10px 9px;
border-bottom: 2px solid gray;
}
.spacer {
height: 45px;
border-bottom: 2px solid gray;
width: 100%;
}

View File

@ -0,0 +1,35 @@
import { IconPlus } from "@tabler/icons-react";
import { Box, Space } from "@mantine/core";
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
import styles from "./CreateBoardButton.module.css";
const CreateBoardButton = () => {
const { onCreateBoard } = useBoardsContext();
return (
<>
<InPlaceInput
placeholder={"Название доски"}
onComplete={onCreateBoard}
getChildren={startEditing => (
<Box
onClick={startEditing}
className={styles["create-button"]}>
<IconPlus />
</Box>
)}
modalTitle={"Создание доски"}
inputStyles={{
wrapper: {
marginLeft: 15,
marginRight: 15,
},
}}
/>
<Space className={styles.spacer} />
</>
);
};
export default CreateBoardButton;

View File

@ -2,8 +2,7 @@
import { IconChevronLeft, IconSettings } from "@tabler/icons-react";
import { Box, Group, Stack, Text } from "@mantine/core";
import Boards from "@/app/deals/components/desktop/Boards/Boards";
import BoardsMobile from "@/app/deals/components/mobile/BoardsMobile/BoardsMobile";
import Boards from "@/app/deals/components/shared/Boards/Boards";
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
import ProjectSelect from "@/components/selects/ProjectSelect/ProjectSelect";
@ -24,10 +23,9 @@ const Header = () => {
return (
<Group
w={"100%"}
justify={"space-between"}
justify={"end"}
wrap={"nowrap"}
pr={"md"}>
<Boards />
<Group wrap={"nowrap"}>
<ColorSchemeToggle />
<ProjectSelect
@ -42,22 +40,19 @@ const Header = () => {
const getMobileHeader = () => {
return (
<Stack gap={0}>
<Group justify={"space-between"}>
<Box
p={"md"}
onClick={() => setIsProjectsDrawerOpened(true)}>
<IconChevronLeft />
</Box>
<Text>{selectedProject?.name}</Text>
<Box
p={"md"}
onClick={() => setIsEditorDrawerOpened(true)}>
<IconSettings />
</Box>
</Group>
<BoardsMobile />
</Stack>
<Group justify={"space-between"}>
<Box
p={"md"}
onClick={() => setIsProjectsDrawerOpened(true)}>
<IconChevronLeft />
</Box>
<Text>{selectedProject?.name}</Text>
<Box
p={"md"}
onClick={() => setIsEditorDrawerOpened(true)}>
<IconSettings />
</Box>
</Group>
);
};
@ -65,7 +60,12 @@ const Header = () => {
<Group
justify={"flex-end"}
w={"100%"}>
{isMobile ? getMobileHeader() : getDesktopHeader()}
<Stack
gap={0}
w={"100%"}>
{isMobile ? getMobileHeader() : getDesktopHeader()}
<Boards />
</Stack>
</Group>
);
};