add tree
This commit is contained in:
parent
af13954707
commit
90563c20ed
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { useLocal } from "@/utils/use-local";
|
||||||
|
import { ElementType, FC, ReactElement, ReactNode, useEffect } from "react";
|
||||||
|
import { NodeRendererProps, Tree as Arborist, NodeApi } from "react-arborist";
|
||||||
|
|
||||||
|
export type TreeItem = {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
id_parent?: string;
|
||||||
|
sort_idx?: number;
|
||||||
|
children?: TreeItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Tree = <T extends TreeItem>({
|
||||||
|
tree,
|
||||||
|
renderRow,
|
||||||
|
className,
|
||||||
|
onChange,
|
||||||
|
drag,
|
||||||
|
}: {
|
||||||
|
className: string;
|
||||||
|
tree: T[];
|
||||||
|
drag?: boolean;
|
||||||
|
onChange?: (rows: T[]) => Promise<void> | void;
|
||||||
|
renderRow: (arg: { row: T; style: any; dragHandle: any }) => ReactNode;
|
||||||
|
}) => {
|
||||||
|
const local = useLocal({
|
||||||
|
update: {} as Record<string, T>,
|
||||||
|
timeout: null as any,
|
||||||
|
children: generateTreeChildren({
|
||||||
|
renderRow,
|
||||||
|
updateNode: (data, arg) => {
|
||||||
|
if (typeof arg.sort_idx === "number") {
|
||||||
|
data.sort_idx = arg.sort_idx;
|
||||||
|
}
|
||||||
|
local.update[data.id] = { ...data };
|
||||||
|
|
||||||
|
clearTimeout(local.timeout);
|
||||||
|
local.timeout = setTimeout(async () => {
|
||||||
|
if (onChange) {
|
||||||
|
await onChange(Object.values(local.update));
|
||||||
|
local.update = {};
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
tree: formatTree(tree),
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
rob: new ResizeObserver(([el]) => {
|
||||||
|
local.width = el.contentRect.width;
|
||||||
|
local.height = el.contentRect.height;
|
||||||
|
local.render();
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
local.rob.disconnect();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
className,
|
||||||
|
css`
|
||||||
|
position: relative;
|
||||||
|
> div {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
ref={(el) => {
|
||||||
|
if (el) {
|
||||||
|
local.rob.observe(el);
|
||||||
|
const bound = el.getBoundingClientRect();
|
||||||
|
if (local.height === 0) {
|
||||||
|
local.width = bound.width;
|
||||||
|
local.height = bound.height;
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{local.width && local.height && (
|
||||||
|
<Arborist
|
||||||
|
initialData={local.tree}
|
||||||
|
width={local.width}
|
||||||
|
height={local.height}
|
||||||
|
disableDrag={drag === false}
|
||||||
|
children={local.children}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatTree = <T extends TreeItem>(tree: T[]) => {
|
||||||
|
const res: T[] = [];
|
||||||
|
const ftree = tree
|
||||||
|
.filter((e) => e.id)
|
||||||
|
.sort((a, b) => (a.sort_idx || 0) - (b.sort_idx || 0))
|
||||||
|
.map((e) => ({
|
||||||
|
...e,
|
||||||
|
id: e.id,
|
||||||
|
text: e.text,
|
||||||
|
children: e.children,
|
||||||
|
id_parent: e.id_parent || null,
|
||||||
|
sort_idx: e.sort_idx || "0",
|
||||||
|
})) as TreeItem[];
|
||||||
|
|
||||||
|
const scan = (id_parent: null | string, parent: T) => {
|
||||||
|
for (const s of ftree) {
|
||||||
|
if (s.id_parent === id_parent) {
|
||||||
|
if (!parent.children) {
|
||||||
|
parent.children = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.children.push(s);
|
||||||
|
scan(s.id, s as T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scan(null, {
|
||||||
|
id: "root",
|
||||||
|
id_parent: "",
|
||||||
|
sort_idx: 0,
|
||||||
|
text: "",
|
||||||
|
children: res as TreeItem[],
|
||||||
|
} as T);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateTreeChildren = <T extends TreeItem>(arg: {
|
||||||
|
renderRow: (arg: { row: T; style: any; dragHandle: any }) => ReactNode;
|
||||||
|
updateNode: (data: T, new_data: Partial<T>) => void;
|
||||||
|
}) => {
|
||||||
|
const { renderRow, updateNode } = arg;
|
||||||
|
return (({ node, style, dragHandle }) => {
|
||||||
|
let sort_idx = node.data.sort_idx;
|
||||||
|
if (typeof sort_idx === "string") sort_idx = parseInt(sort_idx);
|
||||||
|
if (!node.isDragging && sort_idx !== node.rowIndex) {
|
||||||
|
updateNode(node.data, { sort_idx: node.rowIndex } as Partial<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderRow({ row: node.data, style, dragHandle });
|
||||||
|
}) as ElementType<NodeRendererProps<T>>;
|
||||||
|
};
|
||||||
|
|
@ -14,3 +14,4 @@ export { Breadcrumb } from "./comps/custom/Breadcrumb";
|
||||||
export { Header } from "./comps/custom/Header";
|
export { Header } from "./comps/custom/Header";
|
||||||
export { TableList } from "./comps/custom/TableList";
|
export { TableList } from "./comps/custom/TableList";
|
||||||
export { Carousel } from "./comps/custom/Carousel";
|
export { Carousel } from "./comps/custom/Carousel";
|
||||||
|
export { Tree } from "./comps/list/Tree";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue