Skip to content
Snippets Groups Projects
Library.tsx 7.5 KiB
Newer Older
import { FolderOutlined, ReloadOutlined } from "@ant-design/icons";
import { Button, Col, Divider, Layout, Row, Typography } from "antd";
import { Content } from "antd/lib/layout/layout";
Peter J's avatar
Peter J committed
import Tree from "antd/lib/tree";
Peter J's avatar
Peter J committed
import { FunctionComponent, useEffect, useState } from "react";
import { apiGetLibrary, apiMoveItem } from "../../common/APIUtils";
import { Item, ItemType } from "../../common/Common";
Peter J's avatar
Peter J committed
import { useAuth } from "../auth/Auth";
import ItemInfo, { getIconForItemType } from "./items/ItemInfo";
const { Title } = Typography;

Peter J's avatar
Peter J committed
interface LibraryProps {

}

const Library: FunctionComponent<LibraryProps> = () => {
  const auth = useAuth();
  const [isLibraryLoading, setIsLibraryLoading] = useState(false);
Peter J's avatar
Peter J committed
  const [treeData, setTreeData] = useState<any[]>([]);
  const [expandedKeys, setExpandedKeys] = useState<Array<string>>([]);
Peter J's avatar
Peter J committed
  const [selectedItem, setSelectedItem] = useState<any | null>(null);

  // fetch library on load
  useEffect(() => {
    fetchLibrary();
  }, [auth.user.token]); // [] execute on user token value change, could be replaced with just [] which is basicly onLoad ..

  const compare = (a: Item, b: Item) => {
    let aIndex = Object.keys(ItemType).indexOf(a.type);
    let bIndex = Object.keys(ItemType).indexOf(b.type);
    if (aIndex < bIndex) {
      return -1;
    }
    if (aIndex > bIndex) {
      return 1;
    }
    return 0;
  }

Peter J's avatar
Peter J committed
  const fetchLibrary = () => {
    setIsLibraryLoading(true);
    setTimeout(
      () => {
        apiGetLibrary(auth.user.token)
          .then(response => {
            console.log("fetched library: ", response);
Peter J's avatar
Peter J committed
            var data = response;
            setIconsAndSortRecursive(data[0]);
Peter J's avatar
Peter J committed
            setTreeData(data);
            selectedItem ? setSelectedItem(findItemByIdIter(selectedItem?.id, data[0])) : setSelectedItem(null);
Peter J's avatar
Peter J committed
          }).catch(error => {
            console.error("error fetching library: ", error);
          }).finally(() => setIsLibraryLoading(false))
      },
      500
    );
  }

  const setIconsAndSortRecursive = (item: any) => {
    item.icon = getIconForItemType(item?.type as ItemType);
    // item.children?.sort(compare);
Peter J's avatar
Peter J committed
    item.children?.forEach((element: any) => {
      setIconsAndSortRecursive(element);
Peter J's avatar
Peter J committed
    });
  }

  const findItemByIdIter = (id: string, root: any) => {
    var stack = [], item, ii;
    stack.push(root);
    while (stack.length > 0) {
      item = stack.pop();
      if (item.id === id) {
        return item;
      } else if (item.children && item.children.length) {
        for (ii = 0; ii < item.children.length; ii += 1) {
          stack.push(item.children[ii]);
        }
      }
    }
    // Didn't find it. Return null.
    return null;
  }

Peter J's avatar
Peter J committed
  const onSelect = (keys: any, info: any) => {
    console.log("onSelect: ", keys, info);

Peter J's avatar
Peter J committed
    setSelectedItem(info.node);
  };

  const onDrop = (info: any) => {
    // TODO: clean this up a bit
    // console.log("item dropped: ", info);

    const dropKey = info.node.key;
    const dragKey = info.dragNode.key;
    const dropPos = dropKey.split('-');
    const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);

    var actualPosition = dropPosition;

    console.log(`dropkey: ${dropKey}, dragKey: ${dragKey}, dropPos: ${dropPos}, dropPosition: ${dropPosition}`);


    const loop = (data: any, key: any, callback: any) => {
      for (let i = 0; i < data.length; i++) {
        if (data[i].key === key) {
          return callback(data[i], i, data);
        }
        if (data[i].children) {
          loop(data[i].children, key, callback);
        }
      }
    };
    const data = [...treeData];

    // Find dragObject
    let draggedItem: any;
    loop(data, dragKey, (item: any, index: any, arr: any) => {
      arr.splice(index, 1);
      draggedItem = item;
    });

    if (!info.dropToGap) {
      console.log("item dropped onto another item, move to first position");

      // item dropped onto another item
      loop(data, dropKey, (newParent: any) => {
        newParentId = newParent.id;
        newParent.children = newParent.children || [];
        // insert at the beginning of that element's children
        newParent.children.unshift(draggedItem);
      });
    } else if (
      (info.node.props.children || []).length > 0 && // Has children
      info.node.props.expanded && // Is expanded
      dropPosition === 1 // On the bottom gap
    ) {
      console.log("item dropped into gap, different parent");

      loop(data, dropKey, (newParent: any) => {
        newParentId = newParent.id;
        newParent.children = newParent.children || [];
        // where to insert 示例添加到头部,可以是随意位置
        newParent.children.unshift(draggedItem);
        // in previous version, we use item.children.push(dragObj) to insert the
        // item to the tail of the children
      });
    } else {
      console.log("item dropped into gap, same parent");

      let ar: any;
      let i: any;
      loop(data, dropKey, (item: any, index: any, arr: any) => {
        ar = arr;
        i = index;
      });
      if (dropPosition === -1) {
        ar.splice(i, 0, draggedItem);
      } else {
        ar.splice(i + 1, 0, draggedItem);
      }
    }

    console.log(`move item ${draggedItem.title} to position ${actualPosition} under new parent ${newParentId}`);
    apiMoveItem(draggedItem, newParentId, actualPosition, auth.user?.token)
      .then(() => {
        console.log("ITEM MOVED");
      }).catch(error => {
        console.error(error);
      }).finally(() => fetchLibrary());

    // setTreeData(data);
  };

  const onExpand = (expandedKeys: Array<any>, info: any) => {
    console.log("expanded key:", info);
    setExpandedKeys(expandedKeys);
  }

  const library = [
    {
      title: 'Root Category',
      key: '0-0',
      icon: <FolderOutlined />,
      children: [
        {
          title: 'Subcategory',
          key: '0-0-0',
          icon: <FolderOutlined />,
          children: [],
        }
      ]
    }
  ]

