finished the collection listing ui
This commit is contained in:
parent
ac70c9e29c
commit
84aeac96ce
|
@ -1,4 +1,4 @@
|
||||||
import React, { Component, useCallback, useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Tree, {
|
import Tree, {
|
||||||
mutateTree,
|
mutateTree,
|
||||||
moveItemOnTree,
|
moveItemOnTree,
|
||||||
|
@ -9,159 +9,53 @@ import Tree, {
|
||||||
TreeSourcePosition,
|
TreeSourcePosition,
|
||||||
TreeDestinationPosition,
|
TreeDestinationPosition,
|
||||||
} from "@atlaskit/tree";
|
} from "@atlaskit/tree";
|
||||||
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
|
||||||
import useCollectionStore from "@/store/collections";
|
import useCollectionStore from "@/store/collections";
|
||||||
|
import { Collection } from "@prisma/client";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
const collections = [
|
interface ExtendedTreeItem extends TreeItem {
|
||||||
{
|
data: Collection;
|
||||||
id: 262,
|
}
|
||||||
name: "dasd",
|
|
||||||
description: "",
|
|
||||||
color: "#0ea5e9",
|
|
||||||
parentId: null,
|
|
||||||
isPublic: false,
|
|
||||||
ownerId: 1,
|
|
||||||
createdAt: "2024-02-18T23:40:44.043Z",
|
|
||||||
updatedAt: "2024-02-19T19:16:14.873Z",
|
|
||||||
parent: null,
|
|
||||||
members: [
|
|
||||||
{
|
|
||||||
userId: 17,
|
|
||||||
collectionId: 262,
|
|
||||||
canCreate: true,
|
|
||||||
canUpdate: false,
|
|
||||||
canDelete: false,
|
|
||||||
createdAt: "2024-02-19T19:16:14.873Z",
|
|
||||||
updatedAt: "2024-02-19T19:16:14.873Z",
|
|
||||||
user: { username: "test", name: "ben", image: "" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
_count: { links: 0 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 268,
|
|
||||||
name: "ab",
|
|
||||||
description: "",
|
|
||||||
color: "#0ea5e9",
|
|
||||||
parentId: 267,
|
|
||||||
isPublic: false,
|
|
||||||
ownerId: 17,
|
|
||||||
createdAt: "2024-02-19T21:06:52.545Z",
|
|
||||||
updatedAt: "2024-02-19T21:06:52.545Z",
|
|
||||||
parent: { id: 267, name: "a" },
|
|
||||||
members: [],
|
|
||||||
_count: { links: 0 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 269,
|
|
||||||
name: "abc",
|
|
||||||
description: "",
|
|
||||||
color: "#0ea5e9",
|
|
||||||
parentId: 268,
|
|
||||||
isPublic: false,
|
|
||||||
ownerId: 17,
|
|
||||||
createdAt: "2024-02-19T21:07:08.565Z",
|
|
||||||
updatedAt: "2024-02-19T21:07:08.565Z",
|
|
||||||
parent: { id: 268, name: "ab" },
|
|
||||||
members: [],
|
|
||||||
_count: { links: 0 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 267,
|
|
||||||
name: "a",
|
|
||||||
description: "",
|
|
||||||
color: "#0ea5e9",
|
|
||||||
parentId: null,
|
|
||||||
isPublic: false,
|
|
||||||
ownerId: 17,
|
|
||||||
createdAt: "2024-02-19T21:06:45.402Z",
|
|
||||||
updatedAt: "2024-02-26T16:59:20.312Z",
|
|
||||||
parent: null,
|
|
||||||
members: [],
|
|
||||||
_count: { links: 0 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 80,
|
|
||||||
name: "abc",
|
|
||||||
description: "s",
|
|
||||||
color: "#0ea5e9",
|
|
||||||
parentId: 79,
|
|
||||||
isPublic: false,
|
|
||||||
ownerId: 1,
|
|
||||||
createdAt: "2024-02-05T07:00:46.881Z",
|
|
||||||
updatedAt: "2024-02-27T06:11:46.358Z",
|
|
||||||
parent: { id: 79, name: "ab" },
|
|
||||||
members: [
|
|
||||||
{
|
|
||||||
userId: 17,
|
|
||||||
collectionId: 80,
|
|
||||||
canCreate: false,
|
|
||||||
canUpdate: false,
|
|
||||||
canDelete: false,
|
|
||||||
createdAt: "2024-02-27T06:11:46.358Z",
|
|
||||||
updatedAt: "2024-02-27T06:11:46.358Z",
|
|
||||||
user: { username: "test", name: "ben", image: "" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 2,
|
|
||||||
collectionId: 80,
|
|
||||||
canCreate: false,
|
|
||||||
canUpdate: false,
|
|
||||||
canDelete: false,
|
|
||||||
createdAt: "2024-02-27T06:11:46.358Z",
|
|
||||||
updatedAt: "2024-02-27T06:11:46.358Z",
|
|
||||||
user: {
|
|
||||||
username: "sarah_connor",
|
|
||||||
name: "Sarah Smith",
|
|
||||||
image: "uploads/avatar/2.jpg",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
_count: { links: 0 },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const DragDropWithNestingTree = () => {
|
const DragDropWithNestingTree = () => {
|
||||||
const buildTreeFromCollections = (collections) => {
|
const buildTreeFromCollections = (collections: Collection[]): TreeData => {
|
||||||
// Step 1: Map Collections to TreeItems
|
const items: { [key: string]: ExtendedTreeItem } = collections.reduce(
|
||||||
const items = collections.reduce((acc, collection) => {
|
(acc: any, collection) => {
|
||||||
acc[collection.id] = {
|
acc[collection.id] = {
|
||||||
id: collection.id.toString(),
|
id: collection.id,
|
||||||
children: [],
|
children: [],
|
||||||
hasChildren: false, // Initially assume no children, adjust in Step 2
|
hasChildren: false,
|
||||||
isExpanded: false,
|
isExpanded: false,
|
||||||
data: {
|
data: {
|
||||||
title: collection.name,
|
name: collection.name,
|
||||||
description: collection.description,
|
description: collection.description,
|
||||||
color: collection.color,
|
color: collection.color,
|
||||||
isPublic: collection.isPublic,
|
isPublic: collection.isPublic,
|
||||||
ownerId: collection.ownerId,
|
ownerId: collection.ownerId,
|
||||||
createdAt: collection.createdAt,
|
createdAt: collection.createdAt,
|
||||||
updatedAt: collection.updatedAt,
|
updatedAt: collection.updatedAt,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
// Step 2: Build Hierarchy
|
|
||||||
collections.forEach((collection) => {
|
collections.forEach((collection) => {
|
||||||
const parentId = collection.parentId;
|
const parentId = collection.parentId;
|
||||||
if (parentId !== null && items[parentId]) {
|
if (parentId && items[parentId]) {
|
||||||
items[parentId].children.push(collection.id.toString());
|
items[parentId].children.push(collection.id);
|
||||||
items[parentId].hasChildren = true;
|
items[parentId].hasChildren = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Define a root item to act as the top-level node of your tree if needed
|
|
||||||
const rootId = "root";
|
const rootId = "root";
|
||||||
items[rootId] = {
|
items[rootId] = {
|
||||||
id: rootId,
|
id: rootId,
|
||||||
children: collections
|
children: collections.filter((c) => c.parentId === null).map((c) => c.id),
|
||||||
.filter((c) => c.parentId === null)
|
|
||||||
.map((c) => c.id.toString()),
|
|
||||||
hasChildren: true,
|
hasChildren: true,
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
data: { title: "Root" },
|
data: { name: "Root" } as Collection,
|
||||||
};
|
};
|
||||||
|
|
||||||
return { rootId, items };
|
return { rootId, items };
|
||||||
|
@ -172,11 +66,17 @@ const DragDropWithNestingTree = () => {
|
||||||
const { collections } = useCollectionStore();
|
const { collections } = useCollectionStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialTree = buildTreeFromCollections(collections);
|
const initialTree = buildTreeFromCollections(
|
||||||
|
collections as unknown as Collection[]
|
||||||
|
);
|
||||||
collections[0] && setTree(initialTree);
|
collections[0] && setTree(initialTree);
|
||||||
}, [collections]);
|
}, [collections]);
|
||||||
|
|
||||||
const getIcon = useCallback((item, onExpand, onCollapse) => {
|
const getIcon = (
|
||||||
|
item: ExtendedTreeItem,
|
||||||
|
onExpand: (id: ItemId) => void,
|
||||||
|
onCollapse: (id: ItemId) => void
|
||||||
|
) => {
|
||||||
if (item.children && item.children.length > 0) {
|
if (item.children && item.children.length > 0) {
|
||||||
return item.isExpanded ? (
|
return item.isExpanded ? (
|
||||||
<button onClick={() => onCollapse(item.id)}>
|
<button onClick={() => onCollapse(item.id)}>
|
||||||
|
@ -188,63 +88,82 @@ const DragDropWithNestingTree = () => {
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <span>•</span>;
|
// return <span>•</span>;
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = ({
|
||||||
({ item, onExpand, onCollapse, provided }: RenderItemParams) => {
|
item,
|
||||||
return (
|
onExpand,
|
||||||
<div ref={provided.innerRef} {...provided.draggableProps}>
|
onCollapse,
|
||||||
|
provided,
|
||||||
|
}: RenderItemParams) => {
|
||||||
|
const collection = item.data;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={provided.innerRef}
|
||||||
|
{...provided.draggableProps}
|
||||||
|
className="flex gap-1 items-center ml-2"
|
||||||
|
>
|
||||||
|
{getIcon(item as ExtendedTreeItem, onExpand, onCollapse)}
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href={`/collections/${collection.id}`}
|
||||||
|
className="w-full"
|
||||||
|
{...provided.dragHandleProps}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className="flex gap-1 items-center"
|
className={`duration-100 py-1 pr-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize mb-1`}
|
||||||
{...provided.dragHandleProps}
|
|
||||||
>
|
>
|
||||||
{getIcon(item, onExpand, onCollapse)}
|
<i
|
||||||
{item.data ? item.data.title : ""}
|
className="bi-folder-fill text-2xl drop-shadow"
|
||||||
|
style={{ color: collection.color }}
|
||||||
|
></i>
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Link>
|
||||||
);
|
</div>
|
||||||
},
|
);
|
||||||
[getIcon]
|
};
|
||||||
);
|
|
||||||
|
|
||||||
const onExpand = useCallback(
|
|
||||||
(itemId: ItemId) => {
|
|
||||||
if (tree) {
|
|
||||||
setTree((currentTree) =>
|
|
||||||
mutateTree(currentTree, itemId, { isExpanded: true })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[tree]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onCollapse = useCallback(
|
|
||||||
(itemId: ItemId) => {
|
|
||||||
if (tree) {
|
|
||||||
setTree((currentTree) =>
|
|
||||||
mutateTree(currentTree, itemId, { isExpanded: false })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[tree]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onDragEnd = useCallback(
|
|
||||||
(source: TreeSourcePosition, destination?: TreeDestinationPosition) => {
|
|
||||||
if (!destination || !tree) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const onExpand = (itemId: ItemId) => {
|
||||||
|
if (tree) {
|
||||||
setTree((currentTree) =>
|
setTree((currentTree) =>
|
||||||
moveItemOnTree(currentTree, source, destination)
|
mutateTree(currentTree!, itemId, { isExpanded: true })
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
[tree]
|
};
|
||||||
);
|
|
||||||
|
const onCollapse = (itemId: ItemId) => {
|
||||||
|
if (tree) {
|
||||||
|
setTree((currentTree) =>
|
||||||
|
mutateTree(currentTree as TreeData, itemId, { isExpanded: false })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragEnd = (
|
||||||
|
source: TreeSourcePosition,
|
||||||
|
destination: TreeDestinationPosition | undefined
|
||||||
|
) => {
|
||||||
|
if (!destination || !tree) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTree((currentTree) => moveItemOnTree(currentTree!, source, destination));
|
||||||
|
};
|
||||||
|
|
||||||
if (!tree) {
|
if (!tree) {
|
||||||
return <div>Loading...</div>; // or any other loading state representation
|
return <div>Loading...</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -261,99 +180,3 @@ const DragDropWithNestingTree = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DragDropWithNestingTree;
|
export default DragDropWithNestingTree;
|
||||||
|
|
||||||
class TreeBuilder {
|
|
||||||
rootId: ItemId;
|
|
||||||
|
|
||||||
items: Record<ItemId, TreeItem>;
|
|
||||||
|
|
||||||
constructor(rootId: ItemId) {
|
|
||||||
const rootItem = this._createItem(`${rootId}`);
|
|
||||||
this.rootId = rootItem.id;
|
|
||||||
this.items = {
|
|
||||||
[rootItem.id]: rootItem,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
withLeaf(id: number) {
|
|
||||||
const leafItem = this._createItem(`${this.rootId}-${id}`);
|
|
||||||
this._addItemToRoot(leafItem.id);
|
|
||||||
this.items[leafItem.id] = leafItem;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
withSubTree(tree: TreeBuilder) {
|
|
||||||
const subTree = tree.build();
|
|
||||||
this._addItemToRoot(`${this.rootId}-${subTree.rootId}`);
|
|
||||||
|
|
||||||
Object.keys(subTree.items).forEach((itemId) => {
|
|
||||||
const finalId = `${this.rootId}-${itemId}`;
|
|
||||||
this.items[finalId] = {
|
|
||||||
...subTree.items[itemId],
|
|
||||||
id: finalId,
|
|
||||||
children: subTree.items[itemId].children.map(
|
|
||||||
(i) => `${this.rootId}-${i}`
|
|
||||||
),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
|
||||||
return {
|
|
||||||
rootId: this.rootId,
|
|
||||||
items: this.items,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_addItemToRoot(id: string) {
|
|
||||||
const rootItem = this.items[this.rootId];
|
|
||||||
rootItem.children.push(id);
|
|
||||||
rootItem.isExpanded = true;
|
|
||||||
rootItem.hasChildren = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_createItem = (id: string) => {
|
|
||||||
return {
|
|
||||||
id: `${id}`,
|
|
||||||
children: [],
|
|
||||||
hasChildren: false,
|
|
||||||
isExpanded: false,
|
|
||||||
isChildrenLoading: false,
|
|
||||||
data: {
|
|
||||||
title: `Title ${id}`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const complexTree: TreeData = new TreeBuilder(1)
|
|
||||||
.withLeaf(0) // 0
|
|
||||||
.withLeaf(1) // 1
|
|
||||||
.withSubTree(
|
|
||||||
new TreeBuilder(2) // 2
|
|
||||||
.withLeaf(0) // 3
|
|
||||||
.withLeaf(1) // 4
|
|
||||||
.withLeaf(2) // 5
|
|
||||||
.withLeaf(3) // 6
|
|
||||||
)
|
|
||||||
.withLeaf(3) // 7
|
|
||||||
.withLeaf(4) // 8
|
|
||||||
.withLeaf(5) // 9
|
|
||||||
.withSubTree(
|
|
||||||
new TreeBuilder(6) // 10
|
|
||||||
.withLeaf(0) // 11
|
|
||||||
.withLeaf(1) // 12
|
|
||||||
.withSubTree(
|
|
||||||
new TreeBuilder(2) // 13
|
|
||||||
.withLeaf(0) // 14
|
|
||||||
.withLeaf(1) // 15
|
|
||||||
.withLeaf(2) // 16
|
|
||||||
)
|
|
||||||
.withLeaf(3) // 17
|
|
||||||
.withLeaf(4) // 18
|
|
||||||
)
|
|
||||||
.withLeaf(7) // 19
|
|
||||||
.withLeaf(8) // 20
|
|
||||||
.build();
|
|
||||||
|
|
Ŝarĝante…
Reference in New Issue