Skip to content

Commit

Permalink
feat: new component Catalogue in v5 (#389)
Browse files Browse the repository at this point in the history
* feat(Catalogue): new component, catalogue initial

* chore(Catalogue): lint

* docs: update demo docs

* feat: clear searchStr when close search

* docs: update demo docs

* feat: add defaultStatus & onStatusChange props in Catalogue.DtTree

* chore: rename dtTree、dtTreeSelect to Tree、TreeSelect

* chore: update demos and docs

* chore: treeTit prop exchange to treeTitle

---------

Co-authored-by: dilu <dilu@dtstack.com>
  • Loading branch information
2 people authored and mumiao committed Nov 15, 2023
1 parent 172681d commit 2e5d6f9
Show file tree
Hide file tree
Showing 22 changed files with 2,156 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/catalogue/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as Tree } from './tree';
export { type ITabsStatus, type ITreeDataItem } from './tree';
export { default as TreeSelect } from './treeSelect';
50 changes: 50 additions & 0 deletions src/catalogue/components/tree/components/header/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from 'react';
import { MenuFoldOutlined, MenuUnfoldOutlined, ProfileFilled } from '@ant-design/icons';

import { ITreeProps } from '../../';
import './style.scss';

export const prefixCls = 'dtTreeHeaderWrapper';

export const btnSlotProp = 'btnSlot';

export interface ITreeHeaderProps {
/** 目录标题 */
title?: React.ReactNode;
/** 是否收起 */
collapsed?: boolean;
/** 展开、收起的回调 */
onCollapsed?: (collapsed: boolean) => void;
/** Header 右侧按扭组插槽【建议 icon 不超过3个,超出使用更多icon,下拉显示】,showHeader 为 true 时生效 */
btnSlot?: React.ReactElement;
size?: ITreeProps['size'];
}

export default (props: ITreeHeaderProps) => {
const { onCollapsed, btnSlot } = props;
const handleCollapsed = (flag: boolean) => (_: any) => {
onCollapsed?.(flag);
};
return (
<div className={prefixCls}>
<div className={`${prefixCls}-left`}>
<ProfileFilled className={`${prefixCls}-left__catalogueIcon`} />
<span className={`${prefixCls}-left__title`}>{props?.title ?? ''}</span>
</div>
<div className={`${prefixCls}-right`}>
{btnSlot}
{props.collapsed ? (
<MenuUnfoldOutlined
className={`${prefixCls}-menuFold`}
onClick={handleCollapsed(false)}
/>
) : (
<MenuFoldOutlined
className={`${prefixCls}-menuFold`}
onClick={handleCollapsed(true)}
/>
)}
</div>
</div>
);
};
27 changes: 27 additions & 0 deletions src/catalogue/components/tree/components/header/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.dtTreeHeaderWrapper {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 12px 0;
margin-bottom: 8px;
&-left {
display: flex;
align-items: center;
&__catalogueIcon {
font-size: 20px;
margin-right: 4px;
color: #1D78FF;
}
&__title {
font-size: 14px;
}
}
&-right {
display: flex;
align-items: center;
}
&-menuFold {
font-size: 20px;
color: #AAA;
}
}
1 change: 1 addition & 0 deletions src/catalogue/components/tree/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Header } from './header';
88 changes: 88 additions & 0 deletions src/catalogue/components/tree/helpers/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import * as React from 'react';
import { FolderFilled, FolderOpenFilled } from '@ant-design/icons';
import type { TreeProps } from 'antd';
import type { DataNode } from 'antd/es/tree';
import { ContextMenu } from 'dt-react-component';

import type { ITreeDataItem } from '..';

/**
* @description 根据 query 计算应该展开的 expendKeys
*/
export const getExpendKeysByQuery = ({
tree,
searchStr,
}: {
/** 树数据 */
tree: DataNode[];
/** 搜索字符串 */
searchStr: string;
}): React.Key[] => {
const keys: React.Key[] = [];
if (!searchStr) return [];
tree?.forEach?.((item) => {
if (item?.title?.toString()?.includes?.(searchStr)) {
keys.push(item.key);
}
if (item?.children && item?.children?.length) {
keys.push(...getExpendKeysByQuery({ tree: item?.children, searchStr }));
}
});
return keys;
};

/**
* @description 文件 展开/收起样式
*/
export const getIcon: DataNode['icon'] = ({ expanded, data }) => {
if (!data) return null;
if (!data?.hasOwnProperty('children')) return null;
const styles: React.CSSProperties = {
fontSize: 14,
color: '#1D78FF',
};
if (expanded) return <FolderOpenFilled style={styles} />;
return <FolderFilled style={styles} />;
};

/**
* @description 轮询 Tree 数据,赋值 搜索标识和leafIcon
*/
export const loopTree = (
data: ITreeDataItem[] | undefined,
searchValue: string
): TreeProps['treeData'] => {
return data?.map((item: ITreeDataItem) => {
const strTitle = item?.title as string;
const index = strTitle?.indexOf?.(searchValue);
const beforeStr = strTitle?.substring?.(0, index);
const afterStr = strTitle?.slice?.(index + searchValue?.length);
const contextMenuData =
item?.contextMenuConfig?.data?.map?.((e: any) => ({
...e,
cb: () => {
item?.contextMenuConfig?.onClick(e, item);
},
})) || [];
const title =
index > -1 ? (
<ContextMenu data={contextMenuData}>
{beforeStr}
<span style={{ color: '#f50' }}>{searchValue}</span>
{afterStr}
</ContextMenu>
) : (
<ContextMenu data={contextMenuData}>{strTitle}</ContextMenu>
);
if (item.children) {
return {
icon: getIcon,
...item,
title,
key: item.key,
children: loopTree(item.children, searchValue),
};
}
return { ...item, title, key: item.key, children: undefined };
});
};
Loading

0 comments on commit 2e5d6f9

Please sign in to comment.