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 {
|
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/)'}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
Loading…
Reference in New Issue