Peter J's avatar
Peter J committed
  return (
    <Layout className="site-layout-background">
      <Content className="site-layout-background"
        style={{
          padding: 24,
          margin: 0,
          minHeight: "90vh",
        }}>
        <Title level={3} style={{ paddingLeft: "25px", paddingBottom: "15px" }}>Library</Title>
        <Divider orientation="left" orientationMargin="25px">
          Browser
          <Button loading={isLibraryLoading} onClick={fetchLibrary} style={{ margin: "0 15px" }} icon={<ReloadOutlined />}>
            Refresh
          </Button>
        </Divider>
Peter J's avatar
Peter J committed
        <Row wrap={false}>
          <Col flex="600px">
            <Tree
              defaultExpandAll
              showIcon={true}
              selectedKeys={[selectedItem?.key]}
              expandedKeys={expandedKeys}
              defaultExpandedKeys={expandedKeys}
              onSelect={onSelect}
              onExpand={onExpand}
              onDrop={onDrop}
              treeData={treeData}
              draggable={true}
              style={{ padding: "15px" }}
            />
          </Col>
Peter J's avatar
Peter J committed
          <Col flex={3} >
            {
              selectedItem !== null ?
                <div style={{ padding: "15px" }}>
                  <ItemInfo selectedItem={selectedItem} selectedItemParent={findItemByIdIter(selectedItem.parentId, treeData[0])} onContentUpdate={fetchLibrary} />
                </div> :
                <></>
            }
          </Col>
        </Row>
      </Content>
    </Layout>
Peter J's avatar
Peter J committed
  );
}

export default Library;