feat: layouts and styles for desktop and mobile
This commit is contained in:
@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
|
"@dnd-kit/modifiers": "^9.0.0",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@mantine/core": "8.1.2",
|
"@mantine/core": "8.1.2",
|
||||||
"@mantine/form": "^8.1.3",
|
"@mantine/form": "^8.1.3",
|
||||||
|
|||||||
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,32 +1,36 @@
|
|||||||
import React, { CSSProperties, FC, useState } from "react";
|
import React, { FC, useState } from "react";
|
||||||
import { Box, Group, Text } from "@mantine/core";
|
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 BoardMenu from "@/app/deals/components/Board/BoardMenu";
|
||||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||||
|
import SmallPageBlock from "@/components/layout/SmallPageBlock/SmallPageBlock";
|
||||||
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
||||||
import { BoardSchema } from "@/lib/client";
|
import { BoardSchema } from "@/lib/client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
board: BoardSchema;
|
board: BoardSchema;
|
||||||
containerStyle?: CSSProperties;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Board: FC<Props> = ({ board, containerStyle }) => {
|
const Board: FC<Props> = ({ board }) => {
|
||||||
|
const { selectedBoard } = useBoardsContext();
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const { onUpdateBoard } = useBoardsContext();
|
const { onUpdateBoard } = useBoardsContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SmallPageBlock active={selectedBoard?.id === board.id}>
|
||||||
<Group
|
<Group
|
||||||
miw={130}
|
|
||||||
px={"md"}
|
px={"md"}
|
||||||
py={"xs"}
|
py={"xs"}
|
||||||
|
bdrs={"lg"}
|
||||||
justify={"space-between"}
|
justify={"space-between"}
|
||||||
wrap={"nowrap"}
|
className={styles.board}
|
||||||
style={{ ...containerStyle, borderWidth: 1 }}
|
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}>
|
onMouseLeave={() => setIsHovered(false)}>
|
||||||
<InPlaceInput
|
<InPlaceInput
|
||||||
defaultValue={board.name}
|
defaultValue={board.name}
|
||||||
onComplete={value => onUpdateBoard(board.id, { name: value })}
|
onComplete={value =>
|
||||||
|
onUpdateBoard(board.id, { name: value })
|
||||||
|
}
|
||||||
inputStyles={{
|
inputStyles={{
|
||||||
input: {
|
input: {
|
||||||
height: 25,
|
height: 25,
|
||||||
@ -48,6 +52,7 @@ const Board: FC<Props> = ({ board, containerStyle }) => {
|
|||||||
modalTitle={"Редактирование доски"}
|
modalTitle={"Редактирование доски"}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
|
</SmallPageBlock>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -27,9 +27,8 @@ const BoardMenu: FC<Props> = ({
|
|||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
opacity:
|
opacity:
|
||||||
isMobile ||
|
!isMobile &&
|
||||||
isHovered ||
|
(isHovered || selectedBoard?.id === board.id)
|
||||||
selectedBoard?.id === board.id
|
|
||||||
? 1
|
? 1
|
||||||
: 0,
|
: 0,
|
||||||
cursor: "pointer",
|
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 React from "react";
|
||||||
import { Group, ScrollArea } from "@mantine/core";
|
import { Group, ScrollArea } from "@mantine/core";
|
||||||
import Board from "@/app/deals/components/Board/Board";
|
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 CreateBoardButton from "@/app/deals/components/CreateBoardButton/CreateBoardButton";
|
||||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||||
import SortableDnd from "@/components/dnd/SortableDnd";
|
import SortableDnd from "@/components/dnd/SortableDnd";
|
||||||
@ -13,9 +14,11 @@ const Boards = () => {
|
|||||||
const { boards, setSelectedBoard, onUpdateBoard } = useBoardsContext();
|
const { boards, setSelectedBoard, onUpdateBoard } = useBoardsContext();
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
const renderBoard = (board: BoardSchema) => {
|
const renderBoard = (board: BoardSchema) => <Board board={board} />;
|
||||||
return <Board board={board} />;
|
|
||||||
};
|
const renderBoardMobile = (board: BoardSchema) => (
|
||||||
|
<BoardMobile board={board} />
|
||||||
|
);
|
||||||
|
|
||||||
const onDragEnd = (itemId: number, newLexorank: string) => {
|
const onDragEnd = (itemId: number, newLexorank: string) => {
|
||||||
onUpdateBoard(itemId, { lexorank: newLexorank });
|
onUpdateBoard(itemId, { lexorank: newLexorank });
|
||||||
@ -31,13 +34,16 @@ const Boards = () => {
|
|||||||
scrollbars={"x"}
|
scrollbars={"x"}
|
||||||
scrollbarSize={0}
|
scrollbarSize={0}
|
||||||
w={"100%"}>
|
w={"100%"}>
|
||||||
<Group wrap={"nowrap"}>
|
<Group
|
||||||
|
wrap={"nowrap"}
|
||||||
|
gap={0}>
|
||||||
<SortableDnd
|
<SortableDnd
|
||||||
initialItems={boards}
|
initialItems={boards}
|
||||||
renderItem={renderBoard}
|
renderItem={isMobile ? renderBoardMobile : renderBoard}
|
||||||
onDragEnd={onDragEnd}
|
onDragEnd={onDragEnd}
|
||||||
onItemClick={selectBoard}
|
onItemClick={selectBoard}
|
||||||
containerStyle={{ flexWrap: "nowrap" }}
|
containerStyle={{ flexWrap: "nowrap" }}
|
||||||
|
dragHandleStyle={{ cursor: "pointer" }}
|
||||||
disabled={isMobile}
|
disabled={isMobile}
|
||||||
/>
|
/>
|
||||||
<CreateBoardButton />
|
<CreateBoardButton />
|
||||||
|
|||||||
@ -1,26 +1,33 @@
|
|||||||
import { IconPlus } from "@tabler/icons-react";
|
import { IconPlus } from "@tabler/icons-react";
|
||||||
import { Box } from "@mantine/core";
|
import { Box } from "@mantine/core";
|
||||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||||
|
import SmallPageBlock from "@/components/layout/SmallPageBlock/SmallPageBlock";
|
||||||
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
||||||
|
|
||||||
const CreateBoardButton = () => {
|
const CreateBoardButton = () => {
|
||||||
const { onCreateBoard } = useBoardsContext();
|
const { onCreateBoard } = useBoardsContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<SmallPageBlock style={{ cursor: "pointer" }}>
|
||||||
style={{ cursor: "pointer" }}
|
|
||||||
pr={"md"}>
|
|
||||||
<InPlaceInput
|
<InPlaceInput
|
||||||
placeholder={"Название доски"}
|
placeholder={"Название доски"}
|
||||||
onComplete={onCreateBoard}
|
onComplete={onCreateBoard}
|
||||||
getChildren={startEditing => (
|
getChildren={startEditing => (
|
||||||
<Box onClick={startEditing}>
|
<Box
|
||||||
|
p={"sm"}
|
||||||
|
onClick={startEditing}>
|
||||||
<IconPlus />
|
<IconPlus />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
modalTitle={"Создание доски"}
|
modalTitle={"Создание доски"}
|
||||||
|
inputStyles={{
|
||||||
|
wrapper: {
|
||||||
|
marginLeft: 15,
|
||||||
|
marginRight: 15,
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</SmallPageBlock>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const CreateStatusButton = () => {
|
|||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
}}>
|
}}>
|
||||||
<InPlaceInput
|
<InPlaceInput
|
||||||
placeholder={"Название доски"}
|
placeholder={"Название колонки"}
|
||||||
onComplete={onCreateStatus}
|
onComplete={onCreateStatus}
|
||||||
getChildren={startEditing => (
|
getChildren={startEditing => (
|
||||||
<Box
|
<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 { Card } from "@mantine/core";
|
||||||
import { DealSchema } from "@/lib/client";
|
import { DealSchema } from "@/lib/client";
|
||||||
|
import styles from "./DealCard.module.css";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
deal: DealSchema;
|
deal: DealSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DealCard = ({ deal }: Props) => {
|
const DealCard = ({ deal }: Props) => {
|
||||||
return <Card>{deal.name}</Card>;
|
return <Card className={styles.container}>{deal.name}</Card>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DealCard;
|
export default DealCard;
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { IconChevronLeft, IconSettings } from "@tabler/icons-react";
|
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 { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||||
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||||
import ProjectSelect from "@/components/selects/ProjectSelect/ProjectSelect";
|
import ProjectSelect from "@/components/selects/ProjectSelect/ProjectSelect";
|
||||||
@ -16,19 +17,24 @@ const Header = () => {
|
|||||||
|
|
||||||
const getDesktopHeader = () => {
|
const getDesktopHeader = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<Group
|
||||||
|
w={"100%"}
|
||||||
|
justify={"space-between"}
|
||||||
|
wrap={"nowrap"}>
|
||||||
|
<Boards />
|
||||||
<ColorSchemeToggle />
|
<ColorSchemeToggle />
|
||||||
<ProjectSelect
|
<ProjectSelect
|
||||||
data={projects}
|
data={projects}
|
||||||
value={selectedProject}
|
value={selectedProject}
|
||||||
onChange={value => value && setSelectedProject(value)}
|
onChange={value => value && setSelectedProject(value)}
|
||||||
/>
|
/>
|
||||||
</>
|
</Group>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMobileHeader = () => {
|
const getMobileHeader = () => {
|
||||||
return (
|
return (
|
||||||
|
<Stack>
|
||||||
<Group
|
<Group
|
||||||
justify={"space-between"}
|
justify={"space-between"}
|
||||||
w={"100%"}>
|
w={"100%"}>
|
||||||
@ -42,6 +48,8 @@ const Header = () => {
|
|||||||
<IconSettings />
|
<IconSettings />
|
||||||
</Box>
|
</Box>
|
||||||
</Group>
|
</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 React, { ReactNode } from "react";
|
||||||
import { Box, Group, Text } from "@mantine/core";
|
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 StatusMenu from "@/app/deals/components/StatusColumnWrapper/StatusMenu";
|
||||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||||
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput";
|
||||||
@ -27,18 +28,11 @@ const StatusColumnWrapper = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
p={"xs"}
|
mx={7}
|
||||||
style={{
|
className={styles.container}>
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: "gray",
|
|
||||||
width: "15vw",
|
|
||||||
minWidth: 150,
|
|
||||||
}}>
|
|
||||||
<Group
|
<Group
|
||||||
wrap={"nowrap"}
|
|
||||||
justify={"space-between"}
|
justify={"space-between"}
|
||||||
ml={"xs"}
|
className={"flex-nowrap border-b-3 border-blue-500 mb-3 p-3"}>
|
||||||
mb={"xs"}>
|
|
||||||
<InPlaceInput
|
<InPlaceInput
|
||||||
defaultValue={status.name}
|
defaultValue={status.name}
|
||||||
onComplete={value => handleSave(value)}
|
onComplete={value => handleSave(value)}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { Box, Group, Menu, Text } from "@mantine/core";
|
import { Box, Group, Menu, Text } from "@mantine/core";
|
||||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||||
|
import useIsMobile from "@/hooks/useIsMobile";
|
||||||
import { StatusSchema } from "@/lib/client";
|
import { StatusSchema } from "@/lib/client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -15,6 +16,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const StatusMenu: FC<Props> = ({ status, handleEdit }) => {
|
const StatusMenu: FC<Props> = ({ status, handleEdit }) => {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
const { onDeleteStatus, setIsEditorDrawerOpened } = useStatusesContext();
|
const { onDeleteStatus, setIsEditorDrawerOpened } = useStatusesContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -50,6 +52,7 @@ const StatusMenu: FC<Props> = ({ status, handleEdit }) => {
|
|||||||
<Text>Удалить</Text>
|
<Text>Удалить</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
{isMobile && (
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -60,6 +63,7 @@ const StatusMenu: FC<Props> = ({ status, handleEdit }) => {
|
|||||||
<Text>Изменить порядок</Text>
|
<Text>Изменить порядок</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
)}
|
||||||
</Menu.Dropdown>
|
</Menu.Dropdown>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,10 +6,18 @@ import {
|
|||||||
useSensors,
|
useSensors,
|
||||||
} from "@dnd-kit/core";
|
} from "@dnd-kit/core";
|
||||||
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
||||||
|
import useIsMobile from "@/hooks/useIsMobile";
|
||||||
|
|
||||||
const useDndSensors = () => {
|
const useDndSensors = () => {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
const sensorOptions = {
|
const sensorOptions = {
|
||||||
activationConstraint: {
|
activationConstraint: isMobile
|
||||||
|
? {
|
||||||
|
delay: 500,
|
||||||
|
tolerance: 5,
|
||||||
|
}
|
||||||
|
: {
|
||||||
distance: 5,
|
distance: 5,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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 Funnel from "@/app/deals/components/Funnel/Funnel";
|
||||||
import Header from "@/app/deals/components/Header/Header";
|
import Header from "@/app/deals/components/Header/Header";
|
||||||
import { BoardsContextProvider } from "@/app/deals/contexts/BoardsContext";
|
import { BoardsContextProvider } from "@/app/deals/contexts/BoardsContext";
|
||||||
@ -13,13 +11,15 @@ import { DealsContextProvider } from "./contexts/DealsContext";
|
|||||||
|
|
||||||
export default function DealsPage() {
|
export default function DealsPage() {
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
|
||||||
<PageBlock>
|
|
||||||
<ProjectsContextProvider>
|
<ProjectsContextProvider>
|
||||||
<BoardsContextProvider>
|
<BoardsContextProvider>
|
||||||
|
<PageContainer>
|
||||||
|
<PageBlock
|
||||||
|
transparent
|
||||||
|
style={{ padding: 0, paddingRight: 15 }}>
|
||||||
<Header />
|
<Header />
|
||||||
<Boards />
|
</PageBlock>
|
||||||
<Divider my={"xl"} />
|
<PageBlock className={"mobile-padding-height"}>
|
||||||
<StatusesContextProvider>
|
<StatusesContextProvider>
|
||||||
<DealsContextProvider>
|
<DealsContextProvider>
|
||||||
<Funnel />
|
<Funnel />
|
||||||
@ -27,9 +27,9 @@ export default function DealsPage() {
|
|||||||
<BoardStatusesEditorDrawer />
|
<BoardStatusesEditorDrawer />
|
||||||
</StatusesContextProvider>
|
</StatusesContextProvider>
|
||||||
<ProjectBoardsEditorDrawer />
|
<ProjectBoardsEditorDrawer />
|
||||||
</BoardsContextProvider>
|
|
||||||
</ProjectsContextProvider>
|
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
|
</BoardsContextProvider>
|
||||||
|
</ProjectsContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
@mixin light {
|
@mixin light {
|
||||||
background-color: whitesmoke;
|
background-color: #f4f7fd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,11 +68,12 @@ const FunnelDnd = <
|
|||||||
dots: true,
|
dots: true,
|
||||||
infinite: false,
|
infinite: false,
|
||||||
speed: 500,
|
speed: 500,
|
||||||
slidesToShow: 1,
|
slidesToShow: 1.1,
|
||||||
slidesToScroll: 1,
|
slidesToScroll: 1,
|
||||||
draggable: !activeItem && !activeContainer,
|
draggable: !activeItem && !activeContainer,
|
||||||
swipe: !activeItem && !activeContainer,
|
swipe: !activeItem && !activeContainer,
|
||||||
arrows: false,
|
arrows: false,
|
||||||
|
swipeToSlide: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderContainers = () =>
|
const renderContainers = () =>
|
||||||
@ -145,7 +146,7 @@ const FunnelDnd = <
|
|||||||
renderBody()
|
renderBody()
|
||||||
) : (
|
) : (
|
||||||
<Group
|
<Group
|
||||||
gap="xs"
|
gap={0}
|
||||||
wrap="nowrap"
|
wrap="nowrap"
|
||||||
align="start">
|
align="start">
|
||||||
{renderBody()}
|
{renderBody()}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import React, {
|
|||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { Active, DndContext, DragEndEvent } from "@dnd-kit/core";
|
import { Active, DndContext, DragEndEvent } from "@dnd-kit/core";
|
||||||
|
import { restrictToHorizontalAxis } from "@dnd-kit/modifiers";
|
||||||
import { SortableContext } from "@dnd-kit/sortable";
|
import { SortableContext } from "@dnd-kit/sortable";
|
||||||
import { LexoRank } from "lexorank";
|
import { LexoRank } from "lexorank";
|
||||||
import { Box, Flex } from "@mantine/core";
|
import { Box, Flex } from "@mantine/core";
|
||||||
@ -100,6 +101,7 @@ const SortableDnd = <T extends BaseItem>({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DndContext
|
<DndContext
|
||||||
|
modifiers={[restrictToHorizontalAxis]}
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
onDragStart={({ active }) => setActive(active)}
|
onDragStart={({ active }) => setActive(active)}
|
||||||
onDragEnd={onDragEndLocal}
|
onDragEnd={onDragEndLocal}
|
||||||
|
|||||||
@ -9,7 +9,15 @@
|
|||||||
}
|
}
|
||||||
@media (min-width: 48em) {
|
@media (min-width: 48em) {
|
||||||
padding: rem(35);
|
padding: rem(35);
|
||||||
border-radius: rem(40);
|
border-radius: rem(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-padding-height {
|
||||||
|
height: 100% !important;
|
||||||
|
@media (min-width: 48em) {
|
||||||
|
padding: rem(40);
|
||||||
|
height: 89vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,3 +49,10 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.transparent {
|
||||||
|
@media (min-width: 48em) {
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,23 +1,27 @@
|
|||||||
import { CSSProperties, FC, ReactNode } from "react";
|
import { CSSProperties, FC, ReactNode } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import styles from "./PageBlock.module.css";
|
import styles from "./PageBlock.module.css";
|
||||||
5
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
|
className?: string;
|
||||||
fullHeight?: boolean;
|
fullHeight?: boolean;
|
||||||
fullHeightFixed?: boolean;
|
fullHeightFixed?: boolean;
|
||||||
noBorderRadius?: boolean;
|
noBorderRadius?: boolean;
|
||||||
fullScreenMobile?: boolean;
|
fullScreenMobile?: boolean;
|
||||||
|
transparent?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PageBlock: FC<Props> = ({
|
const PageBlock: FC<Props> = ({
|
||||||
children,
|
children,
|
||||||
style,
|
style,
|
||||||
|
className = "",
|
||||||
fullHeight = false,
|
fullHeight = false,
|
||||||
fullHeightFixed = false,
|
fullHeightFixed = false,
|
||||||
noBorderRadius = false,
|
noBorderRadius = false,
|
||||||
fullScreenMobile = false,
|
fullScreenMobile = false,
|
||||||
|
transparent = false,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -27,7 +31,9 @@ const PageBlock: FC<Props> = ({
|
|||||||
fullHeight && styles["container-full-height"],
|
fullHeight && styles["container-full-height"],
|
||||||
fullHeightFixed && styles["container-full-height-fixed"],
|
fullHeightFixed && styles["container-full-height-fixed"],
|
||||||
noBorderRadius && styles["container-no-border-radius"],
|
noBorderRadius && styles["container-no-border-radius"],
|
||||||
fullScreenMobile && styles["container-full-screen-mobile"]
|
fullScreenMobile && styles["container-full-screen-mobile"],
|
||||||
|
transparent && styles.transparent,
|
||||||
|
styles[className]
|
||||||
)}>
|
)}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: rem(10);
|
min-height: 100vh;
|
||||||
min-height: 86vh;
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
|
@media (min-width: 48em) {
|
||||||
|
gap: rem(10);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
.container {
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 18px;
|
||||||
|
|
||||||
|
@mixin dark {
|
||||||
|
background-color: #212121;
|
||||||
|
box-shadow: 1px 1px 10px 1px var(--mantine-color-dark-6);
|
||||||
|
}
|
||||||
|
@mixin light {
|
||||||
|
background-color: #fcfdff;
|
||||||
|
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-active {
|
||||||
|
@mixin dark {
|
||||||
|
background-color: var(--mantine-color-dark-8);
|
||||||
|
box-shadow: 5px 5px 20px 1px var(--mantine-color-dark-6);
|
||||||
|
}
|
||||||
|
@mixin light {
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 5px 5px 20px rgba(0, 0, 0, 0.16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container:hover {
|
||||||
|
@mixin dark {
|
||||||
|
background-color: var(--mantine-color-dark-8);
|
||||||
|
box-shadow: 5px 5px 20px 1px var(--mantine-color-dark-6);
|
||||||
|
}
|
||||||
|
@mixin light {
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 5px 5px 20px rgba(0, 0, 0, 0.16);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/components/layout/SmallPageBlock/SmallPageBlock.tsx
Normal file
23
src/components/layout/SmallPageBlock/SmallPageBlock.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { CSSProperties, FC, ReactNode } from "react";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import styles from "@/components/layout/SmallPageBlock/SmallPageBlock.module.css";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
style?: CSSProperties;
|
||||||
|
active?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SmallPageBlock: FC<Props> = ({ children, style, active = false }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
styles.container,
|
||||||
|
active && styles["container-active"]
|
||||||
|
)}
|
||||||
|
style={style}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default SmallPageBlock;
|
||||||
@ -1,8 +1,7 @@
|
|||||||
import { em } from "@mantine/core";
|
|
||||||
import { useMediaQuery } from "@mantine/hooks";
|
import { useMediaQuery } from "@mantine/hooks";
|
||||||
|
|
||||||
const useIsMobile = () => {
|
const useIsMobile = () => {
|
||||||
return useMediaQuery(`(max-width: ${em(750)})`);
|
return useMediaQuery("(max-width: 48em)");
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useIsMobile;
|
export default useIsMobile;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { createTheme, MantineColorsTuple } from "@mantine/core";
|
import { createTheme, MantineColorsTuple } from "@mantine/core";
|
||||||
|
|
||||||
export const myColor: MantineColorsTuple = [
|
export const myColor: MantineColorsTuple = [
|
||||||
"#e2faff",
|
"#e0f0f4",
|
||||||
"#d4eff8",
|
"#d4eff8",
|
||||||
"#afdce9",
|
"#afdce9",
|
||||||
"#87c8db",
|
"#87c8db",
|
||||||
@ -39,5 +39,10 @@ export const theme = createTheme({
|
|||||||
radius,
|
radius,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Card: {
|
||||||
|
defaultProps: {
|
||||||
|
radius,
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
14
yarn.lock
14
yarn.lock
@ -1558,6 +1558,19 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@dnd-kit/modifiers@npm:^9.0.0":
|
||||||
|
version: 9.0.0
|
||||||
|
resolution: "@dnd-kit/modifiers@npm:9.0.0"
|
||||||
|
dependencies:
|
||||||
|
"@dnd-kit/utilities": "npm:^3.2.2"
|
||||||
|
tslib: "npm:^2.0.0"
|
||||||
|
peerDependencies:
|
||||||
|
"@dnd-kit/core": ^6.3.0
|
||||||
|
react: ">=16.8.0"
|
||||||
|
checksum: 10c0/ca8cc9da8296df10774d779c1611074dc327ccc3c49041c102111c98c7f2b2b73b6af5209c0eef6b2fe978ac63dc2a985efa87c85a8d786577304bd2e64cee1d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@dnd-kit/sortable@npm:^10.0.0":
|
"@dnd-kit/sortable@npm:^10.0.0":
|
||||||
version: 10.0.0
|
version: 10.0.0
|
||||||
resolution: "@dnd-kit/sortable@npm:10.0.0"
|
resolution: "@dnd-kit/sortable@npm:10.0.0"
|
||||||
@ -6087,6 +6100,7 @@ __metadata:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/core": "npm:^7.27.4"
|
"@babel/core": "npm:^7.27.4"
|
||||||
"@dnd-kit/core": "npm:^6.3.1"
|
"@dnd-kit/core": "npm:^6.3.1"
|
||||||
|
"@dnd-kit/modifiers": "npm:^9.0.0"
|
||||||
"@dnd-kit/sortable": "npm:^10.0.0"
|
"@dnd-kit/sortable": "npm:^10.0.0"
|
||||||
"@eslint/js": "npm:^9.29.0"
|
"@eslint/js": "npm:^9.29.0"
|
||||||
"@hey-api/client-axios": "npm:^0.9.1"
|
"@hey-api/client-axios": "npm:^0.9.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user