Portal/rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/TabArguments.tsx
2024-09-04 14:37:07 +03:00

232 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { createColumnHelper } from '@tanstack/react-table';
import clsx from 'clsx';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { IconAccept, IconRemove, IconReset } from '@/components/Icons';
import RSInput from '@/components/RSInput';
import PickConstituenta from '@/components/select/PickConstituenta';
import DataTable, { IConditionalStyle } from '@/components/ui/DataTable';
import MiniButton from '@/components/ui/MiniButton';
import NoData from '@/components/ui/NoData';
import AnimateFade from '@/components/wrap/AnimateFade';
import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { IConstituenta, IRSForm } from '@/models/rsform';
import { IArgumentValue } from '@/models/rslang';
import { prefixes } from '@/utils/constants';
interface TabArgumentsProps {
state: IArgumentsState;
schema: IRSForm;
partialUpdate: React.Dispatch<Partial<IArgumentsState>>;
}
export interface IArgumentsState {
arguments: IArgumentValue[];
definition: string;
}
const argumentsHelper = createColumnHelper<IArgumentValue>();
function TabArguments({ state, schema, partialUpdate }: TabArgumentsProps) {
const { colors } = useConceptOptions();
const [selectedCst, setSelectedCst] = useState<IConstituenta | undefined>(undefined);
const [selectedArgument, setSelectedArgument] = useState<IArgumentValue | undefined>(undefined);
const [argumentValue, setArgumentValue] = useState('');
const isModified = useMemo(
() => selectedArgument && argumentValue !== selectedArgument.value,
[selectedArgument, argumentValue]
);
useEffect(() => {
if (!selectedArgument && state.arguments.length > 0) {
setSelectedArgument(state.arguments[0]);
}
}, [state.arguments, selectedArgument]);
const handleSelectArgument = useCallback((arg: IArgumentValue) => {
setSelectedArgument(arg);
if (arg.value) {
setArgumentValue(arg.value);
}
}, []);
const handleSelectConstituenta = useCallback((cst: IConstituenta) => {
setSelectedCst(cst);
setArgumentValue(cst.alias);
}, []);
const handleClearArgument = useCallback(
(target: IArgumentValue) => {
const newArg = { ...target, value: '' };
partialUpdate({
arguments: state.arguments.map(arg => (arg.alias !== target.alias ? arg : newArg))
});
setSelectedArgument(newArg);
},
[partialUpdate, state.arguments]
);
const handleReset = useCallback(() => {
setArgumentValue(selectedArgument?.value ?? '');
}, [selectedArgument]);
const handleAssignArgument = useCallback(
(target: IArgumentValue, value: string) => {
const newArg = { ...target, value: value };
partialUpdate({
arguments: state.arguments.map(arg => (arg.alias !== target.alias ? arg : newArg))
});
setSelectedArgument(newArg);
},
[partialUpdate, state.arguments]
);
const columns = useMemo(
() => [
argumentsHelper.accessor('alias', {
id: 'alias',
size: 40,
minSize: 40,
maxSize: 40,
cell: props => <div className='text-center'>{props.getValue()}</div>
}),
argumentsHelper.accessor(arg => arg.value || 'свободный аргумент', {
id: 'value',
size: 200,
minSize: 200,
maxSize: 200
}),
argumentsHelper.accessor(arg => arg.typification, {
id: 'type',
enableHiding: true,
cell: props => (
<div
className={clsx(
'min-w-[9.3rem] max-w-[9.3rem]', // prettier: split lines
'text-sm break-words'
)}
>
{props.getValue()}
</div>
)
}),
argumentsHelper.display({
id: 'actions',
size: 0,
cell: props => (
<div className='h-[1.25rem] w-[1.25rem]'>
{props.row.original.value ? (
<MiniButton
title='Очистить значение'
noPadding
noHover
icon={<IconRemove size='1.25rem' className='icon-red' />}
onClick={() => handleClearArgument(props.row.original)}
/>
) : null}
</div>
)
})
],
[handleClearArgument]
);
const conditionalRowStyles = useMemo(
(): IConditionalStyle<IArgumentValue>[] => [
{
when: (arg: IArgumentValue) => arg.alias === selectedArgument?.alias,
style: { backgroundColor: colors.bgSelected }
}
],
[selectedArgument, colors]
);
return (
<AnimateFade>
<DataTable
dense
noFooter
noHeader
className={clsx(
'max-h-[5.8rem] min-h-[5.8rem]', // prettier: split lines
'cc-scroll-y',
'text-sm',
'border',
'select-none'
)}
data={state.arguments}
columns={columns}
conditionalRowStyles={conditionalRowStyles}
noDataComponent={<NoData className='min-h-[3.6rem]'>Аргументы отсутствуют</NoData>}
onRowClicked={handleSelectArgument}
/>
<div
className={clsx(
'my-4', // prettier: split lines
'flex gap-2 justify-center items-center',
'select-none'
)}
>
<span
title='Выберите аргумент из списка сверху и значение из списка снизу'
className='font-semibold text-center'
>
{selectedArgument?.alias || 'ARG'}
</span>
<span>=</span>
<RSInput
noTooltip
className='w-[12rem]'
value={argumentValue}
onChange={newValue => setArgumentValue(newValue)}
/>
<div className='flex'>
<MiniButton
title='Подставить значение аргумента'
noHover
className='py-0'
icon={<IconAccept size='1.5rem' className='icon-green' />}
disabled={!argumentValue || !selectedArgument}
onClick={() => handleAssignArgument(selectedArgument!, argumentValue)}
/>
<MiniButton
title='Очистить поле'
noHover
className='py-0'
disabled={!isModified}
onClick={handleReset}
icon={<IconReset size='1.5rem' className='icon-primary' />}
/>
</div>
</div>
<PickConstituenta
id='dlg_argument_picker'
value={selectedCst}
data={schema?.items}
onSelectValue={handleSelectConstituenta}
prefixID={prefixes.cst_modal_list}
rows={7}
/>
<RSInput
disabled
noTooltip
id='result'
placeholder='Итоговое определение'
className='mt-[1.2rem]'
height='5.1rem'
value={state.definition}
/>
</AnimateFade>
);
}
export default TabArguments;