Skip to content
Snippets Groups Projects
Commit b45b2dcb authored by Vink, S.A. (Sjoerd)'s avatar Vink, S.A. (Sjoerd)
Browse files

feat(visManager): moved search to sidebar

parent b0a7d68d
No related branches found
No related tags found
2 merge requests!135geo intergation,!129Feat/visManager
Pipeline #131230 passed
......@@ -12,7 +12,6 @@ import React, { useState, useRef, useEffect } from 'react';
import logo_white from './gp-logo-white.svg';
import logo from './gp-logo.svg';
import { useAuthorizationCache, useAuth } from '@graphpolaris/shared/lib/data-access';
import { SearchBar } from './search/SearchBar';
import DatabaseSelector from './DatabaseManagement/dbConnectionSelector';
import { DropdownItem, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns';
import ColorMode from '@graphpolaris/shared/lib/components/color-mode';
......
......@@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { Button } from '../components';
import { Addchart, Schema as SchemaIcon, Search as SearchIcon } from '@mui/icons-material';
import Schema from '../schema/panel';
import Search from './search';
import Searchbar from './search/searchbar';
import Settings from './settings';
export default function InfoPanel({ auth, manager }: { auth: boolean; manager: any }) {
......@@ -40,7 +40,7 @@ export default function InfoPanel({ auth, manager }: { auth: boolean; manager: a
<div className="relative flex items-center justify-between z-[2] py-0 px-2 bg-secondary-100 border-b border-secondary-200">
<h1 className="text-xs font-semibold text-secondary-800">{tab}</h1>
</div>
{tab === 'Search' && <Search />}
{tab === 'Search' && <Searchbar />}
{tab === 'Schema' && <Schema auth={auth} />}
{tab === 'Visualization' && <Settings manager={manager} />}
</div>
......
import React from 'react';
type Props = {};
export default function Search({}: Props) {
return <div>Search to be implemented here</div>;
}
import React from 'react';
import React, { useRef, useState, useEffect } from 'react';
import {
AppDispatch,
useAppDispatch,
useGraphQueryResult,
useSchemaGraph,
useQuerybuilderGraph,
useSearchResult,
AppDispatch,
useRecentSearches,
} from '@graphpolaris/shared/lib/data-access';
import { filterData } from './similarity';
import { Search as SearchIcon } from '@mui/icons-material';
useSchemaGraph,
useSearchResult,
} from '../../data-access';
import { QueryMultiGraph } from '../../querybuilder';
import {
addSearchResultData,
addSearchResultSchema,
addSearchResultQueryBuilder,
CATEGORY_KEYS,
addRecentSearch,
} from '@graphpolaris/shared/lib/data-access/store/searchResultSlice';
import { QueryMultiGraph } from '@graphpolaris/shared/lib/querybuilder/model/graphology/utils';
addSearchResultData,
addSearchResultQueryBuilder,
addSearchResultSchema,
} from '../../data-access/store/searchResultSlice';
import { filterData } from './similarity';
const SIMILARITY_THRESHOLD = 0.7;
......@@ -37,17 +36,16 @@ const CATEGORY_ACTIONS: {
const SEARCH_CATEGORIES: CATEGORY_KEYS[] = Object.keys(CATEGORY_ACTIONS) as CATEGORY_KEYS[];
export function SearchBar({}) {
const inputRef = React.useRef<HTMLInputElement>(null);
const searchbarRef = React.useRef<HTMLDivElement>(null);
export default function Searchbar() {
const inputRef = useRef<HTMLInputElement>(null);
const [search, setSearch] = useState<string>('');
const searchbarRef = useRef<HTMLDivElement>(null);
const dispatch = useAppDispatch();
const results = useSearchResult();
const recentSearches = useRecentSearches();
const schema = useSchemaGraph();
const graphData = useGraphQueryResult();
const querybuilderData = useQuerybuilderGraph();
const [search, setSearch] = React.useState<string>('');
const [searchOpen, setSearchOpen] = React.useState<boolean>(false);
const dataSources: {
[key: string]: { nodes: Record<string, any>[]; edges: Record<string, any>[] };
......@@ -57,59 +55,19 @@ export function SearchBar({}) {
querybuilder: querybuilderData as QueryMultiGraph,
};
const toggleSearch = () => {
setSearchOpen(true);
if (!searchOpen && inputRef.current) {
inputRef.current.focus();
}
};
React.useEffect(() => {
useEffect(() => {
const handleKeyPress = (event: KeyboardEvent) => {
if (event.key === 'Enter') {
if (searchOpen && search !== '') {
if (search !== '') {
dispatch(addRecentSearch(search));
}
}
};
window.addEventListener('keydown', handleKeyPress);
return () => window.removeEventListener('keydown', handleKeyPress);
}, [searchOpen, search]);
React.useEffect(() => {
const handleKeyPress = (event: KeyboardEvent) => {
if (event.key === '/') {
if (!searchOpen) {
setSearchOpen(true);
setSearch('');
}
}
};
window.addEventListener('keydown', handleKeyPress);
return () => window.removeEventListener('keydown', handleKeyPress);
}, [searchOpen]);
React.useEffect(() => {
if (searchOpen && inputRef.current) {
inputRef.current.focus();
setSearch('');
}
}, [searchOpen]);
React.useEffect(() => {
const handleKeyPress = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
if (searchOpen) {
setSearchOpen(false);
setSearch('');
}
}
};
window.addEventListener('keydown', handleKeyPress);
return () => window.removeEventListener('keydown', handleKeyPress);
}, [searchOpen]);
}, [search]);
React.useEffect(() => {
useEffect(() => {
handleSearch();
}, [search]);
......@@ -148,109 +106,74 @@ export function SearchBar({}) {
}
};
React.useEffect(() => {
const handleClickOutside = ({ target }: MouseEvent) => {
if (inputRef.current && target && !inputRef.current.contains(target as Node) && !searchbarRef?.current?.contains(target as Node)) {
setSearch('');
setSearchOpen(false);
}
};
document.addEventListener('click', handleClickOutside);
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, []);
return (
<div className="searchbar">
{searchOpen && <div className="fixed inset-0 bg-black bg-opacity-50 z-40"></div>}
<div className="mr-2" ref={searchbarRef}>
<div
className="flex items-center border border-secondary-300 hover:bg-secondary-50 px-2 rounded text-sm w-44 h-8 text-secondary-900 cursor-pointer"
onClick={toggleSearch}
>
<SearchIcon />
<span className="ml-1 text-secondary-900">
Type <span className="border border-secondary-900 rounded px-1">/</span> to search
</span>
</div>
{searchOpen && (
<div className="fixed flex flex-col left-1/2 -translate-x-1/2 w-9/12 max-h-1/2 top-2 items-start justify-center z-50 bg-secondary-200 rounded">
<div className="p-3 w-full">
{/* <label className="sr-only">Search</label> */}
<div className="relative">
<div className="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<SearchIcon className="text-secondary500" />
</div>
<input
type="text"
ref={inputRef}
value={search}
onChange={(e) => setSearch(e.target.value)}
id="input-group-search"
className="block w-full p-2 ps-10 text-sm text-secondary-900 border border-secondary-300 rounded bg-secondary-50 focus:ring-blue-500 focus:border-blue-500 focus:ring-0"
placeholder="Search database"
></input>
</div>
</div>
{recentSearches.length !== 0 && (
<div className="px-3 pb-3">
<p className="text-sm">Recent searches</p>
{recentSearches.slice(0, 3).map((term) => (
<p key={term} className="ml-1 text-sm text-secondary-500 cursor-pointer" onClick={() => setSearch(term)}>
{term}
</p>
))}
<div className="flex flex-col w-full p-2">
<div className="w-full">
<input
type="text"
ref={inputRef}
value={search}
onChange={(e) => setSearch(e.target.value)}
id="input-group-search"
className="block w-full p-2 ps-10 text-sm text-secondary-900 border border-secondary-300 rounded bg-secondary-50 focus:ring-blue-500 focus:border-blue-500 focus:ring-0"
placeholder="Search database"
></input>
</div>
<div>
{recentSearches.length !== 0 && (
<div className="px-3 pb-3">
<p className="text-sm">Recent searches</p>
{recentSearches.slice(0, 3).map((term) => (
<p key={term} className="ml-1 text-sm text-secondary-500 cursor-pointer" onClick={() => setSearch(term)}>
{term}
</p>
))}
</div>
)}
{search !== '' && (
<div className="z-10 rounded card-bordered w-full overflow-auto max-h-[60vh] px-3 pb-3">
<p className="font-bold text-sm">Results</p>
{SEARCH_CATEGORIES.every((category) => results[category].nodes.length === 0 && results[category].edges.length === 0) ? (
<div className="ml-1 text-sm">
<p className="text-secondary-500">Found no matches...</p>
</div>
)}
{search !== '' && (
<div className="z-10 rounded card-bordered w-full overflow-auto max-h-[60vh] px-3 pb-3">
<p className="font-bold text-sm">Results</p>
{SEARCH_CATEGORIES.every((category) => results[category].nodes.length === 0 && results[category].edges.length === 0) ? (
<div className="ml-1 text-sm">
<p className="text-secondary-500">Found no matches...</p>
</div>
) : (
SEARCH_CATEGORIES.map((category, index) => {
if (results[category].nodes.length > 0 || results[category].edges.length > 0) {
return (
<div key={index}>
<div className="flex justify-between p-2 text-lg">
<p className="font-bold text-sm">{category.charAt(0).toUpperCase() + category.slice(1)}</p>
<p className="font-bold text-sm">{results[category].nodes.length + results[category].edges.length} results</p>
) : (
SEARCH_CATEGORIES.map((category, index) => {
if (results[category].nodes.length > 0 || results[category].edges.length > 0) {
return (
<div key={index}>
<div className="flex justify-between p-2 text-lg">
<p className="font-bold text-sm">{category.charAt(0).toUpperCase() + category.slice(1)}</p>
<p className="font-bold text-sm">{results[category].nodes.length + results[category].edges.length} results</p>
</div>
<div className="h-[1px] w-full bg-secondary-200"></div>
{Object.values(Object.values(results[category]))
.flat()
.map((item, index) => (
<div
key={index}
className="flex flex-col hover:bg-secondary-300 px-2 py-1 cursor-pointer rounded ml-2"
title={JSON.stringify(item)}
onClick={() => {
CATEGORY_ACTIONS[category](
{
nodes: results[category].nodes.includes(item) ? [item] : [],
edges: results[category].edges.includes(item) ? [item] : [],
},
dispatch,
);
}}
>
<div className="font-bold text-sm">
{item?.key?.slice(0, 18) || item?.id?.slice(0, 18) || Object.values(item)?.[0]?.slice(0, 18)}
</div>
<div className="font-light text-secondary-800 text-xs">{JSON.stringify(item).substring(0, 40)}...</div>
</div>
<div className="h-[1px] w-full bg-secondary-200"></div>
{Object.values(Object.values(results[category]))
.flat()
.map((item, index) => (
<div
key={index}
className="flex flex-col hover:bg-secondary-300 px-2 py-1 cursor-pointer rounded ml-2"
title={JSON.stringify(item)}
onClick={() => {
CATEGORY_ACTIONS[category](
{
nodes: results[category].nodes.includes(item) ? [item] : [],
edges: results[category].edges.includes(item) ? [item] : [],
},
dispatch,
);
}}
>
<div className="font-bold text-sm">
{item?.key?.slice(0, 18) || item?.id?.slice(0, 18) || Object.values(item)?.[0]?.slice(0, 18)}
</div>
<div className="font-light text-secondary-800 text-xs">{JSON.stringify(item).substring(0, 40)}...</div>
</div>
))}
</div>
);
} else return <></>;
})
)}
</div>
))}
</div>
);
} else return <></>;
})
)}
</div>
)}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment