R: Refactor constituents search
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run

This commit is contained in:
Ivan 2025-03-04 14:30:28 +03:00
parent d0b430eb17
commit cad521f46d
3 changed files with 54 additions and 80 deletions

View File

@ -1,27 +1,20 @@
'use client'; 'use client';
import { useEffect } from 'react';
import { MiniButton } from '@/components/Control'; import { MiniButton } from '@/components/Control';
import { IconChild } from '@/components/Icons'; import { IconChild } from '@/components/Icons';
import { SearchBar } from '@/components/Input'; import { SearchBar } from '@/components/Input';
import { type IConstituenta, type IRSForm } from '../../../models/rsform';
import { matchConstituenta } from '../../../models/rsformAPI';
import { SelectMatchMode } from '../../../pages/RSFormPage/ViewConstituents/SelectMatchMode'; import { SelectMatchMode } from '../../../pages/RSFormPage/ViewConstituents/SelectMatchMode';
import { DependencyMode, useCstSearchStore } from '../../../stores/cstSearch'; import { useCstSearchStore } from '../../../stores/cstSearch';
import { useRSEdit } from '../RSEditContext';
import { SelectGraphFilter } from './SelectGraphFilter'; import { SelectGraphFilter } from './SelectGraphFilter';
interface ConstituentsSearchProps { interface ConstituentsSearchProps {
schema: IRSForm;
dense?: boolean; dense?: boolean;
activeID?: number;
onChange: (newValue: IConstituenta[]) => void;
} }
export function ConstituentsSearch({ schema, activeID, dense, onChange }: ConstituentsSearchProps) { export function ConstituentsSearch({ dense }: ConstituentsSearchProps) {
const query = useCstSearchStore(state => state.query); const query = useCstSearchStore(state => state.query);
const filterMatch = useCstSearchStore(state => state.match); const filterMatch = useCstSearchStore(state => state.match);
const filterSource = useCstSearchStore(state => state.source); const filterSource = useCstSearchStore(state => state.source);
@ -31,13 +24,7 @@ export function ConstituentsSearch({ schema, activeID, dense, onChange }: Consti
const setSource = useCstSearchStore(state => state.setSource); const setSource = useCstSearchStore(state => state.setSource);
const toggleInherited = useCstSearchStore(state => state.toggleInherited); const toggleInherited = useCstSearchStore(state => state.toggleInherited);
const graphFiltered = activeID ? applyGraphQuery(schema, activeID, filterSource) : schema.items; const { schema } = useRSEdit();
const queryFiltered = query ? graphFiltered.filter(cst => matchConstituenta(cst, query, filterMatch)) : graphFiltered;
const inheritanceFiltered = !includeInherited ? queryFiltered.filter(cst => !cst.is_inherited) : queryFiltered;
useEffect(() => {
onChange(inheritanceFiltered);
}, [inheritanceFiltered, onChange]);
return ( return (
<div className='flex border-b clr-input rounded-t-md'> <div className='flex border-b clr-input rounded-t-md'>
@ -62,34 +49,3 @@ export function ConstituentsSearch({ schema, activeID, dense, onChange }: Consti
</div> </div>
); );
} }
// ====== Internals =========
/**
* Filter list of {@link ILibraryItem} to a given graph query.
*/
function applyGraphQuery(target: IRSForm, pivot: number, mode: DependencyMode): IConstituenta[] {
if (mode === DependencyMode.ALL) {
return target.items;
}
const ids = (() => {
switch (mode) {
case DependencyMode.OUTPUTS: {
return target.graph.nodes.get(pivot)?.outputs;
}
case DependencyMode.INPUTS: {
return target.graph.nodes.get(pivot)?.inputs;
}
case DependencyMode.EXPAND_OUTPUTS: {
return target.graph.expandAllOutputs([pivot]);
}
case DependencyMode.EXPAND_INPUTS: {
return target.graph.expandAllInputs([pivot]);
}
}
})();
if (ids) {
return target.items.filter(cst => ids.find(id => id === cst.id));
} else {
return target.items;
}
}

View File

@ -9,27 +9,32 @@ import { PARAMETER, prefixes } from '@/utils/constants';
import { BadgeConstituenta } from '../../../components/BadgeConstituenta'; import { BadgeConstituenta } from '../../../components/BadgeConstituenta';
import { describeConstituenta } from '../../../labels'; import { describeConstituenta } from '../../../labels';
import { type IConstituenta } from '../../../models/rsform'; import { type IConstituenta, type IRSForm } from '../../../models/rsform';
import { matchConstituenta } from '../../../models/rsformAPI';
import { DependencyMode, useCstSearchStore } from '../../../stores/cstSearch';
import { useRSEdit } from '../RSEditContext';
const DESCRIPTION_MAX_SYMBOLS = 280; const DESCRIPTION_MAX_SYMBOLS = 280;
interface TableSideConstituentsProps { interface TableSideConstituentsProps {
items: IConstituenta[];
activeCst: IConstituenta | null;
onOpenEdit: (cstID: number) => void;
autoScroll?: boolean; autoScroll?: boolean;
maxHeight: string; maxHeight: string;
} }
const columnHelper = createColumnHelper<IConstituenta>(); const columnHelper = createColumnHelper<IConstituenta>();
export function TableSideConstituents({ export function TableSideConstituents({ autoScroll = true, maxHeight }: TableSideConstituentsProps) {
items, const { schema, activeCst, navigateCst } = useRSEdit();
activeCst,
autoScroll = true, const query = useCstSearchStore(state => state.query);
onOpenEdit, const filterMatch = useCstSearchStore(state => state.match);
maxHeight const filterSource = useCstSearchStore(state => state.source);
}: TableSideConstituentsProps) { const includeInherited = useCstSearchStore(state => state.includeInherited);
const graphFiltered = activeCst ? applyGraphQuery(schema, activeCst.id, filterSource) : schema.items;
const queryFiltered = query ? graphFiltered.filter(cst => matchConstituenta(cst, query, filterMatch)) : graphFiltered;
const items = !includeInherited ? queryFiltered.filter(cst => !cst.is_inherited) : queryFiltered;
useEffect(() => { useEffect(() => {
if (!activeCst) { if (!activeCst) {
return; return;
@ -116,7 +121,38 @@ export function TableSideConstituents({
<p>Измените параметры фильтра</p> <p>Измените параметры фильтра</p>
</NoData> </NoData>
} }
onRowClicked={cst => onOpenEdit(cst.id)} onRowClicked={cst => navigateCst(cst.id)}
/> />
); );
} }
// ====== Internals =========
/**
* Filter list of {@link ILibraryItem} to a given graph query.
*/
function applyGraphQuery(target: IRSForm, pivot: number, mode: DependencyMode): IConstituenta[] {
if (mode === DependencyMode.ALL) {
return target.items;
}
const ids = (() => {
switch (mode) {
case DependencyMode.OUTPUTS: {
return target.graph.nodes.get(pivot)?.outputs;
}
case DependencyMode.INPUTS: {
return target.graph.nodes.get(pivot)?.inputs;
}
case DependencyMode.EXPAND_OUTPUTS: {
return target.graph.expandAllOutputs([pivot]);
}
case DependencyMode.EXPAND_INPUTS: {
return target.graph.expandAllInputs([pivot]);
}
}
})();
if (ids) {
return target.items.filter(cst => ids.find(id => id === cst.id));
} else {
return target.items;
}
}

View File

@ -1,6 +1,5 @@
'use client'; 'use client';
import { useState } from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import { useRoleStore, UserRole } from '@/features/users'; import { useRoleStore, UserRole } from '@/features/users';
@ -9,9 +8,6 @@ import { useWindowSize } from '@/hooks/useWindowSize';
import { useFitHeight } from '@/stores/appLayout'; import { useFitHeight } from '@/stores/appLayout';
import { PARAMETER } from '@/utils/constants'; import { PARAMETER } from '@/utils/constants';
import { type IConstituenta } from '../../../models/rsform';
import { useRSEdit } from '../RSEditContext';
import { ConstituentsSearch } from './ConstituentsSearch'; import { ConstituentsSearch } from './ConstituentsSearch';
import { TableSideConstituents } from './TableSideConstituents'; import { TableSideConstituents } from './TableSideConstituents';
@ -27,9 +23,6 @@ export function ViewConstituents({ isBottom, isMounted }: ViewConstituentsProps)
const windowSize = useWindowSize(); const windowSize = useWindowSize();
const role = useRoleStore(state => state.role); const role = useRoleStore(state => state.role);
const listHeight = useFitHeight(!isBottom ? '8.2rem' : role !== UserRole.READER ? '42rem' : '35rem', '10rem'); const listHeight = useFitHeight(!isBottom ? '8.2rem' : role !== UserRole.READER ? '42rem' : '35rem', '10rem');
const { schema, activeCst, navigateCst } = useRSEdit();
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema.items);
return ( return (
<aside <aside
@ -49,19 +42,8 @@ export function ViewConstituents({ isBottom, isMounted }: ViewConstituentsProps)
maxWidth: isMounted ? '100%' : '0' maxWidth: isMounted ? '100%' : '0'
}} }}
> >
<ConstituentsSearch <ConstituentsSearch dense={!!windowSize.width && windowSize.width < COLUMN_DENSE_SEARCH_THRESHOLD} />
dense={!!windowSize.width && windowSize.width < COLUMN_DENSE_SEARCH_THRESHOLD} <TableSideConstituents maxHeight={listHeight} autoScroll={!isBottom} />
schema={schema}
activeID={activeCst?.id}
onChange={setFilteredData}
/>
<TableSideConstituents
maxHeight={listHeight}
items={filteredData}
activeCst={activeCst}
onOpenEdit={navigateCst}
autoScroll={!isBottom}
/>
</aside> </aside>
); );
} }