feat: fs tree component
This commit is contained in:
1 parent
6c57df23f2
commit
278255efd7
18 files changed
+2427
-94
No files matched your search
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user