Skip to content

Commit

Permalink
added search panel
Browse files Browse the repository at this point in the history
  • Loading branch information
ensaremirerol committed Dec 26, 2024
1 parent 0ba37b9 commit b7e98d8
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { XYNodeTypes } from '@/pages/mapping_page/components/MainPanel/types';
import useMappingPage from '@/pages/mapping_page/state';
import {
Card,
CardList,
FormGroup,
Icon,
InputGroup,
NonIdealState,
} from '@blueprintjs/core';
import { useNodes, useReactFlow, useStoreApi } from '@xyflow/react';
import { useCallback, useMemo, useState } from 'react';

const SearchPanel = () => {
const [searchValue, setSearchValue] = useState<string>('');
const [searchBounceBack, setSearchBounceBack] = useState<ReturnType<
typeof setTimeout
> | null>(null);
const nodes = useNodes<XYNodeTypes>();
const store = useStoreApi();
const { setNodes } = store.getState();
const reactflow = useReactFlow();
const setSelectedTab = useMappingPage(state => state.setSelectedTab);

const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
if (searchBounceBack) {
clearTimeout(searchBounceBack);
}
const timeout = setTimeout(() => {
setSearchValue(e.target.value);
}, 300);

setSearchBounceBack(timeout);
};

const searchResults = useMemo<XYNodeTypes[] | null>(() => {
if (!searchValue) {
return null;
}

return nodes.filter(node => {
switch (node.type) {
case 'literal':
case 'entity':
return node.data.label
.toLowerCase()
.includes(searchValue.toLowerCase());

case 'uri_ref':
return node.data.uri_pattern
.toLowerCase()
.includes(searchValue.toLowerCase());
}
});
}, [nodes, searchValue]);

const getLabelFromNode = useCallback((node: XYNodeTypes) => {
switch (node.type) {
case 'entity':
case 'literal':
return node.data.label;
case 'uri_ref':
return node.data.uri_pattern;
}
}, []);

const handleNodeFocus = useCallback(
(node: XYNodeTypes) => {
reactflow?.fitView({
padding: 0.1,
includeHiddenNodes: false,
});
reactflow?.setCenter(node.position.x, node.position.y, {
zoom: 1.5,
});
},
[reactflow],
);

const handleNodeEdit = useCallback(
(node: XYNodeTypes) => {
reactflow?.fitView({
padding: 0.1,
includeHiddenNodes: false,
});
reactflow?.setCenter(node.position.x, node.position.y, {
zoom: 1.5,
});
setNodes(
nodes.map(n => {
if (n.id === node.id) {
return {
...n,
selected: true,
};
}
return {
...n,
selected: false,
};
}),
);
setSelectedTab('properties');
},
[reactflow, nodes, setNodes, setSelectedTab],
);

return (
<div
style={{
display: 'flex',
flexDirection: 'column',
height: '100%',
}}
>
<FormGroup
label='Search'
style={{
flex: '0 0 auto',
}}
>
<InputGroup
placeholder='Search nodes'
type='search'
onChange={handleSearch}
/>
</FormGroup>
<div style={{ flex: 1, overflowY: 'auto', height: '100%' }}>
{searchResults === null && (
<NonIdealState
icon='search'
title='Search for nodes'
description='Start typing to search for nodes'
/>
)}
{searchResults && searchResults.length === 0 && (
<NonIdealState
icon='search'
title='No results found'
description='Try searching for something else'
/>
)}
{searchResults && searchResults.length !== 0 && (
<CardList
style={{
height: 'auto',
}}
>
{searchResults?.map(node => (
<Card
key={node.id}
interactive
onClick={() => handleNodeFocus(node)}
style={{ display: 'flex', justifyContent: 'space-between' }}
title={`Focus on ${getLabelFromNode(node)}`}
>
{getLabelFromNode(node)}
<Icon
icon='edit'
style={{
border: '1px solid #ccc',
borderRadius: '4px',
padding: '2px',
}}
onClick={e => {
e.stopPropagation();
handleNodeEdit(node);
}}
title={`Edit ${getLabelFromNode(node)}`}
/>
</Card>
))}
</CardList>
)}
</div>
</div>
);
};

export default SearchPanel;
11 changes: 5 additions & 6 deletions app/src/pages/mapping_page/components/SidePanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import SearchPanel from '@/pages/mapping_page/components/SidePanel/components/SearchPanel';
import useMappingPage from '@/pages/mapping_page/state';
import { Card } from '@blueprintjs/core';
import NodeProperties from './components/NodeProperties';

