Match-id-afbd9aba70098fe6a0446b81b44bcd1220e61ee5
This commit is contained in:
parent
cc11ffd17f
commit
b3a46f31c5
|
@ -1,77 +1,151 @@
|
||||||
// 过滤树的抽象逻辑实现
|
// 过滤树的抽象逻辑
|
||||||
// 需要知道渲染了哪些数据,搜索的字符串
|
// 需要知道渲染了哪些数据,过滤的字符串/正则表达式
|
||||||
// 控制Tree组件位置跳转,告知搜索文本
|
// 控制Tree组件位置跳转,告知匹配结果
|
||||||
// 清空搜索框,告知搜索框当前是第几个结果,跳转搜索结果接口
|
// 清空搜索框,告知搜索框当前是第几个结果,跳转搜索结果
|
||||||
|
//
|
||||||
|
// 跳转搜索结果的交互逻辑:
|
||||||
|
// 如果当前页面存在匹配项,页面不动
|
||||||
|
// 如果当前页面不存在匹配项,页面跳转到第一个匹配项位置
|
||||||
|
// 如果匹配项被折叠,需要展开其父节点。注意只展开当前匹配项的父节点,其他匹配项的父节点不展开
|
||||||
|
// 跳转到上一个匹配项或下一个匹配项时,如果匹配项被折叠,需要展开其父节点
|
||||||
|
//
|
||||||
|
// 寻找父节点:
|
||||||
|
// 找到该节点的缩进值,和index值,在data中向上遍历,通过缩进值判断父节点
|
||||||
|
|
||||||
import { useState, useRef } from 'horizon';
|
import { useState, useRef } from 'horizon';
|
||||||
import { createRegExp } from '../utils';
|
import { createRegExp } from '../utils';
|
||||||
|
|
||||||
export function FilterTree<T extends {
|
/**
|
||||||
|
* 把节点的父节点从收起节点数组中删除,并返回新的收起节点数组
|
||||||
|
*
|
||||||
|
* @param item 需要展开父节点的节点
|
||||||
|
* @param data 全部数据
|
||||||
|
* @param collapsedNodes 收起节点数据
|
||||||
|
* @returns 新的收起节点数组
|
||||||
|
*/
|
||||||
|
function expandItemParent(item: BaseType, data: BaseType[], collapsedNodes: BaseType[]): BaseType[] {
|
||||||
|
const index = data.indexOf(item);
|
||||||
|
let currentIndentation = item.indentation;
|
||||||
|
// 不对原始数据进行修改
|
||||||
|
const newcollapsedNodes = [...collapsedNodes];
|
||||||
|
for (let i = index - 1; i >= 0; i--) {
|
||||||
|
const lastData = data[i];
|
||||||
|
const lastIndentation = lastData.indentation;
|
||||||
|
// 缩进更小,找到了父节点
|
||||||
|
if (lastIndentation < currentIndentation) {
|
||||||
|
// 更新缩进值,只找父节点的父节点,避免修改父节点的兄弟节点的展开状态
|
||||||
|
currentIndentation = lastIndentation;
|
||||||
|
const cIndex = newcollapsedNodes.indexOf(lastData);
|
||||||
|
if (cIndex !== -1) {
|
||||||
|
newcollapsedNodes.splice(cIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newcollapsedNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseType = {
|
||||||
id: string,
|
id: string,
|
||||||
name: string
|
name: string,
|
||||||
}>(props: { data: T[] }) {
|
indentation: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FilterTree<T extends BaseType>(props: { data: T[] }) {
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
const [filterValue, setFilterValue] = useState('');
|
const [filterValue, setFilterValue] = useState('');
|
||||||
const [selectId, setSelectId] = useState(null);
|
const [currentItem, setCurrentItem] = useState(null); // 当前选中的匹配项
|
||||||
const showItems = useRef([]);
|
const showItemsRef = useRef([]); // 页面展示的 items
|
||||||
const matchItemsRef = useRef([]);
|
const matchItemsRef = useRef([]); // 匹配过滤条件的 items
|
||||||
|
const collapsedNodesRef = useRef([]); // 折叠节点,如果匹配 item 被折叠了,需要展开
|
||||||
|
|
||||||
const matchItems = matchItemsRef.current;
|
const matchItems = matchItemsRef.current;
|
||||||
|
const collapsedNodes = collapsedNodesRef.current;
|
||||||
|
|
||||||
|
const updatecollapsedNodes = (item: BaseType) => {
|
||||||
|
const newcollapsedNodes = expandItemParent(item, data, collapsedNodes);
|
||||||
|
// 如果新旧收起节点数组长度不一样,说明存在收起节点
|
||||||
|
if (newcollapsedNodes.length !== collapsedNodes.length) {
|
||||||
|
// 更新引用,确保 VTree 拿到新的 collapsedNodes
|
||||||
|
collapsedNodesRef.current = newcollapsedNodes;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onChangeSearchValue = (search: string) => {
|
const onChangeSearchValue = (search: string) => {
|
||||||
const reg = createRegExp(search);
|
const reg = createRegExp(search);
|
||||||
let matchShowId = null;
|
let newCurrentItem = null;
|
||||||
let newMatchItems = [];
|
let newMatchItems = [];
|
||||||
if (search !== '') {
|
if (search !== '') {
|
||||||
|
const showItems: T[] = showItemsRef.current;
|
||||||
newMatchItems = data.reduce((pre, current) => {
|
newMatchItems = data.reduce((pre, current) => {
|
||||||
const { id, name } = current;
|
const { name } = current;
|
||||||
if (reg && name.match(reg)) {
|
if (reg && name.match(reg)) {
|
||||||
pre.push(id);
|
pre.push(current);
|
||||||
if (matchShowId === null) {
|
// 如果当前页面显示的 item 存在匹配项,则把它设置为 currentItem
|
||||||
matchShowId = id;
|
if (newCurrentItem === null && showItems.includes(current)) {
|
||||||
|
newCurrentItem = current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pre;
|
return pre;
|
||||||
}, []);
|
}, []);
|
||||||
if (newMatchItems.length === 0) {
|
if (newMatchItems.length === 0) {
|
||||||
setSelectId(null);
|
setCurrentItem(null);
|
||||||
} else {
|
} else {
|
||||||
if (matchShowId === null) {
|
if (newCurrentItem === null) {
|
||||||
setSelectId(newMatchItems[0]);
|
const item = newMatchItems[0];
|
||||||
|
// 不处于当前展示页面,需要展开父节点
|
||||||
|
updatecollapsedNodes(item);
|
||||||
|
setCurrentItem(item);
|
||||||
} else {
|
} else {
|
||||||
setSelectId(matchShowId);
|
setCurrentItem(newCurrentItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setCurrentItem(null);
|
||||||
}
|
}
|
||||||
matchItemsRef.current = newMatchItems;
|
matchItemsRef.current = newMatchItems;
|
||||||
setFilterValue(search);
|
setFilterValue(search);
|
||||||
};
|
};
|
||||||
const onSelectNext = () => {
|
const onSelectNext = () => {
|
||||||
const index = matchItems.indexOf(selectId);
|
const index = matchItems.indexOf(currentItem);
|
||||||
const nextIndex = index + 1;
|
const nextIndex = index + 1;
|
||||||
if (nextIndex < matchItemsRef.current.length) {
|
if (nextIndex < matchItemsRef.current.length) {
|
||||||
setSelectId(matchItems[nextIndex]);
|
const item = matchItems[nextIndex];
|
||||||
|
// 不处于当前展示页面,需要展开父节点
|
||||||
|
updatecollapsedNodes(item);
|
||||||
|
setCurrentItem(item);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onSelectLast = () => {
|
const onSelectLast = () => {
|
||||||
const index = matchItems.indexOf(selectId);
|
const index = matchItems.indexOf(currentItem);
|
||||||
const last = index - 1;
|
const last = index - 1;
|
||||||
if (last >= 0) {
|
if (last >= 0) {
|
||||||
setSelectId(matchItems[last]);
|
const item = matchItems[last];
|
||||||
|
// 不处于当前展示页面,需要展开父节点
|
||||||
|
updatecollapsedNodes(item);
|
||||||
|
setCurrentItem(matchItems[last]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const setShowItems = (items) => {
|
const setShowItems = (items) => {
|
||||||
showItems.current = [...items];
|
showItemsRef.current = [...items];
|
||||||
};
|
};
|
||||||
const onClear = () => {
|
const onClear = () => {
|
||||||
onChangeSearchValue('');
|
onChangeSearchValue('');
|
||||||
};
|
};
|
||||||
|
const setcollapsedNodes = (items) => {
|
||||||
|
// 不更新引用,避免子组件的重复渲染
|
||||||
|
collapsedNodesRef.current.length = 0;
|
||||||
|
collapsedNodesRef.current.push(...items);
|
||||||
|
};
|
||||||
return {
|
return {
|
||||||
filterValue,
|
filterValue,
|
||||||
setFilterValue: onChangeSearchValue,
|
onChangeSearchValue,
|
||||||
onClear,
|
onClear,
|
||||||
selectId,
|
currentItem,
|
||||||
matchItems,
|
matchItems,
|
||||||
onSelectNext,
|
onSelectNext,
|
||||||
onSelectLast,
|
onSelectLast,
|
||||||
setShowItems,
|
setShowItems,
|
||||||
|
collapsedNodes,
|
||||||
|
setcollapsedNodes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,33 +8,48 @@ interface IProps<T extends { id: string }> {
|
||||||
height: number, // VList 的高度
|
height: number, // VList 的高度
|
||||||
children: any, // horizon 组件,组件类型是 T
|
children: any, // horizon 组件,组件类型是 T
|
||||||
itemHeight: number,
|
itemHeight: number,
|
||||||
scrollIndex?: number,
|
scrollToItem?: T, // 滚动到指定项位置,如果该项在可见区域内,不滚动,如果不在,则滚动到中间位置
|
||||||
onRendered:(renderInfo: renderInfoType) => void;
|
onRendered: (renderInfo: renderInfoType<T>) => void;
|
||||||
filter?(data: T): boolean, // false 表示该行不显示
|
filter?(data: T): boolean, // false 表示该行不显示
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultRenderInfo = {
|
export type renderInfoType<T> = {
|
||||||
visibleItems: ([] as string[])
|
visibleItems: T[],
|
||||||
|
skipItemCountBeforeScrollItem: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type renderInfoType = typeof defaultRenderInfo;
|
|
||||||
|
|
||||||
export function VList<T extends { id: string }>(props: IProps<T>) {
|
export function VList<T extends { id: string }>(props: IProps<T>) {
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
height,
|
height,
|
||||||
children,
|
children,
|
||||||
itemHeight,
|
itemHeight,
|
||||||
scrollIndex = 0,
|
scrollToItem,
|
||||||
filter,
|
filter,
|
||||||
onRendered,
|
onRendered,
|
||||||
} = props;
|
} = props;
|
||||||
const [scrollTop, setScrollTop] = useState(scrollIndex * itemHeight);
|
const [scrollTop, setScrollTop] = useState(data.indexOf(scrollToItem) * itemHeight);
|
||||||
const renderInfo = useRef({visibleItems: []});
|
const renderInfoRef: { current: renderInfoType<T> } = useRef({ visibleItems: [], skipItemCountBeforeScrollItem: 0 });
|
||||||
|
const containerRef = useRef();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onRendered(renderInfo.current);
|
onRendered(renderInfoRef.current);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (scrollToItem) {
|
||||||
|
const renderInfo = renderInfoRef.current;
|
||||||
|
// 在滚动区域,不滚动
|
||||||
|
if (!renderInfo.visibleItems.includes(scrollToItem)) {
|
||||||
|
const index = data.indexOf(scrollToItem);
|
||||||
|
// top值计算需要减掉filter条件判定不显示项
|
||||||
|
const totalCount = index - renderInfoRef.current.skipItemCountBeforeScrollItem;
|
||||||
|
// 显示在页面中间
|
||||||
|
const top = totalCount * itemHeight - height / 2;
|
||||||
|
containerRef.current.scrollTo({ top: top });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [scrollToItem]);
|
||||||
|
|
||||||
const handleScroll = (event: any) => {
|
const handleScroll = (event: any) => {
|
||||||
const scrollTop = event.target.scrollTop;
|
const scrollTop = event.target.scrollTop;
|
||||||
setScrollTop(scrollTop);
|
setScrollTop(scrollTop);
|
||||||
|
@ -48,9 +63,14 @@ export function VList<T extends { id: string }>(props: IProps<T>) {
|
||||||
// 如果最后一个显示不全,不统计在显示 ids 内
|
// 如果最后一个显示不全,不统计在显示 ids 内
|
||||||
const maxTop = scrollTop + height - itemHeight;
|
const maxTop = scrollTop + height - itemHeight;
|
||||||
// 清空记录的上次渲染的数据
|
// 清空记录的上次渲染的数据
|
||||||
renderInfo.current.visibleItems.length = 0;
|
renderInfoRef.current.visibleItems.length = 0;
|
||||||
|
const scrollItemIndex = data.indexOf(scrollToItem);
|
||||||
|
renderInfoRef.current.skipItemCountBeforeScrollItem = 0;
|
||||||
data.forEach((item, i) => {
|
data.forEach((item, i) => {
|
||||||
if (filter && !filter(item)) {
|
if (filter && !filter(item)) {
|
||||||
|
if (scrollItemIndex > i) {
|
||||||
|
renderInfoRef.current.skipItemCountBeforeScrollItem++;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (totalHeight >= startShowTopValue && showList.length <= showNum) {
|
if (totalHeight >= startShowTopValue && showList.length <= showNum) {
|
||||||
|
@ -63,14 +83,14 @@ export function VList<T extends { id: string }>(props: IProps<T>) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
if (totalHeight >= scrollTop && totalHeight < maxTop) {
|
if (totalHeight >= scrollTop && totalHeight < maxTop) {
|
||||||
renderInfo.current.visibleItems.push(item);
|
renderInfoRef.current.visibleItems.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
totalHeight += itemHeight;
|
totalHeight += itemHeight;
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container} onScroll={handleScroll}>
|
<div ref={containerRef} className={styles.container} onScroll={handleScroll}>
|
||||||
{showList}
|
{showList}
|
||||||
<div style={{ marginTop: totalHeight }} />
|
<div style={{ marginTop: totalHeight }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,8 +14,8 @@ export interface IData {
|
||||||
|
|
||||||
interface IItem {
|
interface IItem {
|
||||||
hasChild: boolean,
|
hasChild: boolean,
|
||||||
onCollapse: (id: string) => void,
|
onCollapse: (data: IData) => void,
|
||||||
onClick: (id: string) => void,
|
onClick: (id: IData) => void,
|
||||||
isCollapsed: boolean,
|
isCollapsed: boolean,
|
||||||
isSelect: boolean,
|
isSelect: boolean,
|
||||||
highlightValue: string,
|
highlightValue: string,
|
||||||
|
@ -34,21 +34,20 @@ function Item(props: IItem) {
|
||||||
isSelect,
|
isSelect,
|
||||||
highlightValue = '',
|
highlightValue = '',
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
userKey,
|
userKey,
|
||||||
id,
|
indentation,
|
||||||
indentation,
|
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
const isShowKey = userKey !== '';
|
const isShowKey = userKey !== '';
|
||||||
const showIcon = hasChild ? <Triangle director={isCollapsed ? 'right' : 'down'} /> : '';
|
const showIcon = hasChild ? <Triangle director={isCollapsed ? 'right' : 'down'} /> : '';
|
||||||
const handleClickCollapse = () => {
|
const handleClickCollapse = () => {
|
||||||
onCollapse(id);
|
onCollapse(data);
|
||||||
};
|
};
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
onClick(id);
|
onClick(data);
|
||||||
};
|
};
|
||||||
const itemAttr: any = { className: styles.treeItem, onClick: handleClick };
|
const itemAttr: any = { className: styles.treeItem, onClick: handleClick };
|
||||||
if (isSelect) {
|
if (isSelect) {
|
||||||
|
@ -96,31 +95,39 @@ function Item(props: IItem) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function VTree({ data, highlightValue, selectedId, onRendered }: {
|
function VTree(props: {
|
||||||
data: IData[],
|
data: IData[],
|
||||||
highlightValue: string,
|
highlightValue: string,
|
||||||
selectedId: number,
|
scrollToItem: IData,
|
||||||
onRendered: (renderInfo: renderInfoType) => void
|
onRendered: (renderInfo: renderInfoType<IData>) => void,
|
||||||
|
collapsedNodes?: IData[],
|
||||||
|
onCollapseNode?: (item: IData[]) => void,
|
||||||
}) {
|
}) {
|
||||||
const [collapseNode, setCollapseNode] = useState(new Set<string>());
|
const { data, highlightValue, scrollToItem, onRendered, onCollapseNode } = props;
|
||||||
const [selectItem, setSelectItem] = useState(selectedId);
|
const [collapseNode, setCollapseNode] = useState(props.collapsedNodes || []);
|
||||||
|
const [selectItem, setSelectItem] = useState(scrollToItem);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectItem(selectedId);
|
setSelectItem(scrollToItem);
|
||||||
}, [selectedId]);
|
}, [scrollToItem]);
|
||||||
const changeCollapseNode = (id: string) => {
|
useEffect(() => {
|
||||||
const nodes = new Set<string>();
|
setCollapseNode(props.collapsedNodes || []);
|
||||||
collapseNode.forEach(value => {
|
}, [props.collapsedNodes]);
|
||||||
nodes.add(value);
|
|
||||||
});
|
const changeCollapseNode = (item: IData) => {
|
||||||
if (nodes.has(id)) {
|
const nodes: IData[] = [...collapseNode];
|
||||||
nodes.delete(id);
|
const index = nodes.indexOf(item);
|
||||||
|
if (index === -1) {
|
||||||
|
nodes.push(item);
|
||||||
} else {
|
} else {
|
||||||
nodes.add(id);
|
nodes.splice(index, 1);
|
||||||
}
|
}
|
||||||
setCollapseNode(nodes);
|
setCollapseNode(nodes);
|
||||||
|
if (onCollapseNode) {
|
||||||
|
onCollapseNode(nodes);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const handleClickItem = (id: string) => {
|
const handleClickItem = (item: IData) => {
|
||||||
setSelectItem(id);
|
setSelectItem(item);
|
||||||
};
|
};
|
||||||
|
|
||||||
let currentCollapseIndentation: null | number = null;
|
let currentCollapseIndentation: null | number = null;
|
||||||
|
@ -135,8 +142,7 @@ function VTree({ data, highlightValue, selectedId, onRendered }: {
|
||||||
currentCollapseIndentation = null;
|
currentCollapseIndentation = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const id = item.id;
|
const isCollapsed = collapseNode.includes(item);
|
||||||
const isCollapsed = collapseNode.has(id);
|
|
||||||
if (isCollapsed) {
|
if (isCollapsed) {
|
||||||
// 该节点需要收起子节点
|
// 该节点需要收起子节点
|
||||||
currentCollapseIndentation = item.indentation;
|
currentCollapseIndentation = item.indentation;
|
||||||
|
@ -146,14 +152,14 @@ function VTree({ data, highlightValue, selectedId, onRendered }: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SizeObserver className={styles.treeContainer}>
|
<SizeObserver className={styles.treeContainer}>
|
||||||
{(width, height) => {
|
{(width: number, height: number) => {
|
||||||
return (
|
return (
|
||||||
<VList
|
<VList
|
||||||
data={data}
|
data={data}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
itemHeight={18}
|
itemHeight={18}
|
||||||
scrollIndex={data.indexOf(selectItem)}
|
scrollToItem={scrollToItem}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
onRendered={onRendered}
|
onRendered={onRendered}
|
||||||
>
|
>
|
||||||
|
@ -164,8 +170,8 @@ function VTree({ data, highlightValue, selectedId, onRendered }: {
|
||||||
return (
|
return (
|
||||||
<Item
|
<Item
|
||||||
hasChild={hasChild}
|
hasChild={hasChild}
|
||||||
isCollapsed={collapseNode.has(item.id)}
|
isCollapsed={collapseNode.includes(item)}
|
||||||
isSelect={selectItem === item.id}
|
isSelect={selectItem === item}
|
||||||
onCollapse={changeCollapseNode}
|
onCollapse={changeCollapseNode}
|
||||||
onClick={handleClickItem}
|
onClick={handleClickItem}
|
||||||
highlightValue={highlightValue}
|
highlightValue={highlightValue}
|
||||||
|
|
|
@ -9,13 +9,39 @@ import { FilterTree } from '../components/FilterTree';
|
||||||
import Close from '../svgs/Close';
|
import Close from '../svgs/Close';
|
||||||
import Arrow from './../svgs/Arrow';
|
import Arrow from './../svgs/Arrow';
|
||||||
|
|
||||||
|
const parseVNodeData = (rawData) => {
|
||||||
|
const idIndentationMap: {
|
||||||
|
[id: string]: number;
|
||||||
|
} = {};
|
||||||
|
const data: IData[] = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < rawData.length) {
|
||||||
|
const id = rawData[i] as string;
|
||||||
|
i++;
|
||||||
|
const name = rawData[i] as string;
|
||||||
|
i++;
|
||||||
|
const parentId = rawData[i] as string;
|
||||||
|
i++;
|
||||||
|
const userKey = rawData[i] as string;
|
||||||
|
i++;
|
||||||
|
const indentation = parentId === '' ? 0 : idIndentationMap[parentId] + 1;
|
||||||
|
idIndentationMap[id] = indentation;
|
||||||
|
const item = {
|
||||||
|
id, name, indentation, userKey
|
||||||
|
};
|
||||||
|
data.push(item);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [parsedVNodeData, setParsedVNodeData] = useState([]);
|
const [parsedVNodeData, setParsedVNodeData] = useState([]);
|
||||||
const [componentInfo, setComponentInfo] = useState({ name: null, attrs: {} });
|
const [componentInfo, setComponentInfo] = useState({ name: null, attrs: {} });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
setParsedVNodeData(mockParsedVNodeData);
|
const parsedData = parseVNodeData(mockParsedVNodeData);
|
||||||
|
setParsedVNodeData(parsedData);
|
||||||
setComponentInfo({
|
setComponentInfo({
|
||||||
name: 'Demo',
|
name: 'Demo',
|
||||||
attrs: {
|
attrs: {
|
||||||
|
@ -25,37 +51,19 @@ function App() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
const idIndentationMap: {
|
|
||||||
[id: string]: number;
|
|
||||||
} = {};
|
|
||||||
const data: IData[] = [];
|
|
||||||
let i = 0;
|
|
||||||
while (i < parsedVNodeData.length) {
|
|
||||||
const id = parsedVNodeData[i] as string;
|
|
||||||
i++;
|
|
||||||
const name = parsedVNodeData[i] as string;
|
|
||||||
i++;
|
|
||||||
const parentId = parsedVNodeData[i] as string;
|
|
||||||
i++;
|
|
||||||
const userKey = parsedVNodeData[i] as string;
|
|
||||||
i++;
|
|
||||||
const indentation = parentId === '' ? 0 : idIndentationMap[parentId] + 1;
|
|
||||||
idIndentationMap[id] = indentation;
|
|
||||||
const item = {
|
|
||||||
id, name, indentation, userKey
|
|
||||||
};
|
|
||||||
data.push(item);
|
|
||||||
}
|
|
||||||
const {
|
const {
|
||||||
filterValue,
|
filterValue,
|
||||||
setFilterValue,
|
onChangeSearchValue: setFilterValue,
|
||||||
onClear,
|
onClear,
|
||||||
selectId,
|
currentItem,
|
||||||
matchItems,
|
matchItems,
|
||||||
onSelectNext,
|
onSelectNext,
|
||||||
onSelectLast,
|
onSelectLast,
|
||||||
setShowItems,
|
setShowItems,
|
||||||
} = FilterTree({ data });
|
collapsedNodes,
|
||||||
|
setcollapsedNodes,
|
||||||
|
} = FilterTree({ data: parsedVNodeData });
|
||||||
|
|
||||||
const handleSearchChange = (str: string) => {
|
const handleSearchChange = (str: string) => {
|
||||||
setFilterValue(str);
|
setFilterValue(str);
|
||||||
|
@ -77,19 +85,21 @@ function App() {
|
||||||
<Search onChange={handleSearchChange} value={filterValue} />
|
<Search onChange={handleSearchChange} value={filterValue} />
|
||||||
</div>
|
</div>
|
||||||
{filterValue !== '' && <>
|
{filterValue !== '' && <>
|
||||||
<span className={styles.searchResult}>{`${matchItems.indexOf(selectId) + 1}/${matchItems.length}`}</span>
|
<span className={styles.searchResult}>{`${matchItems.indexOf(currentItem) + 1}/${matchItems.length}`}</span>
|
||||||
<div className={styles.divider} />
|
<div className={styles.divider} />
|
||||||
<button className={styles.searchAction} onClick={onSelectLast}><Arrow direction={'up'}/></button>
|
<button className={styles.searchAction} onClick={onSelectLast}><Arrow direction={'up'} /></button>
|
||||||
<button className={styles.searchAction} onClick={onSelectNext}><Arrow direction={'down'}/></button>
|
<button className={styles.searchAction} onClick={onSelectNext}><Arrow direction={'down'} /></button>
|
||||||
<button className={styles.searchAction} onClick={onClear}><Close/></button>
|
<button className={styles.searchAction} onClick={onClear}><Close /></button>
|
||||||
</>}
|
</>}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.left_bottom}>
|
<div className={styles.left_bottom}>
|
||||||
<VTree
|
<VTree
|
||||||
data={data}
|
data={parsedVNodeData}
|
||||||
highlightValue={filterValue}
|
highlightValue={filterValue}
|
||||||
onRendered={onRendered}
|
onRendered={onRendered}
|
||||||
selectedId={selectId} />
|
collapsedNodes={collapsedNodes}
|
||||||
|
onCollapseNode={setcollapsedNodes}
|
||||||
|
scrollToItem={currentItem} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.right}>
|
<div className={styles.right}>
|
||||||
|
|
Loading…
Reference in New Issue