gpt4 book ai didi

javascript - 实现搜索栏按钮和文件树组件

转载 作者:行者123 更新时间:2023-12-01 01:57:58 26 4
gpt4 key购买 nike

在这个应用程序中,我有一个搜索引擎,它应该在文件树内部查找所需的内容。目的是,当搜索某些内容并单击搜索按钮时,文件树将过滤并显示您要查找的内容。

现在,我有一个 console.log() (检查 onSubmitSearch(e)<SearchEngine/> )告诉我当我单击“搜索”按钮时要求搜索哪些内容。唯一缺少的是实际在文件树内部查找的搜索。我该怎么做?

请检查附加的工作片段。

谢谢!

/**** TEXT BOX COMPONENT ****/
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>
);
}
}

/**** SEARCH COMPONENT ****/
class SearchEngine extends React.Component {
constructor(props) {
super(props);
this.state = { value: "" };

this.onInputChange = this.onInputChange.bind(this);
this.onSubmitSearch = this.onSubmitSearch.bind(this);
}

onInputChange(e) {
const content = e.target.value;
this.setState({value: content});
console.log(content);
}

onSubmitSearch(e) { // CONSOLE LOG IS HERE
e.preventDefault();
console.log('A node was submitted: ' + this.state.value);
}

render() {
return (
<div>
<form onSubmit={this.onSubmitSearch}>
<input
className="form-control"
value={this.state.value}
type="text"
onChange={this.onInputChange}
/>
<p>{this.state.value}</p>
<SearchButton />
</form>
</div>
);
}
}

/**** SEARCH BUTTON ****/
class SearchButton extends React.Component {

render() {
return (
<button
type="submit"
value="submit"
bsStyle="danger"> Search
</button>
);
}
}

/**** FILE TREE COMPONENT ****/
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);
}

setActiveNode(name) {
this.setState({ activeNode: name });
this.props.liftStateUp(name);
}

componentWillReceiveProps({ searchTerm }) {
this.setState({ searchTerm });
}

render() {
return (
<div className="padd_top">
{renderTree(
this.props.root || root,
this.setActiveNode,
this.state.activeNode,
null,
this.state.searchTerm
)}
</div>
);
}
}


/**** DIRECTORY ****/
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 searchTerm = this.props.searchTerm.toLowerCase();
const matchNode = node =>
node.contents
? node.contents.filter(matchNode).length !== 0
: node.name.toLowerCase().indexOf(searchTerm) !== -1;
return matchNode(this.props.node);
}

render() {
let node = this.props.node;
const rotate = this.state;

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 onClick={() => this.setState({ rotate: true })}
className={rotate ? "rotate" : ""} />
</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 to 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;
}
};

/**** APP ****/
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;
color: #333333 !important;
}

/** DIRECTORY CSS **/

.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 CSS **/

.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;
}

.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;
}

.btn-danger {
color: #fff !important;
}

.content_box {
font-size: 12px;
white-space: pre-wrap;
border: solid 1px black;
padding: 20px;
color: #9da5ab;
min-height: 250px;
width: 100%;
}

.text_color {
color: #21252b !important;
}

/** arrow animation **/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<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>
<!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">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
</head>

<body>
<div id="app"></div>
</body>

</html>

最佳答案

您的问题是数据传播。您将数据提供给 FileTree 组件,并且您的搜索引擎与 FileTree 组件处于同一级别,并且它无法访问必须过滤的数据。我将数据提升到应用程序组件(SearchEngine 和 FileTree 组件的父级),并将数据传播到 FileTree。我没有将数据传播到 SearchEngine,而是将 onSubmitSearch 事件处理程序提升到 App 组件并将其传播到 SearchEngine,因为即使我将数据提供给 SearcEngine,我也无法在 FileTree 组件上更新它(因为单向数据流)。

// Dummy data set
var root = data[0];

/**** APP ****/
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activeNode: "",
root: root
};

this.onChange = this.onChange.bind(this);
}

liftStateUp = data => {
this.setState({ activeNode: data });
};

onSubmitSearch = (e, search) => {
let tree = JSON.stringify(root); // always search full data tree
tree = JSON.parse(tree); // JSON.stringify and then JSON.parse are
if (!search || search === "") {
// if search is undefined, null or empty, set root to full data tree
this.setState({ root: tree }); // state.root is filtered tree passed to the FileTree component
return;
}
/*uncoment if you need to filter already filtered tree*/
// tree = JSON.stringify(this.state.root);
// tree = JSON.parse(tree);
/**/

// else filter tree
this.setState({
root: this.filterTree(tree, search.toLowerCase())
});
};

filterTree = (data, search) => {
let children = data.contents;

if (!children || !children.length) {
if (!data.name.toLowerCase().includes(search)) {
data.remove = true;
}
} else {
for (let i = children.length - 1; i >= 0; i--) {
this.filterTree(children[i], search);
if (children[i].remove) {
children.splice(i, 1);
}
}
if (!children.length) {
data.remove = true;
}
}

return data;
};

onChange(data) {
this.setState({ searchTerm: data });
}

render() {
return (
<div>
<div className="col-md-12">
<SearchEngine
className="form-control"
onChange={this.onChange}
onSubmitSearch={this.onSubmitSearch}
/>
</div>
<div className="col-md-6">
<FileTree
root={this.state.root}
liftStateUp={this.liftStateUp}
searchTerm={this.state.searchTerm}
/>
</div>
<div className="col-md-6">
<TextBox content={this.state.activeNode} />
</div>
</div>
);
}
}

请注意,App 现在具有 onSubmitSearch 函数,该函数随后会传播到 SearchEngine,并在搜索引擎中使用搜索输入值调用该函数:

/**** SEARCH COMPONENT ****/
class SearchEngine extends React.Component {
constructor(props) {
super(props);
this.state = { value: "" };

this.onInputChange = this.onInputChange.bind(this);
}

onInputChange(e) {
const content = e.target.value;
this.setState({ value: content });
}

render() {
const { onSubmitSearch } = this.props;
const { value } = this.state;
return (
<div>
<form onSubmit={e => onSubmitSearch(e, value)}>
<input
className="form-control"
value={this.state.value}
type="text"
onChange={this.onInputChange}
/>
<p>{this.state.value}</p>
<SearchButton />
</form>
</div>
);
}
}

FileTree 组件现在获取过滤数据(通过搜索输入值/搜索引擎)并仅负责渲染 FileTree 组件。

在此处查看使用提交按钮过滤树结构的工作示例:https://codesandbox.io/s/3rnvv0kln6

关于javascript - 实现搜索栏按钮和文件树组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50860099/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com