Skip to content

Commit

Permalink
implement fulltext search
Browse files Browse the repository at this point in the history
Re introduce fulltext search
  • Loading branch information
bilalesi authored May 28, 2024
2 parents 8e3fb63 + 564267e commit b2763bd
Show file tree
Hide file tree
Showing 10 changed files with 539 additions and 59 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"rc-tween-one": "^2.7.3",
"react": "18.3",
"react-beautiful-dnd": "^13.1.0",
"react-cmdk": "^1.3.9",
"react-codemirror2": "^7.2.1",
"react-device-detect": "^2.2.3",
"react-dom": "^18.2.0",
Expand Down
2 changes: 2 additions & 0 deletions src/shared/canvas/MyData/MyData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const HomeMyData: React.FC<{}> = () => {
(state: RootState) => state.auth.identities?.data?.identities
);
const issuerUri = identities?.find(item => item['@type'] === 'User')?.['@id'];

const [
{
dateField,
Expand Down Expand Up @@ -80,6 +81,7 @@ const HomeMyData: React.FC<{}> = () => {
const order = sort.join('-');
const resourceTypes = types?.map(item => get(item, 'value'));
const { data: resources, isLoading } = useQuery({
enabled: !!issuerUri,
queryKey: [
'my-data-resources',
{
Expand Down
111 changes: 111 additions & 0 deletions src/shared/components/FullTextSearch/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { useEffect } from 'react';
import { Link } from 'react-router-dom';
import CommandPalette from 'react-cmdk';
import { Spin, Tag, Empty } from 'antd';
import { LoadingOutlined } from '@ant-design/icons';

import { getNormalizedTypes, getResourceLabel } from 'shared/utils';
import { useFullTextSearch } from './useFullTextSearch';

import 'react-cmdk/dist/cmdk.css';
import './styles.scss';

type Props = {
openCmdk: boolean;
onOpenCmdk(): void;
};

const TagRenderer = ({ type }: { type?: string | string[] }) => {
if (!type) return null;
const types = getNormalizedTypes(type);
return (
<div className="type-tags-list">
{types.map(t => (
<Tag color="blue" className="tag" title={t}>
{t}
</Tag>
))}
</div>
);
};

const FullTextSearch = ({ openCmdk, onOpenCmdk }: Props) => {
const {
search,
onSearch,
resetSearch,
searchResults,
isLoading,
} = useFullTextSearch();

const onChangeOpen = () => {
onOpenCmdk();
resetSearch();
};

let content = null;

if (isLoading) {
content = (
<div className="search-resources-loader">
<Spin size="large" indicator={<LoadingOutlined />} />
</div>
);
} else if (!Boolean(searchResults.length) && !Boolean(search)) {
content = <div className="search-for-placeholder">Search for ""</div>;
} else if (!Boolean(searchResults.length) && Boolean(search)) {
content = <Empty description="No results found" />;
} else {
content = searchResults.map(({ id, title, items }) => (
<div className="cmdk-list-container" key={id}>
<div className="cmdk-list-title">
<Tag color="geekblue">{title?.orgLabel}</Tag>|
<Tag color="geekblue">{title?.projectLabel}</Tag>
</div>
<div className="cmdk-list-content">
{items.map(resource => {
const label = getResourceLabel(resource);
return (
<Link
to={`/resolve/${encodeURIComponent(resource['@id'])}`}
onClick={onOpenCmdk}
className="cmdk-list-item"
>
<div className="item-title">{label}</div>
<TagRenderer type={resource['@type']} />
</Link>
);
})}
</div>
</div>
));
}

useEffect(() => {
const keyDown = (e: KeyboardEvent) => {
if (e.ctrlKey && e.key === 'e') {
e.preventDefault();
e.stopPropagation();
onOpenCmdk();
}
};

document.addEventListener('keydown', keyDown);
return () => document.removeEventListener('keydown', keyDown);
}, []);

return (
<CommandPalette
onChangeSearch={onSearch}
onChangeOpen={onChangeOpen}
search={search}
isOpen={openCmdk}
page="root"
placeholder="Search for resources"
>
{content}
</CommandPalette>
);
};

export default FullTextSearch;
98 changes: 98 additions & 0 deletions src/shared/components/FullTextSearch/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
@import '../../lib.scss';

.cmdk-list-container {
padding: 10px;
margin-top: 0 !important;
}
.cmdk-list-title {
text-transform: uppercase !important;
font-weight: bold !important;
margin-bottom: 10px !important;
color: $fusion-daybreak-7;
display: flex;
align-items: center;
gap: 5px;

.ant-tag {
padding: 2px 4px;
}
}

.cmdk-list-content {
display: flex;
flex-direction: column;
gap: 3px;
}

.cmdk-list-item {
padding: 10px 8px;
margin-left: 5px;
border-radius: 4px;
font-size: 16px;
color: #333;
border: 1px solid #efefef;

&:hover {
background: $fusion-blue-1 !important;
box-shadow: 0 2px 12px 0px rgba($color: #333, $alpha: 0.14);
}
}

.type-tags-list {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 3px;
margin: 4px 0px;
.tag {
padding: 2px 3px;
}
}

.item-creation_date {
font-size: 12px;
}

.search-resources-loader {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-top: 10px;
padding-bottom: 10px;
}

.search-for-placeholder {
background-color: $fusion-neutral-2;
padding: 10px;
color: #333;
user-select: none;
border-radius: 4px;
}

@media (prefers-color-scheme: dark) {
.cmdk-list-item {
background: white;
color: black;

&:hover {
background: #efefef !important;
box-shadow: 0 2px 12px 0px rgba($color: white, $alpha: 0.14);
color: black;
.item-title {
color: #333;
}
}
}

.search-for-placeholder {
background-color: white;
color: #101827;
}

.ant-empty {
.ant-empty-description {
color: white;
}
}
}
44 changes: 44 additions & 0 deletions src/shared/components/FullTextSearch/useFullTextSearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useState } from 'react';
import { useNexusContext } from '@bbp/react-nexus';
import { useQuery } from 'react-query';
import { getOrgAndProjectFromProjectId } from 'shared/utils';
import { groupBy } from 'lodash';

export function useFullTextSearch() {
const [search, setSearch] = useState('');
const nexus = useNexusContext();

const onSearch = (value: string) => setSearch(value);
const resetSearch = () => setSearch('');

const { isLoading, data } = useQuery({
enabled: !!search,
queryKey: ['cmdk-search', { search }],
queryFn: () =>
nexus.Resource.list(undefined, undefined, {
q: search,
deprecated: false,
}),
select: data => data._results,
staleTime: 2,
});

const resources = groupBy(data, '_project');

const searchResults = Object.entries(resources).map(([key, value]) => {
const orgProject = getOrgAndProjectFromProjectId(key);
return {
id: key,
title: orgProject,
items: value,
};
});

return {
search,
onSearch,
resetSearch,
isLoading,
searchResults,
};
}
34 changes: 34 additions & 0 deletions src/shared/components/Header/Header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,37 @@
display: none;
}
}

.cmdk-shortcut {
margin: 5px;
border: 1px solid #ffffff;
background: transparent;
padding: 4px 10px;
cursor: pointer;
border-radius: 4px;
color: #efefef;
display: flex;
gap: 2px;
align-items: center;
justify-content: center;
min-width: max-content;
position: relative;
background-color: white;
color: #33333385;

&-input {
position: absolute;
inset: 0;
}
&-btn {
position: relative;
width: max-content;
min-width: max-content;
}
kbd {
box-shadow: none;
background-color: transparent;
margin-left: 2px;
margin-right: 2px;
}
}
Loading

0 comments on commit b2763bd

Please sign in to comment.