feat: layouts and styles for desktop and mobile
This commit is contained in:
7
src/app/deals/components/Board/Board.module.css
Normal file
7
src/app/deals/components/Board/Board.module.css
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
.board {
|
||||
min-width: 130px;
|
||||
flex-wrap: nowrap;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
@ -1,53 +1,58 @@
|
||||
import React, { CSSProperties, FC, useState } from "react";
|
||||
import React, { FC, useState } from "react";
|
||||
import { Box, Group, Text } from "@mantine/core";
|
||||
import styles from "@/app/deals/components/Board/Board.module.css";
|
||||
import BoardMenu from "@/app/deals/components/Board/BoardMenu";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import SmallPageBlock from "@/components/layout/SmallPageBlock/SmallPageBlock";
|
||||
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
||||
import { BoardSchema } from "@/lib/client";
|
||||
|
||||
type Props = {
|
||||
board: BoardSchema;
|
||||
containerStyle?: CSSProperties;
|
||||
};
|
||||
|
||||
const Board: FC<Props> = ({ board, containerStyle }) => {
|
||||
const Board: FC<Props> = ({ board }) => {
|
||||
const { selectedBoard } = useBoardsContext();
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const { onUpdateBoard } = useBoardsContext();
|
||||
|
||||
return (
|
||||
<Group
|
||||
miw={130}
|
||||
px={"md"}
|
||||
py={"xs"}
|
||||
justify={"space-between"}
|
||||
wrap={"nowrap"}
|
||||
style={{ ...containerStyle, borderWidth: 1 }}
|
||||
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>{board.name}</Text>
|
||||
</Box>
|
||||
<BoardMenu
|
||||
isHovered={isHovered}
|
||||
board={board}
|
||||
startEditing={startEditing}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
modalTitle={"Редактирование доски"}
|
||||
/>
|
||||
</Group>
|
||||
<SmallPageBlock active={selectedBoard?.id === board.id}>
|
||||
<Group
|
||||
px={"md"}
|
||||
py={"xs"}
|
||||
bdrs={"lg"}
|
||||
justify={"space-between"}
|
||||
className={styles.board}
|
||||
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>{board.name}</Text>
|
||||
</Box>
|
||||
<BoardMenu
|
||||
isHovered={isHovered}
|
||||
board={board}
|
||||
startEditing={startEditing}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
modalTitle={"Редактирование доски"}
|
||||
/>
|
||||
</Group>
|
||||
</SmallPageBlock>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -27,9 +27,8 @@ const BoardMenu: FC<Props> = ({
|
||||
<Box
|
||||
style={{
|
||||
opacity:
|
||||
isMobile ||
|
||||
isHovered ||
|
||||
selectedBoard?.id === board.id
|
||||
!isMobile &&
|
||||
(isHovered || selectedBoard?.id === board.id)
|
||||
? 1
|
||||
: 0,
|
||||
cursor: "pointer",
|
||||
|
||||
50
src/app/deals/components/BoardMobile/BoardMobile.tsx
Normal file
50
src/app/deals/components/BoardMobile/BoardMobile.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import React, { FC } from "react";
|
||||
import { Box, Group, Text } from "@mantine/core";
|
||||
import BoardMenu from "@/app/deals/components/Board/BoardMenu";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
||||
import { BoardSchema } from "@/lib/client";
|
||||
|
||||
type Props = {
|
||||
board: BoardSchema;
|
||||
};
|
||||
|
||||
const BoardMobile: FC<Props> = ({ board }) => {
|
||||
const { selectedBoard } = useBoardsContext();
|
||||
const { onUpdateBoard } = useBoardsContext();
|
||||
|
||||
return (
|
||||
<Group
|
||||
px={"md"}
|
||||
py={"xs"}
|
||||
bdrs={"lg"}
|
||||
wrap={"nowrap"}
|
||||
justify={"space-between"}>
|
||||
<InPlaceInput
|
||||
defaultValue={board.name}
|
||||
onComplete={value => onUpdateBoard(board.id, { name: value })}
|
||||
inputStyles={{
|
||||
input: {
|
||||
height: 25,
|
||||
minHeight: 25,
|
||||
},
|
||||
}}
|
||||
getChildren={startEditing => (
|
||||
<>
|
||||
<Box>
|
||||
<Text>{board.name}</Text>
|
||||
</Box>
|
||||
<BoardMenu
|
||||
isHovered
|
||||
board={board}
|
||||
startEditing={startEditing}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
modalTitle={"Редактирование доски"}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
export default BoardMobile;
|
||||
@ -3,6 +3,7 @@
|
||||
import React from "react";
|
||||
import { Group, ScrollArea } from "@mantine/core";
|
||||
import Board from "@/app/deals/components/Board/Board";
|
||||
import BoardMobile from "@/app/deals/components/BoardMobile/BoardMobile";
|
||||
import CreateBoardButton from "@/app/deals/components/CreateBoardButton/CreateBoardButton";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import SortableDnd from "@/components/dnd/SortableDnd";
|
||||
@ -13,9 +14,11 @@ const Boards = () => {
|
||||
const { boards, setSelectedBoard, onUpdateBoard } = useBoardsContext();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const renderBoard = (board: BoardSchema) => {
|
||||
return <Board board={board} />;
|
||||
};
|
||||
const renderBoard = (board: BoardSchema) => <Board board={board} />;
|
||||
|
||||
const renderBoardMobile = (board: BoardSchema) => (
|
||||
<BoardMobile board={board} />
|
||||
);
|
||||
|
||||
const onDragEnd = (itemId: number, newLexorank: string) => {
|
||||
onUpdateBoard(itemId, { lexorank: newLexorank });
|
||||
@ -31,13 +34,16 @@ const Boards = () => {
|
||||
scrollbars={"x"}
|
||||
scrollbarSize={0}
|
||||
w={"100%"}>
|
||||
<Group wrap={"nowrap"}>
|
||||
<Group
|
||||
wrap={"nowrap"}
|
||||
gap={0}>
|
||||
<SortableDnd
|
||||
initialItems={boards}
|
||||
renderItem={renderBoard}
|
||||
renderItem={isMobile ? renderBoardMobile : renderBoard}
|
||||
onDragEnd={onDragEnd}
|
||||
onItemClick={selectBoard}
|
||||
containerStyle={{ flexWrap: "nowrap" }}
|
||||
dragHandleStyle={{ cursor: "pointer" }}
|
||||
disabled={isMobile}
|
||||
/>
|
||||
<CreateBoardButton />
|
||||
|
||||
@ -1,26 +1,33 @@
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import { Box } from "@mantine/core";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import SmallPageBlock from "@/components/layout/SmallPageBlock/SmallPageBlock";
|
||||
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
||||
|
||||
const CreateBoardButton = () => {
|
||||
const { onCreateBoard } = useBoardsContext();
|
||||
|
||||
return (
|
||||
<Box
|
||||
style={{ cursor: "pointer" }}
|
||||
pr={"md"}>
|
||||
<SmallPageBlock style={{ cursor: "pointer" }}>
|
||||
<InPlaceInput
|
||||
placeholder={"Название доски"}
|
||||
onComplete={onCreateBoard}
|
||||
getChildren={startEditing => (
|
||||
<Box onClick={startEditing}>
|
||||
<Box
|
||||
p={"sm"}
|
||||
onClick={startEditing}>
|
||||
<IconPlus />
|
||||
</Box>
|
||||
)}
|
||||
modalTitle={"Создание доски"}
|
||||
inputStyles={{
|
||||
wrapper: {
|
||||
marginLeft: 15,
|
||||
marginRight: 15,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</SmallPageBlock>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ const CreateStatusButton = () => {
|
||||
cursor: "pointer",
|
||||
}}>
|
||||
<InPlaceInput
|
||||
placeholder={"Название доски"}
|
||||
placeholder={"Название колонки"}
|
||||
onComplete={onCreateStatus}
|
||||
getChildren={startEditing => (
|
||||
<Box
|
||||
|
||||
8
src/app/deals/components/DealCard/DealCard.module.css
Normal file
8
src/app/deals/components/DealCard/DealCard.module.css
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
.container {
|
||||
background-color: #e0f0f4;
|
||||
|
||||
@mixin dark {
|
||||
background-color: var(--mantine-color-dark-7);
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
import { Card } from "@mantine/core";
|
||||
import { DealSchema } from "@/lib/client";
|
||||
import styles from "./DealCard.module.css";
|
||||
|
||||
type Props = {
|
||||
deal: DealSchema;
|
||||
};
|
||||
|
||||
const DealCard = ({ deal }: Props) => {
|
||||
return <Card>{deal.name}</Card>;
|
||||
return <Card className={styles.container}>{deal.name}</Card>;
|
||||
};
|
||||
|
||||
export default DealCard;
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { IconChevronLeft, IconSettings } from "@tabler/icons-react";
|
||||
import { Box, Group, Text } from "@mantine/core";
|
||||
import { Box, Group, Stack, Text } from "@mantine/core";
|
||||
import Boards from "@/app/deals/components/Boards/Boards";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||
import ProjectSelect from "@/components/selects/ProjectSelect/ProjectSelect";
|
||||
@ -16,32 +17,39 @@ const Header = () => {
|
||||
|
||||
const getDesktopHeader = () => {
|
||||
return (
|
||||
<>
|
||||
<Group
|
||||
w={"100%"}
|
||||
justify={"space-between"}
|
||||
wrap={"nowrap"}>
|
||||
<Boards />
|
||||
<ColorSchemeToggle />
|
||||
<ProjectSelect
|
||||
data={projects}
|
||||
value={selectedProject}
|
||||
onChange={value => value && setSelectedProject(value)}
|
||||
/>
|
||||
</>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
const getMobileHeader = () => {
|
||||
return (
|
||||
<Group
|
||||
justify={"space-between"}
|
||||
w={"100%"}>
|
||||
<Box p={"md"}>
|
||||
<IconChevronLeft />
|
||||
</Box>
|
||||
<Text>{selectedProject?.name}</Text>
|
||||
<Box
|
||||
p={"md"}
|
||||
onClick={() => setIsEditorDrawerOpened(true)}>
|
||||
<IconSettings />
|
||||
</Box>
|
||||
</Group>
|
||||
<Stack>
|
||||
<Group
|
||||
justify={"space-between"}
|
||||
w={"100%"}>
|
||||
<Box p={"md"}>
|
||||
<IconChevronLeft />
|
||||
</Box>
|
||||
<Text>{selectedProject?.name}</Text>
|
||||
<Box
|
||||
p={"md"}
|
||||
onClick={() => setIsEditorDrawerOpened(true)}>
|
||||
<IconSettings />
|
||||
</Box>
|
||||
</Group>
|
||||
<Boards />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
|
||||
.container {
|
||||
min-width: 150px;
|
||||
width: 15vw;
|
||||
|
||||
@media (max-width: 48em) {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { Box, Group, Text } from "@mantine/core";
|
||||
import styles from "@/app/deals/components/StatusColumnWrapper/StatusColumnWrapper.module.css";
|
||||
import StatusMenu from "@/app/deals/components/StatusColumnWrapper/StatusMenu";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
||||
@ -27,18 +28,11 @@ const StatusColumnWrapper = ({
|
||||
|
||||
return (
|
||||
<Box
|
||||
p={"xs"}
|
||||
style={{
|
||||
borderWidth: 1,
|
||||
borderColor: "gray",
|
||||
width: "15vw",
|
||||
minWidth: 150,
|
||||
}}>
|
||||
mx={7}
|
||||
className={styles.container}>
|
||||
<Group
|
||||
wrap={"nowrap"}
|
||||
justify={"space-between"}
|
||||
ml={"xs"}
|
||||
mb={"xs"}>
|
||||
className={"flex-nowrap border-b-3 border-blue-500 mb-3 p-3"}>
|
||||
<InPlaceInput
|
||||
defaultValue={status.name}
|
||||
onComplete={value => handleSave(value)}
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
} from "@tabler/icons-react";
|
||||
import { Box, Group, Menu, Text } from "@mantine/core";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import useIsMobile from "@/hooks/useIsMobile";
|
||||
import { StatusSchema } from "@/lib/client";
|
||||
|
||||
type Props = {
|
||||
@ -15,6 +16,7 @@ type Props = {
|
||||
};
|
||||
|
||||
const StatusMenu: FC<Props> = ({ status, handleEdit }) => {
|
||||
const isMobile = useIsMobile();
|
||||
const { onDeleteStatus, setIsEditorDrawerOpened } = useStatusesContext();
|
||||
|
||||
return (
|
||||
@ -50,16 +52,18 @@ const StatusMenu: FC<Props> = ({ status, handleEdit }) => {
|
||||
<Text>Удалить</Text>
|
||||
</Group>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setIsEditorDrawerOpened(true);
|
||||
}}>
|
||||
<Group wrap={"nowrap"}>
|
||||
<IconExchange />
|
||||
<Text>Изменить порядок</Text>
|
||||
</Group>
|
||||
</Menu.Item>
|
||||
{isMobile && (
|
||||
<Menu.Item
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setIsEditorDrawerOpened(true);
|
||||
}}>
|
||||
<Group wrap={"nowrap"}>
|
||||
<IconExchange />
|
||||
<Text>Изменить порядок</Text>
|
||||
</Group>
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
@ -6,12 +6,20 @@ import {
|
||||
useSensors,
|
||||
} from "@dnd-kit/core";
|
||||
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
||||
import useIsMobile from "@/hooks/useIsMobile";
|
||||
|
||||
const useDndSensors = () => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const sensorOptions = {
|
||||
activationConstraint: {
|
||||
distance: 5,
|
||||
},
|
||||
activationConstraint: isMobile
|
||||
? {
|
||||
delay: 500,
|
||||
tolerance: 5,
|
||||
}
|
||||
: {
|
||||
distance: 5,
|
||||
},
|
||||
};
|
||||
|
||||
return useSensors(
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { Divider } from "@mantine/core";
|
||||
import Boards from "@/app/deals/components/Boards/Boards";
|
||||
import Funnel from "@/app/deals/components/Funnel/Funnel";
|
||||
import Header from "@/app/deals/components/Header/Header";
|
||||
import { BoardsContextProvider } from "@/app/deals/contexts/BoardsContext";
|
||||
@ -13,13 +11,15 @@ import { DealsContextProvider } from "./contexts/DealsContext";
|
||||
|
||||
export default function DealsPage() {
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageBlock>
|
||||
<ProjectsContextProvider>
|
||||
<BoardsContextProvider>
|
||||
<ProjectsContextProvider>
|
||||
<BoardsContextProvider>
|
||||
<PageContainer>
|
||||
<PageBlock
|
||||
transparent
|
||||
style={{ padding: 0, paddingRight: 15 }}>
|
||||
<Header />
|
||||
<Boards />
|
||||
<Divider my={"xl"} />
|
||||
</PageBlock>
|
||||
<PageBlock className={"mobile-padding-height"}>
|
||||
<StatusesContextProvider>
|
||||
<DealsContextProvider>
|
||||
<Funnel />
|
||||
@ -27,9 +27,9 @@ export default function DealsPage() {
|
||||
<BoardStatusesEditorDrawer />
|
||||
</StatusesContextProvider>
|
||||
<ProjectBoardsEditorDrawer />
|
||||
</BoardsContextProvider>
|
||||
</ProjectsContextProvider>
|
||||
</PageBlock>
|
||||
</PageContainer>
|
||||
</PageBlock>
|
||||
</PageContainer>
|
||||
</BoardsContextProvider>
|
||||
</ProjectsContextProvider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user