我在这里开发了一个搜索输入:https://codesandbox.io/s/k9448q2v87可以工作,但不能正常工作。
输入可以在主文件夹(称为'.')和子文件夹(/bin、/lib、/spec)中找到文件,但在这些子文件夹和主文件夹E 210中找不到文件。
例如,如果您试图搜索01_greeting_spec.rb或greeting.rb,,搜索将不会正确筛选。
但是,如果您搜索一个像/bin这样的文件夹,则输入将过滤该文件夹和其中的所有文件。
,那么,少了什么?
考虑这个问题所附的Stack片段。
class TextBox extends React.Component {
constructor(props) {
super(props);
this.state = {
content: "Select A Node To See Its Data Structure Here..."
};
this.changeContent = this.changeContent.bind(this);
}
changeContent(newContent) {
this.setState({
content: newContent
});
}
componentWillReceiveProps(nextProps) {
this.setState({
content: nextProps.content
});
}
render() {
return (
<div className="padd_top">
<div className="content_box">{this.state.content}</div>
</div>
);
}
}
class SearchEngine extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ""
};
this.inputChange = this.inputChange.bind(this);
}
inputChange(e) {
const content = e.target.value;
this.props.onChange(content);
}
render() {
return (
<input className="form-control" placeholder="Search the tree..." onChange={this.inputChange} />
);
}
}
let data = [
{
type: "directory",
name: ".",
contents: [
{
type: "directory",
name: "./bin",
contents: [{ type: "file", name: "./bin/greet" }]
},
{
type: "directory",
name: "./lib",
contents: [{ type: "file", name: "./lib/greeting.rb" }]
},
{
type: "directory",
name: "./spec",
contents: [
{ type: "file", name: "./spec/01_greeting_spec.rb" },
{ type: "file", name: "./spec/02_cli_spec.rb" },
{ type: "file", name: "./spec/spec_helper.rb" }
]
},
{ type: "file", name: "./CONTRIBUTING.md" },
{ type: "file", name: "./Gemfile" },
{ type: "file", name: "./Gemfile.lock" },
{ type: "file", name: "./LICENSE.md" },
{ type: "file", name: "./README.md" }
]
}
];
// Icon file image for 'FileTree'
const FileIcon = () => {
return (
<div className="svg-icon">
<svg
id="icon-file-text2"
className="icon"
viewBox="0 0 32 32"
fill="currentColor"
width="1em"
height="1em"
>
<path d="M28.681 7.159c-0.694-0.947-1.662-2.053-2.724-3.116s-2.169-2.030-3.116-2.724c-1.612-1.182-2.393-1.319-2.841-1.319h-15.5c-1.378 0-2.5 1.121-2.5 2.5v27c0 1.378 1.122 2.5 2.5 2.5h23c1.378 0 2.5-1.122 2.5-2.5v-19.5c0-0.448-0.137-1.23-1.319-2.841zM24.543 5.457c0.959 0.959 1.712 1.825 2.268 2.543h-4.811v-4.811c0.718 0.556 1.584 1.309 2.543 2.268zM28 29.5c0 0.271-0.229 0.5-0.5 0.5h-23c-0.271 0-0.5-0.229-0.5-0.5v-27c0-0.271 0.229-0.5 0.5-0.5 0 0 15.499-0 15.5 0v7c0 0.552 0.448 1 1 1h7v19.5z" />
<path d="M23 26h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z" />
<path d="M23 22h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z" />
<path d="M23 18h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z" />
</svg>
</div>
);
};
// Icon folder image for 'FileTree'
const FolderIcon = () => {
return (
<div className="svg-icon">
<svg
id="icon-folder"
className="icon"
viewBox="0 0 32 32"
fill="currentColor"
height="1em"
width="1em"
>
<path d="M14 4l4 4h14v22h-32v-26z" />
</svg>
</div>
);
};
// Icon arrow image for 'FileTree'
const TriangleDown = () => {
return (
<div className="svg-icon">
<svg
id="svg__icon--triangle-down"
viewBox="0 0 9 4.5"
fill="currentColor"
height="1em"
width="1em"
>
<path d="M0,0,4.5,4.5,9,0Z" />
</svg>
</div>
);
};
// Filters file 'name' and adds '/'
const formatName = name => {
return name.substr(name.lastIndexOf("/") + 1);
};
// Dummy data set
var root = data[0];
// Construction of FileTree
class FileTree extends React.Component {
constructor(props) {
super(props);
this.state = {
activeNode: null
};
this.setActiveNode = this.setActiveNode.bind(this);
}
componentWillReceiveProps({ searchTerm }) {
this.setState({ searchTerm });
}
setActiveNode(name) {
this.setState({ activeNode: name });
this.props.liftStateUp(name);
}
render() {
return (
<div className="padd_top">
{renderTree(
this.props.root || root,
this.setActiveNode,
this.state.activeNode,
null,
this.state.searchTerm
)}
</div>
);
}
}
class Directory extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: true,
};
this.toggleDirectory = this.toggleDirectory.bind(this);
}
toggleDirectory() {
this.setState({ expanded: !this.state.expanded });
}
hasMatchingNodes() {
const filteredNodes = this.props.node.contents.filter(
(node) => {
return (
node.name.toLowerCase().indexOf(this.props.searchTerm.toLowerCase()) >
-1
);
});
console.log(this.props.searchTerm);
return filteredNodes.length > 0;
}
render() {
let node = this.props.node;
if (this.props.searchTerm && !this.hasMatchingNodes()) return null;
return (
<div className="directory-container">
<div className="directory">
<div
className=
{`directory__toggle ${ this.state.expanded ? "expanded" : ""}`}
>
<div onClick={this.toggleDirectory}>
<TriangleDown />
</div>
</div>
<div className="directory__icon" onClick={this.toggleDirectory}>
<FolderIcon />
</div>
<div className="directory__name" onClick={this.toggleDirectory}>
<div>{formatName(node.name)}</div>
</div>
</div>
{this.state.expanded
? node.contents.map((content, index) =>
renderTree(
content,
this.props.setActiveNode,
this.props.activeNode,
index,
this.props.searchTerm
)
)
: ""}
</div>
);
}
}
// Set class Active do selected file
const File = ({ name, setActiveNode, activeNode, searchTerm }) => {
if (searchTerm && name.toLowerCase().indexOf(searchTerm.toLowerCase()) < 0)
return null;
let isActive = activeNode === name;
let className = isActive ? "active" : "";
return (
<div className={className + " file"} onClick={() => setActiveNode(name)}>
<div className="file__icon">
<FileIcon />
</div>
<div className="file__name">{formatName(name)}</div>
{isActive && <div className="file__options">...</div>}
</div>
);
};
var renderTree = (node, setActiveNode, activeNode, index, searchTerm) => {
if (node.type === "file") {
return (
<File
key={index}
name={node.name}
setActiveNode={setActiveNode}
activeNode={activeNode}
searchTerm={searchTerm}
/>
);
} else if (node.type === "directory") {
return (
<Directory
key={index}
node={node}
setActiveNode={setActiveNode}
activeNode={activeNode}
searchTerm={searchTerm}
/>
);
} else {
return null;
}
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activeNode: ""
};
this.onChange = this.onChange.bind(this);
}
liftStateUp = (data) => {
this.setState({ activeNode: data });
};
onChange(data) {
this.setState({ searchTerm: data });
}
render() {
return (
<div>
<div className="col-md-12">
<SearchEngine className="form-control" onChange={this.onChange} />
</div>
<div className="col-md-6">
<FileTree
liftStateUp={this.liftStateUp}
searchTerm={this.state.searchTerm}
/>
</div>
<div className="col-md-6">
<TextBox content={this.state.activeNode} />
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));* {
font-family: Helvetica;
background-color: #212830;
color: #9ea1b2 !important;
}
.directory {
padding-left: 10px;
padding-top: 1px;
padding-bottom: 1px;
display: flex;
flex-direction: row;
align-items: center;
}
.directory__toggle {
padding-left: 10px;
transform: rotate(-90deg);
}
.directory__icon {
padding-left: 10px;
}
.directory__icon {
padding-left: 10px;
}
.directory__name {
padding-left: 10px;
}
.directory-container {
padding-left: 10px;
}
.file {
padding-left: 50px;
padding-top: 1px;
padding-bottom: 1px;
display: flex;
}
.file__icon {
padding-left: 10px;
}
.file__name {
padding-left: 10px;
}
.file__options {
align-self: flex-end;
}
.active {
color: #ffffff;
background-color: #31343f;
}
.icon {
display: inline-block;
width: 1em;
height: 1em;
stroke-width: 0;
stroke: currentColor;
fill: currentColor;
}
.svg-icon {
width: 1em;
height: 1em;
}
.expanded {
transform: rotate(0deg);
}
/** CONTENT BOX **/
.padd_top {
padding-top: 20px;
}
.content_box {
font-size: 12px;
white-space: pre-wrap;
background-color: #282c34;
border: solid 1px black;
padding: 20px;
color: #9da5ab;
min-height: 250px;
margin-bottom: 15px;
}
.text_color {
color: #21252b !important;
}
/** SEARCH **/
.form-control {
margin-top: 15px;
}
/** ARROW ANIMATION **/
.rotate {
animation: rotate-keyframes 1s;
}
@keyframes rotate-keyframes {
from {
transform: rotate(-90deg);
}
to {
transform: rotate(0deg);
}
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="app"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</body>
</html>
谢谢你的帮助。
发布于 2018-04-30 17:53:35
您的hasMatchingNodes方法不递归地检查子目录。它只检查根目录.的直接子目录的文件和文件夹的名称。
您应该测试文件节点的名称,如果节点是目录,则递归调用匹配函数,如果节点是文件,则将搜索术语与节点名称进行比较。
hasMatchingNodes() {
const searchTerm = this.props.searchTerm.toLowerCase();
const matchNode = node =>
node.contents
? node.contents.some(matchNode)
: node.name.toLowerCase().includes(searchTerm);
return matchNode(this.props.node);
} 请参阅此处更新的应用程序:https://codesandbox.io/s/245nv8n3j
https://stackoverflow.com/questions/50105097
复制相似问题