Match-id-1ded90506bca2ba868af23776da7f666859a3b06
This commit is contained in:
parent
d490707e78
commit
baf13b7c59
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -2,10 +2,11 @@ import styles from './Search.less';
|
|||
|
||||
interface SearchProps {
|
||||
onChange: (event: any) => void,
|
||||
value: string,
|
||||
}
|
||||
|
||||
export default function Search(props: SearchProps) {
|
||||
const { onChange } = props;
|
||||
const { onChange, value } = props;
|
||||
const handleChange = (event) => {
|
||||
onChange(event.target.value);
|
||||
};
|
||||
|
@ -13,6 +14,7 @@ export default function Search(props: SearchProps) {
|
|||
<input
|
||||
onChange={handleChange}
|
||||
className={styles.search}
|
||||
value={value}
|
||||
placeholder={'Search (text or /regex/)'}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
flex: 0 0 @top-height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 0.4rem;
|
||||
|
||||
.select {
|
||||
padding: 0 0.25rem 0 0.25rem;
|
||||
|
@ -33,6 +34,23 @@
|
|||
.search {
|
||||
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 {
|
||||
|
|
|
@ -5,11 +5,14 @@ import ComponentInfo from '../components/ComponentInfo';
|
|||
import styles from './App.less';
|
||||
import Select from '../svgs/Select';
|
||||
import { mockParsedVNodeData, parsedMockState } from '../devtools/mock';
|
||||
import { FilterTree } from '../components/FilterTree';
|
||||
import Close from '../svgs/Close';
|
||||
import Arrow from './../svgs/Arrow';
|
||||
|
||||
function App() {
|
||||
const [parsedVNodeData, setParsedVNodeData] = useState([]);
|
||||
const [componentInfo, setComponentInfo] = useState({ name: null, attrs: {} });
|
||||
const [filterValue, setFilterValue] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (isDev) {
|
||||
setParsedVNodeData(mockParsedVNodeData);
|
||||
|
@ -43,11 +46,25 @@ function App() {
|
|||
};
|
||||
data.push(item);
|
||||
}
|
||||
const {
|
||||
filterValue,
|
||||
setFilterValue,
|
||||
onClear,
|
||||
selectId,
|
||||
matchItems,
|
||||
onSelectNext,
|
||||
onSelectLast,
|
||||
setShowItems,
|
||||
} = FilterTree({ data });
|
||||
|
||||
const handleSearchChange = (str: string) => {
|
||||
setFilterValue(str);
|
||||
};
|
||||
|
||||
const onRendered = (info) => {
|
||||
setShowItems(info.visibleItems);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.app}>
|
||||
<div className={styles.left}>
|
||||
|
@ -57,11 +74,22 @@ function App() {
|
|||
</div>
|
||||
<div className={styles.divider} />
|
||||
<div className={styles.search}>
|
||||
<Search onChange={handleSearchChange} />
|
||||
<Search onChange={handleSearchChange} value={filterValue} />
|
||||
</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 className={styles.left_bottom}>
|
||||
<VTree data={data} highlightValue={filterValue} />
|
||||
<VTree
|
||||
data={data}
|
||||
highlightValue={filterValue}
|
||||
onRendered={onRendered}
|
||||
selectedId={selectId} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.right}>
|
||||
|
|
Loading…
Reference in New Issue