el.xwx.moe/components/CollectionListing.tsx

217 lines
5.9 KiB
TypeScript
Raw Normal View History

2024-03-01 05:59:14 -06:00
import React, { useEffect, useState } from "react";
import Tree, {
mutateTree,
moveItemOnTree,
RenderItemParams,
TreeItem,
TreeData,
ItemId,
TreeSourcePosition,
TreeDestinationPosition,
} from "@atlaskit/tree";
import useCollectionStore from "@/store/collections";
2024-03-01 05:59:14 -06:00
import { Collection } from "@prisma/client";
import Link from "next/link";
2024-03-01 08:33:58 -06:00
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
2024-03-01 13:02:55 -06:00
import { useRouter } from "next/router";
2024-03-01 05:59:14 -06:00
interface ExtendedTreeItem extends TreeItem {
data: Collection;
}
2024-03-01 13:02:55 -06:00
const CollectionListing = () => {
const [tree, setTree] = useState<TreeData>();
const { collections } = useCollectionStore();
2024-03-01 13:02:55 -06:00
const router = useRouter();
const currentPath = router.asPath;
useEffect(() => {
2024-03-01 13:02:55 -06:00
const initialTree = buildTreeFromCollections(collections, router);
collections[0] && setTree(initialTree);
}, [collections]);
2024-03-01 05:59:14 -06:00
const onExpand = (itemId: ItemId) => {
if (tree) {
setTree((currentTree) =>
mutateTree(currentTree!, itemId, { isExpanded: true })
);
}
};
2024-02-22 01:51:51 -06:00
2024-03-01 05:59:14 -06:00
const onCollapse = (itemId: ItemId) => {
if (tree) {
setTree((currentTree) =>
2024-03-01 05:59:14 -06:00
mutateTree(currentTree as TreeData, itemId, { isExpanded: false })
);
2024-03-01 05:59:14 -06:00
}
};
const onDragEnd = (
source: TreeSourcePosition,
destination: TreeDestinationPosition | undefined
) => {
if (!destination || !tree) {
return;
}
setTree((currentTree) => moveItemOnTree(currentTree!, source, destination));
};
2024-02-22 02:04:01 -06:00
if (!tree) {
2024-03-01 13:02:55 -06:00
return <></>;
} else
return (
<Tree
tree={tree}
renderItem={(itemProps) => renderItem({ ...itemProps }, currentPath)}
onExpand={onExpand}
onCollapse={onCollapse}
onDragEnd={onDragEnd}
isDragEnabled
isNestingEnabled
/>
);
};
2024-03-01 13:02:55 -06:00
export default CollectionListing;
2024-03-01 07:37:20 -06:00
2024-03-01 13:02:55 -06:00
const renderItem = (
{ item, onExpand, onCollapse, provided }: RenderItemParams,
currentPath: string
) => {
2024-03-01 07:37:20 -06:00
const collection = item.data;
return (
2024-03-01 13:02:55 -06:00
<div ref={provided.innerRef} {...provided.draggableProps} className="mb-1">
<div
className={`${
currentPath === `/collections/${collection.id}`
? "bg-primary/20 is-active"
: "hover:bg-neutral/20"
} duration-100 flex gap-1 items-center pr-2 pl-1 rounded-md`}
2024-03-01 07:37:20 -06:00
>
2024-03-01 13:02:55 -06:00
{Icon(item as ExtendedTreeItem, onExpand, onCollapse)}
2024-03-01 07:37:20 -06:00
2024-03-01 13:02:55 -06:00
<Link
href={`/collections/${collection.id}`}
className="w-full"
{...provided.dragHandleProps}
>
<div
className={`py-1 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`}
>
2024-03-01 07:37:20 -06:00
<i
2024-03-01 13:02:55 -06:00
className="bi-folder-fill text-2xl drop-shadow"
style={{ color: collection.color }}
2024-03-01 07:37:20 -06:00
></i>
2024-03-01 13:02:55 -06:00
<p className="truncate w-full">{collection.name}</p>
{collection.isPublic ? (
<i
className="bi-globe2 text-sm text-black/50 dark:text-white/50 drop-shadow"
title="This collection is being shared publicly."
></i>
) : undefined}
<div className="drop-shadow text-neutral text-xs">
{collection._count?.links}
</div>
2024-03-01 07:37:20 -06:00
</div>
2024-03-01 13:02:55 -06:00
</Link>
</div>
2024-03-01 07:37:20 -06:00
</div>
);
};
const Icon = (
item: ExtendedTreeItem,
onExpand: (id: ItemId) => void,
onCollapse: (id: ItemId) => void
) => {
if (item.children && item.children.length > 0) {
return item.isExpanded ? (
<button onClick={() => onCollapse(item.id)}>
2024-03-01 13:02:55 -06:00
<div className="bi-caret-down-fill opacity-50 hover:opacity-100 duration-200"></div>
2024-03-01 07:37:20 -06:00
</button>
) : (
<button onClick={() => onExpand(item.id)}>
2024-03-01 13:02:55 -06:00
<div className="bi-caret-right-fill opacity-40 hover:opacity-100 duration-200"></div>
2024-03-01 07:37:20 -06:00
</button>
);
}
// return <span>&bull;</span>;
2024-03-01 13:02:55 -06:00
return <div className="pl-1"></div>;
2024-03-01 07:37:20 -06:00
};
2024-03-01 08:33:58 -06:00
const buildTreeFromCollections = (
2024-03-01 13:02:55 -06:00
collections: CollectionIncludingMembersAndLinkCount[],
router: ReturnType<typeof useRouter>
2024-03-01 08:33:58 -06:00
): TreeData => {
const items: { [key: string]: ExtendedTreeItem } = collections.reduce(
(acc: any, collection) => {
acc[collection.id as number] = {
id: collection.id,
children: [],
hasChildren: false,
isExpanded: false,
data: {
id: collection.id,
2024-03-01 13:02:55 -06:00
parentId: collection.parentId,
2024-03-01 08:33:58 -06:00
name: collection.name,
description: collection.description,
color: collection.color,
isPublic: collection.isPublic,
ownerId: collection.ownerId,
createdAt: collection.createdAt,
updatedAt: collection.updatedAt,
_count: {
links: collection._count?.links,
},
},
};
return acc;
},
{}
);
2024-03-01 13:02:55 -06:00
console.log("items:", items);
const activeCollectionId = Number(router.asPath.split("/collections/")[1]);
if (activeCollectionId) {
for (const item in items) {
const collection = items[item];
if (Number(item) === activeCollectionId && collection.data.parentId) {
// get all the parents of the active collection recursively until root and set isExpanded to true
let parentId = collection.data.parentId || null;
while (parentId) {
items[parentId].isExpanded = true;
parentId = items[parentId].data.parentId;
}
}
}
}
2024-03-01 08:33:58 -06:00
collections.forEach((collection) => {
const parentId = collection.parentId;
if (parentId && items[parentId] && collection.id) {
items[parentId].children.push(collection.id);
items[parentId].hasChildren = true;
}
});
const rootId = "root";
items[rootId] = {
id: rootId,
children: (collections
.filter((c) => c.parentId === null)
.map((c) => c.id) || "") as unknown as string[],
hasChildren: true,
isExpanded: true,
data: { name: "Root" } as Collection,
};
return { rootId, items };
};