ConceptPortal-public/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx

146 lines
5.3 KiB
TypeScript
Raw Normal View History

'use client';
2023-12-08 19:24:08 +03:00
import { useCallback, useLayoutEffect } from 'react';
2023-12-16 19:20:26 +03:00
import { BiCog, BiFilterAlt } from 'react-icons/bi';
2023-12-08 19:24:08 +03:00
import Dropdown from '@/components/ui/Dropdown';
import DropdownButton from '@/components/ui/DropdownButton';
import SearchBar from '@/components/ui/SearchBar';
import SelectorButton from '@/components/ui/SelectorButton';
import useDropdown from '@/hooks/useDropdown';
import useLocalStorage from '@/hooks/useLocalStorage';
2023-12-26 14:23:51 +03:00
import { CstMatchMode, DependencyMode } from '@/models/miscellaneous';
import { applyGraphFilter } from '@/models/miscellaneousAPI';
import { IConstituenta, IRSForm } from '@/models/rsform';
import { createMockConstituenta, matchConstituenta } from '@/models/rsformAPI';
import { extractGlobals } from '@/models/rslangAPI';
import { prefixes } from '@/utils/constants';
2023-12-26 14:23:51 +03:00
import { describeCstMatchMode, describeCstSource, labelCstMatchMode, labelCstSource } from '@/utils/labels';
2023-12-08 19:24:08 +03:00
interface ConstituentsSearchProps {
2023-12-28 14:04:44 +03:00
schema?: IRSForm;
activeID?: number;
activeExpression: string;
setFiltered: React.Dispatch<React.SetStateAction<IConstituenta[]>>;
2023-12-08 19:24:08 +03:00
}
function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }: ConstituentsSearchProps) {
const [filterMatch, setFilterMatch] = useLocalStorage('side-filter-match', CstMatchMode.ALL);
const [filterText, setFilterText] = useLocalStorage('side-filter-text', '');
const [filterSource, setFilterSource] = useLocalStorage('side-filter-dependency', DependencyMode.ALL);
const matchModeMenu = useDropdown();
const sourceMenu = useDropdown();
2023-12-28 14:04:44 +03:00
useLayoutEffect(() => {
2023-12-08 19:24:08 +03:00
if (!schema || schema.items.length === 0) {
setFiltered([]);
return;
}
let result: IConstituenta[] = [];
if (filterSource === DependencyMode.EXPRESSION) {
const aliases = extractGlobals(activeExpression);
2023-12-28 14:04:44 +03:00
result = schema.items.filter(cst => aliases.has(cst.alias));
const names = result.map(cst => cst.alias);
2023-12-08 19:24:08 +03:00
const diff = Array.from(aliases).filter(name => !names.includes(name));
if (diff.length > 0) {
2023-12-28 14:04:44 +03:00
diff.forEach((alias, index) => result.push(createMockConstituenta(-index, alias, 'Конституента отсутствует')));
2023-12-08 19:24:08 +03:00
}
} else if (!activeID) {
result = schema.items;
} else {
result = applyGraphFilter(schema, activeID, filterSource);
}
if (filterText) {
result = result.filter(cst => matchConstituenta(cst, filterText, filterMatch));
}
setFiltered(result);
}, [filterText, setFiltered, filterSource, activeExpression, schema?.items, schema, filterMatch, activeID]);
const handleMatchModeChange = useCallback(
2023-12-28 14:04:44 +03:00
(newValue: CstMatchMode) => {
matchModeMenu.hide();
setFilterMatch(newValue);
},
[matchModeMenu, setFilterMatch]
);
2023-12-08 19:24:08 +03:00
const handleSourceChange = useCallback(
2023-12-28 14:04:44 +03:00
(newValue: DependencyMode) => {
sourceMenu.hide();
setFilterSource(newValue);
},
[sourceMenu, setFilterSource]
);
2023-12-08 19:24:08 +03:00
return (
2023-12-28 14:04:44 +03:00
<div className='flex border-b clr-input'>
<SearchBar noBorder className='min-w-[6rem] pr-2 flex-grow' value={filterText} onChange={setFilterText} />
2023-12-28 14:04:44 +03:00
<div ref={matchModeMenu.ref}>
<SelectorButton
transparent
tabIndex={-1}
title='Настройка атрибутов для фильтрации'
hideTitle={matchModeMenu.isOpen}
2023-12-28 14:04:44 +03:00
className='h-full'
icon={<BiFilterAlt size='1.25rem' />}
text={labelCstMatchMode(filterMatch)}
onClick={matchModeMenu.toggle}
/>
<Dropdown stretchLeft isOpen={matchModeMenu.isOpen}>
{Object.values(CstMatchMode)
.filter(value => !isNaN(Number(value)))
.map((value, index) => {
const matchMode = value as CstMatchMode;
return (
<DropdownButton
className='w-[22rem]'
2023-12-28 14:04:44 +03:00
key={`${prefixes.cst_match_mode_list}${index}`}
onClick={() => handleMatchModeChange(matchMode)}
>
<p>
<b>{labelCstMatchMode(matchMode)}:</b> {describeCstMatchMode(matchMode)}
</p>
</DropdownButton>
);
})}
</Dropdown>
</div>
2023-12-08 19:24:08 +03:00
2023-12-28 14:04:44 +03:00
<div ref={sourceMenu.ref}>
<SelectorButton
transparent
tabIndex={-1}
title='Настройка фильтрации по графу термов'
hideTitle={sourceMenu.isOpen}
2023-12-28 14:04:44 +03:00
className='h-full pr-2'
icon={<BiCog size='1.25rem' />}
text={labelCstSource(filterSource)}
onClick={sourceMenu.toggle}
/>
<Dropdown stretchLeft isOpen={sourceMenu.isOpen}>
{Object.values(DependencyMode)
.filter(value => !isNaN(Number(value)))
.map((value, index) => {
const source = value as DependencyMode;
return (
<DropdownButton
className='w-[23rem]'
key={`${prefixes.cst_source_list}${index}`}
onClick={() => handleSourceChange(source)}
>
2023-12-28 14:04:44 +03:00
<p>
<b>{labelCstSource(source)}:</b> {describeCstSource(source)}
</p>
</DropdownButton>
);
})}
</Dropdown>
</div>
2023-12-08 19:24:08 +03:00
</div>
2023-12-28 14:04:44 +03:00
);
2023-12-08 19:24:08 +03:00
}
2023-12-28 14:04:44 +03:00
export default ConstituentsSearch;