diff --git a/components/webui/client/src/pages/IngestPage/Compress/PathsSelectFormItem/DirectoryTreePopup.tsx b/components/webui/client/src/pages/IngestPage/Compress/PathsSelectFormItem/DirectoryTreePopup.tsx new file mode 100644 index 0000000000..d09612f641 --- /dev/null +++ b/components/webui/client/src/pages/IngestPage/Compress/PathsSelectFormItem/DirectoryTreePopup.tsx @@ -0,0 +1,97 @@ +import React, { + useCallback, + useEffect, + useState, +} from "react"; + +import {Tree} from "antd"; +import type {TreeDataNode} from "antd/es"; +import type {TreeProps} from "antd/es/tree"; + +import styles from "./index.module.css"; +import {getListHeight} from "./utils"; + + +const {DirectoryTree} = Tree; + + +interface DirectoryTreePopupProps { + checkedKeys: string[]; + expandedKeys: string[]; + treeData: TreeDataNode[]; + onCheck: (keys: string[]) => void; + onExpand: (keys: string[]) => void; + onLoadData: (path: string) => Promise; +} + +/** + * Renders a popup component containing a DirectoryTree for path selection. + * + * @param props + * @param props.checkedKeys + * @param props.expandedKeys + * @param props.treeData + * @param props.onCheck + * @param props.onExpand + * @param props.onLoadData + * @return + */ +const DirectoryTreePopup = ({ + checkedKeys, + expandedKeys, + treeData, + onCheck, + onExpand, + onLoadData, +}: DirectoryTreePopupProps) => { + const [height, setHeight] = useState(getListHeight); + + const handleCheck: TreeProps["onCheck"] = useCallback(( + checked: React.Key[] | {checked: React.Key[]; halfChecked: React.Key[]} + ) => { + const keys = Array.isArray(checked) ? + checked : + checked.checked; + + onCheck(keys as string[]); + }, [onCheck]); + + const handleExpand = useCallback((keys: React.Key[]) => { + onExpand(keys as string[]); + }, [onExpand]); + + const handleLoadData = useCallback(async (node: TreeDataNode) => { + await onLoadData(node.key as string); + }, [onLoadData]); + + useEffect(() => { + const handleResize = () => { + setHeight(getListHeight()); + }; + + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + + return ( +
+ +
+ ); +}; + + +export default DirectoryTreePopup; diff --git a/components/webui/client/src/pages/IngestPage/Compress/PathsSelectFormItem/DirectoryTreeSelect.tsx b/components/webui/client/src/pages/IngestPage/Compress/PathsSelectFormItem/DirectoryTreeSelect.tsx new file mode 100644 index 0000000000..4655b7e61f --- /dev/null +++ b/components/webui/client/src/pages/IngestPage/Compress/PathsSelectFormItem/DirectoryTreeSelect.tsx @@ -0,0 +1,107 @@ +import { + useCallback, + useMemo, +} from "react"; + +import {Select} from "antd"; +import type {TreeDataNode} from "antd/es"; + +import DirectoryTreePopup from "./DirectoryTreePopup"; +import {TreeNode} from "./typings"; +import { + filterToParents, + flatToHierarchy, + removeWithDescendants, +} from "./utils"; + + +interface DirectoryTreeSelectProps { + checkedKeys: string[]; + expandedKeys: string[]; + treeData: TreeNode[]; + onCheck: (keys: string[]) => void; + onExpand: (keys: string[]) => void; + onLoadData: (path: string) => Promise; +} + +/** + * Renders a select component that uses DirectoryTree for the dropdown. Provides folder/file + * icons and intuitive directory navigation. + * + * @param props + * @param props.checkedKeys + * @param props.expandedKeys + * @param props.treeData + * @param props.onCheck + * @param props.onExpand + * @param props.onLoadData + * @return + */ +const DirectoryTreeSelect = ({ + checkedKeys, + expandedKeys, + treeData, + onCheck, + onExpand, + onLoadData, +}: DirectoryTreeSelectProps) => { + const hierarchicalTreeData: TreeDataNode[] = useMemo( + () => flatToHierarchy(treeData), + [treeData] + ); + const selectOptions = useMemo( + () => treeData.map((node) => ({label: node.value, value: node.value})), + [treeData] + ); + const displayValue = useMemo( + () => filterToParents(treeData, checkedKeys), + [ + checkedKeys, + treeData, + ] + ); + + const handleClear = useCallback(() => { + onCheck([]); + }, [onCheck]); + + const handleDeselect = useCallback((value: string) => { + onCheck(removeWithDescendants(treeData, checkedKeys, value)); + }, [ + checkedKeys, + onCheck, + treeData, + ]); + + const renderDropdown = useCallback(() => ( + + ), [ + checkedKeys, + expandedKeys, + hierarchicalTreeData, + onCheck, + onExpand, + onLoadData, + ]); + + return ( +