feat: swiper for boards on desktop
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Group, ScrollArea } from "@mantine/core";
|
import { Group } from "@mantine/core";
|
||||||
import Board from "@/app/deals/components/desktop/Board/Board";
|
import Board from "@/app/deals/components/desktop/Board/Board";
|
||||||
import CreateBoardButton from "@/app/deals/components/desktop/CreateBoardButton/CreateBoardButton";
|
import CreateBoardButton from "@/app/deals/components/desktop/CreateBoardButton/CreateBoardButton";
|
||||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||||
@ -24,31 +24,28 @@ const Boards = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea
|
<Group
|
||||||
offsetScrollbars={"x"}
|
wrap={"nowrap"}
|
||||||
scrollbars={"x"}
|
gap={"md"}
|
||||||
scrollbarSize={0}
|
pr={"sm"}
|
||||||
w={"100%"}>
|
style={{ maxWidth: "100%", overflow: "hidden" }}>
|
||||||
<Group
|
<SortableDnd
|
||||||
pr={"xs"}
|
initialItems={boards}
|
||||||
wrap={"nowrap"}
|
renderItem={renderBoard}
|
||||||
gap={0}>
|
onDragEnd={onDragEnd}
|
||||||
<SortableDnd
|
onItemClick={selectBoard}
|
||||||
initialItems={boards}
|
containerStyle={{
|
||||||
renderItem={renderBoard}
|
flexWrap: "nowrap",
|
||||||
onDragEnd={onDragEnd}
|
gap: "var(--mantine-spacing-md)",
|
||||||
onItemClick={selectBoard}
|
paddingBlock: "var(--mantine-spacing-md)",
|
||||||
containerStyle={{
|
paddingLeft: "var(--mantine-spacing-md)",
|
||||||
flexWrap: "nowrap",
|
}}
|
||||||
gap: "var(--mantine-spacing-md)",
|
dragHandleStyle={{ cursor: "pointer" }}
|
||||||
margin: "var(--mantine-spacing-md)",
|
disabled={isMobile}
|
||||||
}}
|
swiperEnabled
|
||||||
dragHandleStyle={{ cursor: "pointer" }}
|
/>
|
||||||
disabled={isMobile}
|
<CreateBoardButton />
|
||||||
/>
|
</Group>
|
||||||
<CreateBoardButton />
|
|
||||||
</Group>
|
|
||||||
</ScrollArea>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -28,12 +28,14 @@ const Header = () => {
|
|||||||
wrap={"nowrap"}
|
wrap={"nowrap"}
|
||||||
pr={"md"}>
|
pr={"md"}>
|
||||||
<Boards />
|
<Boards />
|
||||||
<ColorSchemeToggle />
|
<Group wrap={"nowrap"}>
|
||||||
<ProjectSelect
|
<ColorSchemeToggle />
|
||||||
data={projects}
|
<ProjectSelect
|
||||||
value={selectedProject}
|
data={projects}
|
||||||
onChange={value => value && setSelectedProject(value)}
|
value={selectedProject}
|
||||||
/>
|
onChange={value => value && setSelectedProject(value)}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiperContainer :global(.swiper-scrollbar-drag) {
|
.swiper-container :global(.swiper-scrollbar-drag) {
|
||||||
@mixin dark {
|
@mixin dark {
|
||||||
background-color: var(--mantine-color-dark-9);
|
background-color: var(--mantine-color-dark-9);
|
||||||
}
|
}
|
||||||
@ -22,6 +22,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiperContainer :global(.swiper-slide) {
|
.swiper-container :global(.swiper-slide) {
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,7 +101,7 @@ const FunnelDnd = <
|
|||||||
return (
|
return (
|
||||||
<Swiper
|
<Swiper
|
||||||
ref={swiperRef}
|
ref={swiperRef}
|
||||||
className={classes.swiperContainer}
|
className={classes["swiper-container"]}
|
||||||
slidesPerView={1.2}
|
slidesPerView={1.2}
|
||||||
modules={[Pagination]}
|
modules={[Pagination]}
|
||||||
freeMode={{ enabled: false }}
|
freeMode={{ enabled: false }}
|
||||||
@ -118,7 +118,7 @@ const FunnelDnd = <
|
|||||||
return (
|
return (
|
||||||
<Swiper
|
<Swiper
|
||||||
ref={swiperRef}
|
ref={swiperRef}
|
||||||
className={classes.swiperContainer}
|
className={classes["swiper-container"]}
|
||||||
modules={[Scrollbar, Mousewheel, FreeMode]}
|
modules={[Scrollbar, Mousewheel, FreeMode]}
|
||||||
spaceBetween={15}
|
spaceBetween={15}
|
||||||
slidesPerView={"auto"}
|
slidesPerView={"auto"}
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import { Active, DndContext, DragEndEvent } from "@dnd-kit/core";
|
|||||||
import { restrictToHorizontalAxis } from "@dnd-kit/modifiers";
|
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 { FreeMode, Mousewheel, Scrollbar } from "swiper/modules";
|
||||||
|
import { Swiper, SwiperSlide } from "swiper/react";
|
||||||
import { Box, Flex } from "@mantine/core";
|
import { Box, Flex } from "@mantine/core";
|
||||||
import useDndSensors from "@/app/deals/hooks/useSensors";
|
import useDndSensors from "@/app/deals/hooks/useSensors";
|
||||||
import { SortableOverlay } from "@/components/dnd/SortableDnd/SortableOverlay";
|
import { SortableOverlay } from "@/components/dnd/SortableDnd/SortableOverlay";
|
||||||
@ -35,6 +37,7 @@ type Props<T extends BaseItem> = {
|
|||||||
containerStyle?: CSSProperties;
|
containerStyle?: CSSProperties;
|
||||||
vertical?: boolean;
|
vertical?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
swiperEnabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SortableDnd = <T extends BaseItem>({
|
const SortableDnd = <T extends BaseItem>({
|
||||||
@ -45,8 +48,9 @@ const SortableDnd = <T extends BaseItem>({
|
|||||||
onDragEnd,
|
onDragEnd,
|
||||||
onItemClick,
|
onItemClick,
|
||||||
containerStyle,
|
containerStyle,
|
||||||
vertical,
|
vertical = false,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
swiperEnabled = false,
|
||||||
}: Props<T>) => {
|
}: Props<T>) => {
|
||||||
const [active, setActive] = useState<Active | null>(null);
|
const [active, setActive] = useState<Active | null>(null);
|
||||||
const [items, setItems] = useState<T[]>([]);
|
const [items, setItems] = useState<T[]>([]);
|
||||||
@ -98,6 +102,81 @@ const SortableDnd = <T extends BaseItem>({
|
|||||||
setActive(null);
|
setActive(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderWithSwiper = () => (
|
||||||
|
<Swiper
|
||||||
|
modules={[Scrollbar, Mousewheel, FreeMode]}
|
||||||
|
spaceBetween={15}
|
||||||
|
slidesPerView={"auto"}
|
||||||
|
scrollbar={{ hide: false }}
|
||||||
|
mousewheel={{
|
||||||
|
enabled: true,
|
||||||
|
sensitivity: 0.2,
|
||||||
|
}}
|
||||||
|
style={containerStyle}
|
||||||
|
direction={vertical ? "vertical" : "horizontal"}
|
||||||
|
freeMode={{ enabled: true }}
|
||||||
|
grabCursor>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<SwiperSlide
|
||||||
|
style={{ width: "fit-content" }}
|
||||||
|
key={index}
|
||||||
|
onClick={e => {
|
||||||
|
if (!onItemClick) return;
|
||||||
|
e.stopPropagation();
|
||||||
|
onItemClick(item);
|
||||||
|
}}>
|
||||||
|
<SortableItem
|
||||||
|
id={item.id}
|
||||||
|
disabled={disabled}
|
||||||
|
renderItem={renderDraggable =>
|
||||||
|
renderItem(item, renderDraggable)
|
||||||
|
}
|
||||||
|
renderDraggable={
|
||||||
|
renderDraggable
|
||||||
|
? () => renderDraggable(item)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
dragHandleStyle={dragHandleStyle}
|
||||||
|
/>
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderWithFlex = () => (
|
||||||
|
<Flex
|
||||||
|
style={{
|
||||||
|
gap: 0,
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
flexDirection: vertical ? "column" : "row",
|
||||||
|
...containerStyle,
|
||||||
|
}}>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<Box
|
||||||
|
key={index}
|
||||||
|
onClick={e => {
|
||||||
|
if (!onItemClick) return;
|
||||||
|
e.stopPropagation();
|
||||||
|
onItemClick(item);
|
||||||
|
}}>
|
||||||
|
<SortableItem
|
||||||
|
id={item.id}
|
||||||
|
disabled={disabled}
|
||||||
|
renderItem={renderDraggable =>
|
||||||
|
renderItem(item, renderDraggable)
|
||||||
|
}
|
||||||
|
renderDraggable={
|
||||||
|
renderDraggable
|
||||||
|
? () => renderDraggable(item)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
dragHandleStyle={dragHandleStyle}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndContext
|
<DndContext
|
||||||
modifiers={[restrictToHorizontalAxis]}
|
modifiers={[restrictToHorizontalAxis]}
|
||||||
@ -108,37 +187,7 @@ const SortableDnd = <T extends BaseItem>({
|
|||||||
<SortableContext
|
<SortableContext
|
||||||
items={items}
|
items={items}
|
||||||
disabled={disabled}>
|
disabled={disabled}>
|
||||||
<Flex
|
{swiperEnabled ? renderWithSwiper() : renderWithFlex()}
|
||||||
style={{
|
|
||||||
gap: 0,
|
|
||||||
flexWrap: "nowrap",
|
|
||||||
flexDirection: vertical ? "column" : "row",
|
|
||||||
...containerStyle,
|
|
||||||
}}>
|
|
||||||
{items.map((item, index) => (
|
|
||||||
<Box
|
|
||||||
key={index}
|
|
||||||
onClick={e => {
|
|
||||||
if (!onItemClick) return;
|
|
||||||
e.stopPropagation();
|
|
||||||
onItemClick(item);
|
|
||||||
}}>
|
|
||||||
<SortableItem
|
|
||||||
id={item.id}
|
|
||||||
disabled={disabled}
|
|
||||||
renderItem={renderDraggable =>
|
|
||||||
renderItem(item, renderDraggable)
|
|
||||||
}
|
|
||||||
renderDraggable={
|
|
||||||
renderDraggable
|
|
||||||
? () => renderDraggable(item)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
dragHandleStyle={dragHandleStyle}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
<SortableOverlay>
|
<SortableOverlay>
|
||||||
{activeItem ? renderItem(activeItem, renderDraggable) : null}
|
{activeItem ? renderItem(activeItem, renderDraggable) : null}
|
||||||
|
|||||||
Reference in New Issue
Block a user