diff --git a/src/display/components/searchbar.css b/src/display/components/searchbar.css index 1166144aea9d33e4086609c99f919d4da0b7d801..aa0f1fdf33f940ac561de62e40f7abe7386c5c18 100644 --- a/src/display/components/searchbar.css +++ b/src/display/components/searchbar.css @@ -7,11 +7,44 @@ gap: 10px; } +.searchbar-input[type="search"] { + border-style: hidden; +} + +.searchbar-input[type="search"]:focus { + outline: none; +} + .searchbar-icon { + cursor: pointer; font-size: 30px; } .searchbar-text { overflow: hidden; - transition: width 0.2s ease-in-out; + transition: width 0.25s ease-in-out; +} + +.searchbar-results { + overflow: hidden; +} + +.searchbar-result { + cursor: pointer; + z-index: 99; + pointer-events: all; +} + +.searchbar-result:hover { + color: gray; +} + +.serach-area { + display: flex; + flex-direction: column; + background-color: transparent; + transition: background-color 0.2s ease-in-out; + border-radius: 30px; + padding-left: 10px; + padding-right: 5px; } \ No newline at end of file diff --git a/src/display/components/searchbar.tsx b/src/display/components/searchbar.tsx index b4fce34390c8a9526472182bd0ca7ff15ac36fe4..29114b2b48d7cdcb23a03dfdab7439764181ee56 100644 --- a/src/display/components/searchbar.tsx +++ b/src/display/components/searchbar.tsx @@ -1,17 +1,19 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import "./searchbar.css"; +import { NodeData } from "../graph"; interface SearchBarProps { minified: boolean; - searchTerms: string[]; - onSearch?: (term: string) => void; + nodeSet: NodeData[]; + onSearch?: (node: NodeData) => void; } -function SearchBar({ minified, searchTerms, onSearch }: SearchBarProps) { +function SearchBar({ minified, nodeSet, onSearch }: SearchBarProps) { const [isMinified, setMinified] = useState(minified); - const [width, setWidth] = useState<number>(100); + const [width, setWidth] = useState<number>(minified ? 0 : 100); const [inputText, setInputText] = useState(""); + const inputField = useRef<HTMLInputElement>(); const toggleMinified = () => { setMinified((prev) => !prev); @@ -19,9 +21,12 @@ function SearchBar({ minified, searchTerms, onSearch }: SearchBarProps) { useEffect(() => { if (isMinified) { - setWidth(100); - } else { setWidth(0); + setInputText(""); + inputField.current.value = ""; + } else { + setWidth(100); + inputField.current.focus(); } }, [isMinified]); @@ -29,19 +34,67 @@ function SearchBar({ minified, searchTerms, onSearch }: SearchBarProps) { setInputText(e.target.value.toLowerCase()); }; + const labels = nodeSet.map((node) => node.name); + const nodeLoopup = new Map<string, NodeData>(); + labels.forEach((label, i) => nodeLoopup.set(label, nodeSet[i])); + + const filtered = labels + .filter((el) => { + if (inputText !== "") { + return el.toLowerCase().includes(inputText); + } + }) + .slice(0, 3); + + const handleNodeClick = (node: string) => { + if (onSearch !== undefined) { + onSearch(nodeLoopup.get(node)); + } + }; + return ( - <div className={"searchbar"}> - <div className={"searchbar-icon"} onClick={toggleMinified}> - 🔍 + <div + className={"serach-area"} + style={{ backgroundColor: isMinified ? "" : "white" }} + > + <div className={"searchbar"}> + <div className={"searchbar-icon"} onClick={toggleMinified}> + 🔍 + </div> + <div + className={"searchbar-text"} + style={{ width: width + "%" }} + > + <form action={"#"}> + <input + ref={inputField} + className={"searchbar-input"} + type={"search"} + placeholder={"Knoten suchen..."} + onInput={handleInput} + onBlur={toggleMinified} + ></input> + </form> + </div> </div> - <div className={"searchbar-text"} style={{ width: width + "%" }}> - <form action={"#"}> - <input - type={"search"} - placeholder={"Suchbegriff eingeben..."} - onInput={handleInput} - ></input> - </form> + <div + className={"searchbar-results"} + style={{ + height: inputText.length > 0 ? filtered.length * 30 : 0, + marginBottom: filtered.length > 0 ? 10 : 0, + }} + > + <ul> + {filtered.map((el) => ( + <li + key={el} + className={"searchbar-result"} + onMouseDown={() => handleNodeClick(el)} + > + {el} + </li> + ))} + </ul> </div> </div> ); diff --git a/src/display/display.tsx b/src/display/display.tsx index 7b3c83b37cb423c4921fc98bfe40148d9788a322..bb220f2b8bbc7b61d46f275437bf95fce1c2e913 100644 --- a/src/display/display.tsx +++ b/src/display/display.tsx @@ -10,6 +10,7 @@ import { loadGraphJson } from "../datasets"; import NodeInfoBar from "./components/nodeinfo/nodeinfobar"; import FilterMenu from "./components/nodefilter/filtermenu"; import SearchBar from "./components/searchbar"; +import { graph } from "../editor/js/editor"; /** * This component manages and renders a 3d-force-graph with additional menus to navigate, filter and view information on nodes. @@ -74,6 +75,7 @@ class Display extends React.Component< handleNodeChangeRequest(node: NodeData) { this.rendererRef.current.focusOnNode(node as GraphNode); + this.rendererRef.current.displayNodeSelection(node as GraphNode); this.handleNodeClicked(node); } @@ -121,7 +123,13 @@ class Display extends React.Component< > ⟷ </div> - <SearchBar minified={true} searchTerms={["a", "b", "c"]} /> + {this.state.graph && ( + <SearchBar + minified={true} + nodeSet={this.state.graph.nodes} + onSearch={this.handleNodeChangeRequest} + /> + )} </div> {/*{this.graph && (*/}