feat: fs tree component

This commit is contained in:
duanhongbo committed 2020-11-19 18:18:45 +08:00
1 parent 6c57df23f2
commit 278255efd7
18 files changed
+2427 -94

No files matched your search

+157
View File
@@ -0,0 +1,157 @@
import React from "react";
import icon from "../icon";
import request from "../../services/request";
import "./search.css";
interface SearchProp {
basePath: string;
onItemSelected?: Function;
}
interface SearchState {
query: string;
result: { name: string; path: string }[];
showSearchLoader: boolean;
showSearchBox: boolean;
}
class Search extends React.Component<SearchProp, SearchState> {
state: SearchState = {
query: "",
result: [],
showSearchLoader: false,
showSearchBox: false,
};
constructor(props: SearchProp) {
super(props);
}
runDebounce = this.debounce(this.doSearch, 1000);
debounce(fn: Function, delay: number) {
let timer: NodeJS.Timeout;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
}
async doSearch(query: string) {
if (!query) {
this.setState({
query: "",
result: [],
showSearchBox: false,
showSearchLoader: false,
});
return;
}
const dirPath = this.props.basePath;
const result = await request.search(dirPath, query);
this.setState({
query,
result,
showSearchBox: true,
showSearchLoader: false,
});
}
handleChange = (query: string) => {
if (!query) {
this.setState({
query: "",
});
return;
}
this.setState({
query,
showSearchBox: true,
showSearchLoader: true,
});
};
clearSearch = (event: React.KeyboardEvent<HTMLInputElement>) => {
const keyCode = event.which || event.keyCode;
if (keyCode === 27) {
// Clear search
this.setState({
query: "",
result: [],
showSearchBox: false,
});
}
};
showResults = () => {
return (
<div className="item-wrapper search-results">
{this.state.result.map((item) => (
<div
onClick={() => this.props.onItemSelected(item)}
key={item.path}
className="indent mb search-item"
>
<div>
{icon.file(item)} <span>{item.name}</span>
</div>
<div className="search-path">{item.path}</div>
</div>
))}
</div>
);
};
emptyResults = () => {
return (
<div className="item-wrapper search-results empty-results">
<span className="tomato">{icon.close()}</span> No Matching Results
</div>
);
};
loadingResults = () => {
return <div className="item-wrapper search-results empty-results">{icon.loading()}</div>;
};
results = () => {
if (this.state.showSearchLoader) {
return this.loadingResults();
}
if (this.state.result.length > 0) {
return this.showResults();
}
return this.emptyResults();
};
render() {
return (
<div className="search-wrapper">
<span className="search-icon">{icon.search()}</span>
<input
type="text"
className="fa search"
placeholder="Search"
onChange={(event) => {
this.handleChange(event.target.value);
this.runDebounce();
}}
onKeyDown={this.clearSearch}
value={this.state.query}
/>
<div style={{ display: this.state.showSearchBox ? "block" : "none" }}>
{this.results()}
</div>
</div>
);
}
}
export default Search;
+53
View File
@@ -0,0 +1,53 @@
/* Search Styles */
.search-wrapper {
height: 35px;
width: 100%;
padding-bottom: 10px;
position: relative;
}
.search {
width: 100%;
height: 100%;
border: 1px solid rgb(180, 179, 179);
border-radius: 0;
padding-left: 30px;
color: grey;
font-size: 0.85em;
font-weight: 500;
/* border: 1px solid purple; */
}
.search-icon {
position: absolute;
padding-left: 8px;
padding-top: 12px;
color: grey;
font-size: 0.85em;
font-weight: 200;
}
.search-results {
background: #ffffff;
padding-top: 10px;
width: 100%;
min-height: 100vh;
height: auto;
}
.empty-results {
padding-top: 20px;
text-align: center;
font-size: 0.8em;
}
.search-item:hover > div {
background: rgba(0, 0, 0, 0.15);
}
.search-path {
font-size: 10px;
color: #6e6e6e;
padding: 5px 0;
padding-left: 20px;
}