diff --git a/package.json b/package.json index c7bdac0..90fc1b0 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/sortable": "^10.0.0", "@mantine/core": "8.1.2", "@mantine/form": "^8.1.3", diff --git a/src/app/deals/components/Board/Board.module.css b/src/app/deals/components/Board/Board.module.css new file mode 100644 index 0000000..9b74015 --- /dev/null +++ b/src/app/deals/components/Board/Board.module.css @@ -0,0 +1,7 @@ + +.board { + min-width: 130px; + flex-wrap: nowrap; + gap: 3px; +} + diff --git a/src/app/deals/components/Board/Board.tsx b/src/app/deals/components/Board/Board.tsx index 14e7e78..29b5ad3 100644 --- a/src/app/deals/components/Board/Board.tsx +++ b/src/app/deals/components/Board/Board.tsx @@ -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 = ({ board, containerStyle }) => { +const Board: FC = ({ board }) => { + const { selectedBoard } = useBoardsContext(); const [isHovered, setIsHovered] = useState(false); const { onUpdateBoard } = useBoardsContext(); return ( - setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)}> - onUpdateBoard(board.id, { name: value })} - inputStyles={{ - input: { - height: 25, - minHeight: 25, - }, - }} - getChildren={startEditing => ( - <> - - {board.name} - - - - )} - modalTitle={"Редактирование доски"} - /> - + + setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)}> + + onUpdateBoard(board.id, { name: value }) + } + inputStyles={{ + input: { + height: 25, + minHeight: 25, + }, + }} + getChildren={startEditing => ( + <> + + {board.name} + + + + )} + modalTitle={"Редактирование доски"} + /> + + ); }; diff --git a/src/app/deals/components/Board/BoardMenu.tsx b/src/app/deals/components/Board/BoardMenu.tsx index c04ac42..aeb8d2f 100644 --- a/src/app/deals/components/Board/BoardMenu.tsx +++ b/src/app/deals/components/Board/BoardMenu.tsx @@ -27,9 +27,8 @@ const BoardMenu: FC = ({ = ({ board }) => { + const { selectedBoard } = useBoardsContext(); + const { onUpdateBoard } = useBoardsContext(); + + return ( + + onUpdateBoard(board.id, { name: value })} + inputStyles={{ + input: { + height: 25, + minHeight: 25, + }, + }} + getChildren={startEditing => ( + <> + + {board.name} + + + + )} + modalTitle={"Редактирование доски"} + /> + + ); +}; + +export default BoardMobile; diff --git a/src/app/deals/components/Boards/Boards.tsx b/src/app/deals/components/Boards/Boards.tsx index 0721357..11abfbc 100644 --- a/src/app/deals/components/Boards/Boards.tsx +++ b/src/app/deals/components/Boards/Boards.tsx @@ -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 ; - }; + const renderBoard = (board: BoardSchema) => ; + + const renderBoardMobile = (board: BoardSchema) => ( + + ); const onDragEnd = (itemId: number, newLexorank: string) => { onUpdateBoard(itemId, { lexorank: newLexorank }); @@ -31,13 +34,16 @@ const Boards = () => { scrollbars={"x"} scrollbarSize={0} w={"100%"}> - + diff --git a/src/app/deals/components/CreateBoardButton/CreateBoardButton.tsx b/src/app/deals/components/CreateBoardButton/CreateBoardButton.tsx index 8de7717..e2ee066 100644 --- a/src/app/deals/components/CreateBoardButton/CreateBoardButton.tsx +++ b/src/app/deals/components/CreateBoardButton/CreateBoardButton.tsx @@ -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 ( - + ( - + )} modalTitle={"Создание доски"} + inputStyles={{ + wrapper: { + marginLeft: 15, + marginRight: 15, + }, + }} /> - + ); }; diff --git a/src/app/deals/components/CreateStatusButton/CreateStatusButton.tsx b/src/app/deals/components/CreateStatusButton/CreateStatusButton.tsx index b38e544..8fbf96b 100644 --- a/src/app/deals/components/CreateStatusButton/CreateStatusButton.tsx +++ b/src/app/deals/components/CreateStatusButton/CreateStatusButton.tsx @@ -17,7 +17,7 @@ const CreateStatusButton = () => { cursor: "pointer", }}> ( { - return {deal.name}; + return {deal.name}; }; export default DealCard; diff --git a/src/app/deals/components/Header/Header.tsx b/src/app/deals/components/Header/Header.tsx index 7253f5d..1b7942c 100644 --- a/src/app/deals/components/Header/Header.tsx +++ b/src/app/deals/components/Header/Header.tsx @@ -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 ( - <> + + value && setSelectedProject(value)} /> - + ); }; const getMobileHeader = () => { return ( - - - - - {selectedProject?.name} - setIsEditorDrawerOpened(true)}> - - - + + + + + + {selectedProject?.name} + setIsEditorDrawerOpened(true)}> + + + + + ); }; diff --git a/src/app/deals/components/StatusColumnWrapper/StatusColumnWrapper.module.css b/src/app/deals/components/StatusColumnWrapper/StatusColumnWrapper.module.css new file mode 100644 index 0000000..4b88a97 --- /dev/null +++ b/src/app/deals/components/StatusColumnWrapper/StatusColumnWrapper.module.css @@ -0,0 +1,9 @@ + +.container { + min-width: 150px; + width: 15vw; + + @media (max-width: 48em) { + width: auto; + } +} diff --git a/src/app/deals/components/StatusColumnWrapper/StatusColumnWrapper.tsx b/src/app/deals/components/StatusColumnWrapper/StatusColumnWrapper.tsx index 6a1f8aa..b80cfda 100644 --- a/src/app/deals/components/StatusColumnWrapper/StatusColumnWrapper.tsx +++ b/src/app/deals/components/StatusColumnWrapper/StatusColumnWrapper.tsx @@ -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 ( + mx={7} + className={styles.container}> + className={"flex-nowrap border-b-3 border-blue-500 mb-3 p-3"}> handleSave(value)} diff --git a/src/app/deals/components/StatusColumnWrapper/StatusMenu.tsx b/src/app/deals/components/StatusColumnWrapper/StatusMenu.tsx index 9f7eeee..6ebaa9a 100644 --- a/src/app/deals/components/StatusColumnWrapper/StatusMenu.tsx +++ b/src/app/deals/components/StatusColumnWrapper/StatusMenu.tsx @@ -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 = ({ status, handleEdit }) => { + const isMobile = useIsMobile(); const { onDeleteStatus, setIsEditorDrawerOpened } = useStatusesContext(); return ( @@ -50,16 +52,18 @@ const StatusMenu: FC = ({ status, handleEdit }) => { Удалить - { - e.stopPropagation(); - setIsEditorDrawerOpened(true); - }}> - - - Изменить порядок - - + {isMobile && ( + { + e.stopPropagation(); + setIsEditorDrawerOpened(true); + }}> + + + Изменить порядок + + + )} ); diff --git a/src/app/deals/hooks/useSensors.ts b/src/app/deals/hooks/useSensors.ts index 48ce3f4..3d5aaf8 100644 --- a/src/app/deals/hooks/useSensors.ts +++ b/src/app/deals/hooks/useSensors.ts @@ -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( diff --git a/src/app/deals/page.tsx b/src/app/deals/page.tsx index ed4372b..11cc221 100644 --- a/src/app/deals/page.tsx +++ b/src/app/deals/page.tsx @@ -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 ( - - - - + + + +
- - + + @@ -27,9 +27,9 @@ export default function DealsPage() { - - - - + + + + ); } diff --git a/src/app/global.css b/src/app/global.css index 622e476..6618edb 100644 --- a/src/app/global.css +++ b/src/app/global.css @@ -2,6 +2,6 @@ body { @mixin light { - background-color: whitesmoke; + background-color: #f4f7fd; } } diff --git a/src/components/dnd/FunnelDnd/FunnelDnd.tsx b/src/components/dnd/FunnelDnd/FunnelDnd.tsx index 272a6bd..2e597bf 100644 --- a/src/components/dnd/FunnelDnd/FunnelDnd.tsx +++ b/src/components/dnd/FunnelDnd/FunnelDnd.tsx @@ -68,11 +68,12 @@ const FunnelDnd = < dots: true, infinite: false, speed: 500, - slidesToShow: 1, + slidesToShow: 1.1, slidesToScroll: 1, draggable: !activeItem && !activeContainer, swipe: !activeItem && !activeContainer, arrows: false, + swipeToSlide: true, }; const renderContainers = () => @@ -145,7 +146,7 @@ const FunnelDnd = < renderBody() ) : ( {renderBody()} diff --git a/src/components/dnd/SortableDnd/SortableDnd.tsx b/src/components/dnd/SortableDnd/SortableDnd.tsx index e9b7319..dd4ec55 100644 --- a/src/components/dnd/SortableDnd/SortableDnd.tsx +++ b/src/components/dnd/SortableDnd/SortableDnd.tsx @@ -8,6 +8,7 @@ import React, { useState, } from "react"; import { Active, DndContext, DragEndEvent } from "@dnd-kit/core"; +import { restrictToHorizontalAxis } from "@dnd-kit/modifiers"; import { SortableContext } from "@dnd-kit/sortable"; import { LexoRank } from "lexorank"; import { Box, Flex } from "@mantine/core"; @@ -100,6 +101,7 @@ const SortableDnd = ({ return ( setActive(active)} onDragEnd={onDragEndLocal} diff --git a/src/components/layout/PageBlock/PageBlock.module.css b/src/components/layout/PageBlock/PageBlock.module.css index 7a0bdf2..86d7c9f 100644 --- a/src/components/layout/PageBlock/PageBlock.module.css +++ b/src/components/layout/PageBlock/PageBlock.module.css @@ -9,7 +9,15 @@ } @media (min-width: 48em) { 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; } } + +.transparent { + @media (min-width: 48em) { + box-shadow: none !important; + background-color: transparent !important; + } +} diff --git a/src/components/layout/PageBlock/PageBlock.tsx b/src/components/layout/PageBlock/PageBlock.tsx index 18f0e15..8e2095b 100644 --- a/src/components/layout/PageBlock/PageBlock.tsx +++ b/src/components/layout/PageBlock/PageBlock.tsx @@ -1,23 +1,27 @@ import { CSSProperties, FC, ReactNode } from "react"; import classNames from "classnames"; import styles from "./PageBlock.module.css"; -5 + type Props = { children: ReactNode; style?: CSSProperties; + className?: string; fullHeight?: boolean; fullHeightFixed?: boolean; noBorderRadius?: boolean; fullScreenMobile?: boolean; + transparent?: boolean; }; const PageBlock: FC = ({ children, style, + className = "", fullHeight = false, fullHeightFixed = false, noBorderRadius = false, fullScreenMobile = false, + transparent = false, }) => { return (
= ({ fullHeight && styles["container-full-height"], fullHeightFixed && styles["container-full-height-fixed"], noBorderRadius && styles["container-no-border-radius"], - fullScreenMobile && styles["container-full-screen-mobile"] + fullScreenMobile && styles["container-full-screen-mobile"], + transparent && styles.transparent, + styles[className] )}> {children}
diff --git a/src/components/layout/PageContainer/PageContainer.module.css b/src/components/layout/PageContainer/PageContainer.module.css index a2cca97..0c926b6 100644 --- a/src/components/layout/PageContainer/PageContainer.module.css +++ b/src/components/layout/PageContainer/PageContainer.module.css @@ -1,7 +1,10 @@ .container { display: flex; flex-direction: column; - gap: rem(10); - min-height: 86vh; + min-height: 100vh; background-color: transparent; + + @media (min-width: 48em) { + gap: rem(10); + } } diff --git a/src/components/layout/SmallPageBlock/SmallPageBlock.module.css b/src/components/layout/SmallPageBlock/SmallPageBlock.module.css new file mode 100644 index 0000000..c978510 --- /dev/null +++ b/src/components/layout/SmallPageBlock/SmallPageBlock.module.css @@ -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); + } +} diff --git a/src/components/layout/SmallPageBlock/SmallPageBlock.tsx b/src/components/layout/SmallPageBlock/SmallPageBlock.tsx new file mode 100644 index 0000000..c7d4c4c --- /dev/null +++ b/src/components/layout/SmallPageBlock/SmallPageBlock.tsx @@ -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 = ({ children, style, active = false }) => { + return ( +
+ {children} +
+ ); +}; +export default SmallPageBlock; diff --git a/src/hooks/useIsMobile.ts b/src/hooks/useIsMobile.ts index 68fa0b5..4b38be0 100644 --- a/src/hooks/useIsMobile.ts +++ b/src/hooks/useIsMobile.ts @@ -1,8 +1,7 @@ -import { em } from "@mantine/core"; import { useMediaQuery } from "@mantine/hooks"; const useIsMobile = () => { - return useMediaQuery(`(max-width: ${em(750)})`); + return useMediaQuery("(max-width: 48em)"); }; export default useIsMobile; diff --git a/src/theme.ts b/src/theme.ts index 0abd2de..7e9fd50 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -1,7 +1,7 @@ import { createTheme, MantineColorsTuple } from "@mantine/core"; export const myColor: MantineColorsTuple = [ - "#e2faff", + "#e0f0f4", "#d4eff8", "#afdce9", "#87c8db", @@ -39,5 +39,10 @@ export const theme = createTheme({ radius, }, }, + Card: { + defaultProps: { + radius, + } + } }, }); diff --git a/yarn.lock b/yarn.lock index 5771c8f..eb401ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1558,6 +1558,19 @@ __metadata: languageName: node 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": version: 10.0.0 resolution: "@dnd-kit/sortable@npm:10.0.0" @@ -6087,6 +6100,7 @@ __metadata: dependencies: "@babel/core": "npm:^7.27.4" "@dnd-kit/core": "npm:^6.3.1" + "@dnd-kit/modifiers": "npm:^9.0.0" "@dnd-kit/sortable": "npm:^10.0.0" "@eslint/js": "npm:^9.29.0" "@hey-api/client-axios": "npm:^0.9.1"