2023-12-13 14:32:57 +03:00
|
|
|
'use client';
|
|
|
|
|
2024-03-27 15:32:59 +03:00
|
|
|
import { useCallback, useLayoutEffect, useState } from 'react';
|
2023-12-08 19:24:08 +03:00
|
|
|
|
2024-04-04 20:03:41 +03:00
|
|
|
import {
|
|
|
|
IconFilter,
|
|
|
|
IconGraphCollapse,
|
|
|
|
IconGraphExpand,
|
|
|
|
IconGraphInputs,
|
|
|
|
IconGraphOutputs,
|
|
|
|
IconSettings,
|
|
|
|
IconText
|
|
|
|
} from '@/components/Icons';
|
2024-01-04 19:38:12 +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';
|
2023-12-13 14:32:57 +03:00
|
|
|
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';
|
2024-03-17 19:24:12 +03:00
|
|
|
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
2023-12-13 14:32:57 +03:00
|
|
|
import { createMockConstituenta, matchConstituenta } from '@/models/rsformAPI';
|
|
|
|
import { extractGlobals } from '@/models/rslangAPI';
|
2024-03-27 15:32:59 +03:00
|
|
|
import { prefixes, storage } 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;
|
2024-03-17 19:24:12 +03:00
|
|
|
activeID?: ConstituentaID;
|
2023-12-28 14:04:44 +03:00
|
|
|
activeExpression: string;
|
|
|
|
setFiltered: React.Dispatch<React.SetStateAction<IConstituenta[]>>;
|
2023-12-08 19:24:08 +03:00
|
|
|
}
|
|
|
|
|
2024-04-04 20:03:41 +03:00
|
|
|
function DependencyIcon(mode: DependencyMode, size: string) {
|
|
|
|
switch (mode) {
|
|
|
|
case DependencyMode.ALL:
|
|
|
|
return <IconSettings size={size} />;
|
|
|
|
case DependencyMode.EXPRESSION:
|
|
|
|
return <IconText size={size} />;
|
|
|
|
case DependencyMode.OUTPUTS:
|
|
|
|
return <IconGraphOutputs size={size} />;
|
|
|
|
case DependencyMode.INPUTS:
|
|
|
|
return <IconGraphInputs size={size} />;
|
|
|
|
case DependencyMode.EXPAND_OUTPUTS:
|
|
|
|
return <IconGraphExpand size={size} />;
|
|
|
|
case DependencyMode.EXPAND_INPUTS:
|
|
|
|
return <IconGraphCollapse size={size} />;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-08 19:24:08 +03:00
|
|
|
function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }: ConstituentsSearchProps) {
|
2024-03-27 15:32:59 +03:00
|
|
|
const [filterMatch, setFilterMatch] = useLocalStorage(storage.cstFilterMatch, CstMatchMode.ALL);
|
|
|
|
const [filterSource, setFilterSource] = useLocalStorage(storage.cstFilterGraph, DependencyMode.ALL);
|
|
|
|
const [filterText, setFilterText] = useState('');
|
2023-12-08 19:24:08 +03:00
|
|
|
|
|
|
|
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));
|
2023-12-15 17:34:50 +03:00
|
|
|
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'>
|
2024-03-18 16:21:39 +03:00
|
|
|
<SearchBar
|
|
|
|
id='constituents_search'
|
|
|
|
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='Настройка атрибутов для фильтрации'
|
2024-02-03 15:33:28 +03:00
|
|
|
hideTitle={matchModeMenu.isOpen}
|
2023-12-28 14:04:44 +03:00
|
|
|
className='h-full'
|
2024-04-03 21:05:53 +03:00
|
|
|
icon={<IconFilter size='1.25rem' />}
|
2023-12-28 14:04:44 +03:00
|
|
|
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
|
2024-02-03 15:33:28 +03:00
|
|
|
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='Настройка фильтрации по графу термов'
|
2024-02-03 15:33:28 +03:00
|
|
|
hideTitle={sourceMenu.isOpen}
|
2023-12-28 14:04:44 +03:00
|
|
|
className='h-full pr-2'
|
2024-04-04 20:03:41 +03:00
|
|
|
icon={DependencyIcon(filterSource, '1.25rem')}
|
2023-12-28 14:04:44 +03:00
|
|
|
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 (
|
2024-02-03 15:33:28 +03:00
|
|
|
<DropdownButton
|
2024-04-04 20:03:41 +03:00
|
|
|
className='w-[18rem]'
|
2024-02-03 15:33:28 +03:00
|
|
|
key={`${prefixes.cst_source_list}${index}`}
|
|
|
|
onClick={() => handleSourceChange(source)}
|
|
|
|
>
|
2024-04-04 20:03:41 +03:00
|
|
|
<div className='inline-flex items-center gap-1'>
|
|
|
|
{DependencyIcon(source, '1.25rem')}
|
2023-12-28 14:04:44 +03:00
|
|
|
<b>{labelCstSource(source)}:</b> {describeCstSource(source)}
|
2024-04-04 20:03:41 +03:00
|
|
|
</div>
|
2023-12-28 14:04:44 +03:00
|
|
|
</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;
|