type SidePanelProps = {
selectedTab: string | undefined;
};

const SidePanel = ({ selectedTab }: SidePanelProps) => {
const SidePanel = () => {
const selectedTab = useMappingPage(state => state.selectedTab);
const panelContent = () => {
switch (selectedTab) {
case 'properties':
Expand All @@ -15,7 +14,7 @@ const SidePanel = ({ selectedTab }: SidePanelProps) => {
case 'references':
return <div>Source References Panel Content</div>;
case 'search':
return <div>Search Panel Content</div>;
return <SearchPanel />;
case 'settings':
return <div>Settings Panel Content</div>;
default:
Expand Down
12 changes: 6 additions & 6 deletions app/src/pages/mapping_page/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ReactFlowProvider } from '@xyflow/react';
import { languages } from 'monaco-editor';
import { useEffect, useRef, useState } from 'react';
import { useEffect, useRef } from 'react';
import {
ImperativePanelHandle,
Panel,
Expand Down Expand Up @@ -28,16 +28,16 @@ type MappingPageURLProps = {

const MappingPage = () => {
const props = useParams<MappingPageURLProps>();
const [selectedTab, setSelectedTab] = useState<
'properties' | 'ai' | 'references' | 'search' | 'settings'
>('properties');
const [isCollapsed, setIsCollapsed] = useState(false);
const selectedTab = useMappingPage(state => state.selectedTab);
const isCollapsed = useMappingPage(state => state.isSidePanelCollapsed);
const mapping = useMappingPage(state => state.mapping);
const source = useMappingPage(state => state.source);
const prefixes = useMappingPage(state => state.prefixes);
const isLoading = useMappingPage(state => state.isLoading);
const error = useMappingPage(state => state.error);
const loadMapping = useMappingPage(state => state.loadMapping);
const setSelectedTab = useMappingPage(state => state.setSelectedTab);
const setIsCollapsed = useMappingPage(state => state.setIsSidePanelCollapsed);

useRegisterTheme('mapping-theme', mapping_theme);
useRegisterLanguage('mapping_language', mapping_language, {});
Expand Down Expand Up @@ -133,7 +133,7 @@ const MappingPage = () => {
minSize={10}
maxSize={50}
>
<SidePanel selectedTab={selectedTab} />
<SidePanel />
</Panel>
{!isCollapsed && (
<PanelResizeHandle className='resize-handle'></PanelResizeHandle>
Expand Down
16 changes: 16 additions & 0 deletions app/src/pages/mapping_page/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import SourceApi from '../../lib/api/source_api';
import { Source } from '../../lib/api/source_api/types';
import { ZustandActions } from '../../utils/zustand';

type SelectedTab = 'properties' | 'ai' | 'references' | 'search' | 'settings';

interface MappingPageState {
mapping: MappingGraph | null;
source: Source | null;
Expand All @@ -22,6 +24,8 @@ interface MappingPageState {
isLoading: string | null;
error: string | null;
isSaved: boolean;
selectedTab: SelectedTab;
isSidePanelCollapsed: boolean;
}

interface MappingPageStateActions {
Expand All @@ -34,6 +38,10 @@ interface MappingPageStateActions {
edges: XYEdgeType[],
) => Promise<void>;
setIsSaved: (isSaved: boolean) => void;
setSelectedTab: (
selectedTab: 'properties' | 'ai' | 'references' | 'search' | 'settings',
) => void;
setIsSidePanelCollapsed: (isSidePanelCollapsed: boolean) => void;
}

const defaultState: MappingPageState = {
Expand All @@ -44,6 +52,8 @@ const defaultState: MappingPageState = {
isLoading: null,
error: null,
isSaved: true,
selectedTab: 'properties',
isSidePanelCollapsed: false,
};

const functions: ZustandActions<MappingPageStateActions, MappingPageState> = (
Expand Down Expand Up @@ -119,6 +129,12 @@ const functions: ZustandActions<MappingPageStateActions, MappingPageState> = (
setIsSaved(isSaved: boolean) {
set({ isSaved });
},
setSelectedTab(selectedTab: SelectedTab) {
set({ selectedTab, isSidePanelCollapsed: false });
},
setIsSidePanelCollapsed(isSidePanelCollapsed: boolean) {
set({ isSidePanelCollapsed });
},
});

export const useMappingPage = create<
Expand Down

0 comments on commit b7e98d8

Please sign in to comment.