Match-id-1ded90506bca2ba868af23776da7f666859a3b06

This commit is contained in:
* 2022-04-07 11:52:07 +08:00 committed by *
parent d490707e78
commit baf13b7c59
4 changed files with 129 additions and 4 deletions

View File

@ -0,0 +1,77 @@
// 过滤树的抽象逻辑实现
// 需要知道渲染了哪些数据,搜索的字符串
// 控制Tree组件位置跳转告知搜索文本
// 清空搜索框,告知搜索框当前是第几个结果,跳转搜索结果接口
import { useState, useRef } from 'horizon';
import { createRegExp } from '../utils';
export function FilterTree<T extends {
id: string,
name: string
}>(props: { data: T[] }) {
const { data } = props;
const [filterValue, setFilterValue] = useState('');
const [selectId, setSelectId] = useState(null);
const showItems = useRef([]);
const matchItemsRef = useRef([]);
const matchItems = matchItemsRef.current;
const onChangeSearchValue = (search: string) => {
const reg = createRegExp(search);
let matchShowId = null;
let newMatchItems = [];
if (search !== '') {
newMatchItems = data.reduce((pre, current) => {
const { id, name } = current;
if (reg && name.match(reg)) {
pre.push(id);
if (matchShowId === null) {
matchShowId = id;
}
}
return pre;
}, []);
if (newMatchItems.length === 0) {
setSelectId(null);
} else {
if (matchShowId === null) {
setSelectId(newMatchItems[0]);
} else {
setSelectId(matchShowId);
}
}
}
matchItemsRef.current = newMatchItems;
setFilterValue(search);
};
const onSelectNext = () => {
const index = matchItems.indexOf(selectId);
const nextIndex = index + 1;
if (nextIndex < matchItemsRef.current.length) {
setSelectId(matchItems[nextIndex]);
}
};
const onSelectLast = () => {
const index = matchItems.indexOf(selectId);
const last = index - 1;
if (last >= 0) {
setSelectId(matchItems[last]);
}
};
const setShowItems = (items) => {
showItems.current = [...items];
};
const onClear = () => {
onChangeSearchValue('');
};
return {
filterValue,
setFilterValue: onChangeSearchValue,
onClear,
selectId,
matchItems,
onSelectNext,
onSelectLast,
setShowItems,
};
}

View File

@ -2,10 +2,11 @@ import styles from './Search.less';
interface SearchProps { interface SearchProps {
onChange: (event: any) => void, onChange: (event: any) => void,
value: string,
} }
export default function Search(props: SearchProps) { export default function Search(props: SearchProps) {
const { onChange } = props; const { onChange, value } = props;
const handleChange = (event) => { const handleChange = (event) => {
onChange(event.target.value); onChange(event.target.value);
}; };
@ -13,6 +14,7 @@ export default function Search(props: SearchProps) {
<input <input
onChange={handleChange} onChange={handleChange}
className={styles.search} className={styles.search}
value={value}
placeholder={'Search (text or /regex/)'} placeholder={'Search (text or /regex/)'}
/> />
); );

View File

@ -17,6 +17,7 @@
flex: 0 0 @top-height; flex: 0 0 @top-height;
display: flex; display: flex;
align-items: center; align-items: center;
padding-right: 0.4rem;
.select { .select {
padding: 0 0.25rem 0 0.25rem; padding: 0 0.25rem 0 0.25rem;
@ -33,6 +34,23 @@
.search { .search {
flex: 1 1 0; flex: 1 1 0;
} }
.searchResult{
flex: 0 0 ;
padding: 0 0.4rem;
}
.searchAction {
padding: 0;
flex: 0 0 1rem;
border: none;
background: none;
height: 1rem;
color: @arrow-color;
&:hover{
color: @hover-color;
}
}
} }
.left_bottom { .left_bottom {

View File

@ -5,11 +5,14 @@ import ComponentInfo from '../components/ComponentInfo';
import styles from './App.less'; import styles from './App.less';
import Select from '../svgs/Select'; import Select from '../svgs/Select';
import { mockParsedVNodeData, parsedMockState } from '../devtools/mock'; import { mockParsedVNodeData, parsedMockState } from '../devtools/mock';
import { FilterTree } from '../components/FilterTree';
import Close from '../svgs/Close';
import Arrow from './../svgs/Arrow';
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: {} });
const [filterValue, setFilterValue] = useState('');
useEffect(() => { useEffect(() => {
if (isDev) { if (isDev) {
setParsedVNodeData(mockParsedVNodeData); setParsedVNodeData(mockParsedVNodeData);
@ -43,11 +46,25 @@ function App() {
}; };
data.push(item); data.push(item);
} }
const {
filterValue,
setFilterValue,
onClear,
selectId,
matchItems,
onSelectNext,
onSelectLast,
setShowItems,
} = FilterTree({ data });
const handleSearchChange = (str: string) => { const handleSearchChange = (str: string) => {
setFilterValue(str); setFilterValue(str);
}; };
const onRendered = (info) => {
setShowItems(info.visibleItems);
};
return ( return (
<div className={styles.app}> <div className={styles.app}>
<div className={styles.left}> <div className={styles.left}>
@ -57,11 +74,22 @@ function App() {
</div> </div>
<div className={styles.divider} /> <div className={styles.divider} />
<div className={styles.search}> <div className={styles.search}>
<Search onChange={handleSearchChange} /> <Search onChange={handleSearchChange} value={filterValue} />
</div> </div>
{filterValue !== '' && <>
<span className={styles.searchResult}>{`${matchItems.indexOf(selectId) + 1}/${matchItems.length}`}</span>
<div className={styles.divider} />
<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={onClear}><Close/></button>
</>}
</div> </div>
<div className={styles.left_bottom}> <div className={styles.left_bottom}>
<VTree data={data} highlightValue={filterValue} /> <VTree
data={data}
highlightValue={filterValue}
onRendered={onRendered}
selectedId={selectId} />
</div> </div>
</div> </div>
<div className={styles.right}> <div className={styles.right}>