Skip to content

Commit

Permalink
add virtualized list in columns
Browse files Browse the repository at this point in the history
  • Loading branch information
Hartaithan committed Aug 3, 2023
1 parent c9761ab commit 97a2adc
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 21 deletions.
8 changes: 7 additions & 1 deletion components/BoardCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import BoardCardOverlay from "./BoardCardOverlay";
interface IBoardCardProps {
item: IGame;
interactive?: boolean;
divider?: boolean;
}

const animateLayoutChanges: AnimateLayoutChanges = (args) => {
Expand All @@ -27,13 +28,17 @@ const animateLayoutChanges: AnimateLayoutChanges = (args) => {
};

const useStyles = createStyles(
({ colors, radius, spacing }, { interactive = true }: IBoardCardProps) => ({
(
{ colors, radius, spacing },
{ interactive = true, divider = false }: IBoardCardProps
) => ({
container: {
width: "100%",
padding: spacing.xs,
background: colors.primary[6],
borderRadius: radius.md,
cursor: interactive ? "pointer" : "default",
marginBottom: divider ? spacing.sm : 0,
},
imageWrapper: {
position: "relative",
Expand Down Expand Up @@ -99,6 +104,7 @@ const BoardCard: FC<IBoardCardProps> = (props) => {
style={{
transform: CSS.Transform.toString(transform),
transition,
zIndex: isDragging ? 99999 : undefined,
}}
>
<Flex className={classes.header}>
Expand Down
38 changes: 30 additions & 8 deletions components/BoardColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
Text,
Flex,
createStyles,
useMantineTheme,
UnstyledButton,
Button,
Transition,
Expand All @@ -19,6 +18,7 @@ import { type IGame } from "@/models/GameModel";
import { IconArticleOff, IconPlaylistAdd } from "@tabler/icons-react";
import { columnColors, columnsLabels } from "@/constants/board";
import { useBoard } from "@/providers/BoardProvider";
import { Virtuoso } from "react-virtuoso";

interface IBoardColumnProps {
column: BOARD_COLUMNS;
Expand Down Expand Up @@ -98,8 +98,19 @@ const useStyles = createStyles(
stroke: colors[color][shade],
},
},
list: {
listWrapper: {
position: "relative",
flex: 1,
"& > div": {
msOverflowStyle: "none",
scrollbarWidth: "none",
"&::-webkit-scrollbar": {
display: "none",
},
},
},
list: {
height: "100%",
},
empty: {
position: "absolute",
Expand Down Expand Up @@ -133,7 +144,6 @@ const useStyles = createStyles(
const BoardColumn: FC<IBoardColumnProps> = (props) => {
const { column, items, interactive = true } = props;
const { classes } = useStyles({ column });
const { spacing } = useMantineTheme();
const { setNodeRef } = useDroppable({ id: column, disabled: !interactive });
const { addGameModal } = useBoard();

Expand Down Expand Up @@ -163,9 +173,8 @@ const BoardColumn: FC<IBoardColumnProps> = (props) => {
)}
</Flex>
<Flex
className={classes.list}
className={classes.listWrapper}
direction="column"
gap={spacing.sm}
ref={setNodeRef}
>
<Transition
Expand Down Expand Up @@ -195,9 +204,22 @@ const BoardColumn: FC<IBoardColumnProps> = (props) => {
</Flex>
)}
</Transition>
{items.map((item) => (
<BoardCard key={item.id} item={item} interactive={interactive} />
))}
<Virtuoso
data={items}
className={classes.list}
totalCount={items.length}
itemContent={(index, item) => {
const isLast = index + 1 === items.length;
return (
<BoardCard
key={item.id}
item={item}
divider={!isLast}
interactive={interactive}
/>
);
}}
/>
</Flex>
</Flex>
</SortableContext>
Expand Down
40 changes: 31 additions & 9 deletions components/BoardContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
useSensors,
MeasuringStrategy,
type PointerActivationConstraint,
type DragStartEvent,
DragOverlay,
} from "@dnd-kit/core";
import {
PointerSensor,
Expand All @@ -22,6 +24,7 @@ import { type IReorderItem, type IReorderPayload } from "@/models/GameModel";
import API from "@/helpers/api";
import { notifications } from "@mantine/notifications";
import { IconAlertOctagon, IconCheck } from "@tabler/icons-react";
import BoardCard from "./BoardCard";

interface IMove {
start: string | null;
Expand All @@ -44,10 +47,11 @@ const useStyles = createStyles(() => ({

const BoardContainer: FC = () => {
const { classes } = useStyles();
const { columns, setColumns } = useBoard();
const { columns, setColumns, active, setActive } = useBoard();
const move = useRef<IMove>({ start: null, end: null });
const columnsRef = useRef<IBoardColumns>(columns);
const previousRef = useRef<IBoardColumns>(columns);
const lastActiveContainer = useRef<BOARD_COLUMNS | null>(null);

const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint })
Expand Down Expand Up @@ -118,17 +122,30 @@ const BoardContainer: FC = () => {
move.current = { start: null, end: null };
};

const handleDragStart = ({ active }: DragStartEvent): void => {
if (active.data.current === undefined) return;

const activeContainer: BOARD_COLUMNS =
active.data.current.sortable.containerId;
lastActiveContainer.current = activeContainer;

const item = columns[activeContainer].find((item) => item.id === active.id);
if (item === undefined) return;

setActive(item);
};

const handleDragOver = ({ over, active }: DragOverEvent): void => {
if (over == null || active.data.current === undefined || over === null) {
return;
}

const activeContainer: BOARD_COLUMNS =
active.data.current.sortable.containerId;
active.data.current?.sortable?.containerId ?? lastActiveContainer.current;
const overContainer: BOARD_COLUMNS =
over.data.current?.sortable.containerId ?? over.id;
const activeIndex: number = active.data.current.sortable.index;
const overIndex: number = over.data.current?.sortable.index ?? 0;
over.data.current?.sortable?.containerId ?? lastActiveContainer.current;
const activeIndex: number = active.data.current?.sortable?.index ?? 0;
const overIndex: number = over.data.current?.sortable?.index ?? 0;

if (move.current.start === null) {
move.current.start = activeContainer;
Expand All @@ -152,16 +169,19 @@ const BoardContainer: FC = () => {
};

const handleDragEnd = ({ active, over }: DragEndEvent): void => {
setActive(null);
lastActiveContainer.current = null;

if (over == null || active.data.current === undefined || over === null) {
return;
}

const activeContainer: BOARD_COLUMNS =
active.data.current.sortable.containerId;
active.data.current?.sortable?.containerId ?? lastActiveContainer.current;
const overContainer: BOARD_COLUMNS =
over.data.current?.sortable.containerId ?? over.id;
const activeIndex: number = active.data.current.sortable.index;
const overIndex: number = over.data.current?.sortable.index ?? 0;
over.data.current?.sortable?.containerId ?? lastActiveContainer.current;
const activeIndex: number = active.data.current?.sortable?.index ?? 0;
const overIndex: number = over.data.current?.sortable?.index ?? 0;

if (active.id !== over.id) {
setColumns((items) => {
Expand Down Expand Up @@ -201,6 +221,7 @@ const BoardContainer: FC = () => {
<DndContext
sensors={sensors}
measuring={measuring}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDragOver={handleDragOver}
collisionDetection={closestCorners}
Expand All @@ -212,6 +233,7 @@ const BoardContainer: FC = () => {
return <BoardColumn key={col} column={key} items={items} />;
})}
</Flex>
<DragOverlay>{active != null && <BoardCard item={active} />}</DragOverlay>
</DndContext>
);
};
Expand Down
15 changes: 14 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"psn-api": "^2.9.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-rewards": "^2.0.4"
"react-rewards": "^2.0.4",
"react-virtuoso": "^4.4.2"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.55.0",
Expand Down
9 changes: 8 additions & 1 deletion providers/BoardProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import AddGameModal from "@/modals/AddGameModal";
import { type IBoardColumns, type BOARD_COLUMNS } from "@/models/BoardModel";
import { type IAddGameState } from "@/models/GameModel";
import { type IGame, type IAddGameState } from "@/models/GameModel";
import {
type PropsWithChildren,
type FC,
Expand All @@ -23,6 +23,8 @@ interface IAddGameModal extends IAddGameState {
interface IBoardContext {
columns: IBoardColumns;
setColumns: Dispatch<SetStateAction<IBoardColumns>>;
active: IGame | null;
setActive: Dispatch<SetStateAction<IGame | null>>;
addGameModal: IAddGameModal;
}

Expand All @@ -34,6 +36,8 @@ const initialState: IAddGameState = {
const initialContextValue: IBoardContext = {
columns: {},
setColumns: () => null,
active: null,
setActive: () => null,
addGameModal: {
...initialState,
open: () => null,
Expand All @@ -48,6 +52,7 @@ const BoardProvider: FC<IBoardProviderProps> = (props) => {

const [columns, setColumns] = useState<IBoardColumns>(initializedBoard);
const [addGameModal, setAddGameModal] = useState<IAddGameState>(initialState);
const [active, setActive] = useState<IGame | null>(null);

const handleOpen: IAddGameModal["open"] = (status) => {
setAddGameModal((prev) => ({ ...prev, status, opened: true }));
Expand All @@ -60,6 +65,8 @@ const BoardProvider: FC<IBoardProviderProps> = (props) => {
const exposed: IBoardContext = {
columns,
setColumns,
active,
setActive,
addGameModal: {
status: addGameModal.status,
opened: addGameModal.opened,
Expand Down

0 comments on commit 97a2adc

Please sign in to comment.