ConceptPortal-public/rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/ArgumentsTab.tsx

207 lines
6.8 KiB
TypeScript
Raw Normal View History

import { createColumnHelper } from '@tanstack/react-table';
2023-11-06 23:13:27 +03:00
import { Dispatch, useCallback, useEffect, useMemo, useState } from 'react';
2023-11-06 02:20:16 +03:00
2023-11-27 12:11:39 +03:00
import MiniButton from '../../components/Common/MiniButton';
import DataTable, { IConditionalStyle } from '../../components/DataTable';
2023-12-02 00:15:56 +03:00
import { ArrowsRotateIcon, CheckIcon, CrossIcon } from '../../components/Icons';
2023-11-06 02:20:16 +03:00
import RSInput from '../../components/RSInput';
2023-11-27 12:11:39 +03:00
import ConstituentaPicker from '../../components/Shared/ConstituentaPicker';
import { useConceptTheme } from '../../context/ThemeContext';
import { IConstituenta, IRSForm } from '../../models/rsform';
2023-11-06 02:20:16 +03:00
import { IArgumentValue } from '../../models/rslang';
import { prefixes } from '../../utils/constants';
2023-11-06 02:20:16 +03:00
interface ArgumentsTabProps {
state: IArgumentsState
schema: IRSForm
2023-11-06 02:20:16 +03:00
partialUpdate: Dispatch<Partial<IArgumentsState>>
}
export interface IArgumentsState {
arguments: IArgumentValue[]
2023-11-06 02:20:16 +03:00
definition: string
}
const argumentsHelper = createColumnHelper<IArgumentValue>();
function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
const { colors } = useConceptTheme();
const [selectedCst, setSelectedCst] = useState<IConstituenta | undefined>(undefined);
const [selectedArgument, setSelectedArgument] = useState<IArgumentValue | undefined>(undefined);
const [argumentValue, setArgumentValue] = useState('');
2023-12-02 00:15:56 +03:00
const selectedClearable = useMemo(
() => {
return argumentValue && !!selectedArgument && !!selectedArgument.value;
}, [argumentValue, selectedArgument]);
const isModified = useMemo(
() => (selectedArgument && argumentValue !== selectedArgument.value),
[selectedArgument, argumentValue]);
2023-11-06 23:13:27 +03:00
useEffect(
() => {
if (!selectedArgument && state.arguments.length > 0) {
setSelectedArgument(state.arguments[0]);
}
}, [state.arguments, selectedArgument]);
const handleSelectArgument = useCallback(
(arg: IArgumentValue) => {
setSelectedArgument(arg);
2023-11-06 23:13:27 +03:00
if (arg.value) {
setArgumentValue(arg.value);
}
}, []);
const handleSelectConstituenta = useCallback(
(cst: IConstituenta) => {
setSelectedCst(cst);
setArgumentValue(cst.alias);
}, []);
const handleClearArgument = useCallback(
(target: IArgumentValue) => {
2023-12-02 00:15:56 +03:00
const newArg = { ...target, value: '' }
partialUpdate({
2023-12-02 00:15:56 +03:00
arguments: state.arguments.map((arg) => (arg.alias !== target.alias ? arg : newArg))
});
2023-12-02 00:15:56 +03:00
setSelectedArgument(newArg);
}, [partialUpdate, state.arguments]);
2023-12-02 00:15:56 +03:00
const handleReset = useCallback(
() => {
setArgumentValue(selectedArgument?.value ?? '');
}, [selectedArgument]);
const handleAssignArgument = useCallback(
(target: IArgumentValue, value: string) => {
2023-12-02 00:15:56 +03:00
const newArg = { ...target, value: value }
partialUpdate({
2023-12-02 00:15:56 +03:00
arguments: state.arguments.map((arg) => (arg.alias !== target.alias ? arg : newArg))
});
2023-12-02 00:15:56 +03:00
setSelectedArgument(newArg);
}, [partialUpdate, state.arguments]);
const columns = useMemo(
() => [
argumentsHelper.accessor('alias', {
id: 'alias',
header: 'Имя',
size: 40,
minSize: 40,
maxSize: 40,
cell: props => <div className='w-full text-center'>{props.getValue()}</div>
}),
argumentsHelper.accessor(arg => arg.value || 'свободный аргумент', {
id: 'value',
header: 'Значение',
size: 200,
minSize: 200,
maxSize: 200,
}),
argumentsHelper.accessor(arg => arg.typification, {
id: 'type',
header: 'Типизация',
size: 150,
minSize: 150,
maxSize: 150,
enableHiding: true,
cell: props => <div className='text-sm min-w-[9.3rem] max-w-[9.3rem] break-words'>{props.getValue()}</div>
}),
argumentsHelper.display({
id: 'actions',
size: 50,
minSize: 50,
maxSize: 50,
cell: props =>
<div className='max-h-[1.2rem]'>
{props.row.original.value &&
<MiniButton
tooltip='Очистить значение'
icon={<CrossIcon size={3} color='text-warning'/>}
noHover
onClick={() => handleClearArgument(props.row.original)}
/>}
</div>
})
], [handleClearArgument]);
const conditionalRowStyles = useMemo(
(): IConditionalStyle<IArgumentValue>[] => [{
when: (arg: IArgumentValue) => arg.alias === selectedArgument?.alias,
style: { backgroundColor: colors.bgSelected },
}], [selectedArgument, colors]);
2023-11-06 02:20:16 +03:00
return (
<div className='flex flex-col gap-3'>
2023-11-07 10:05:50 +03:00
<div className='overflow-y-auto text-sm border select-none max-h-[5.8rem] min-h-[5.8rem]'>
<DataTable dense noFooter
data={state.arguments}
columns={columns}
conditionalRowStyles={conditionalRowStyles}
noDataComponent={
2023-11-07 10:05:50 +03:00
<span className='flex flex-col justify-center p-2 text-center min-h-[3.6rem]'>
<p>Аргументы отсутствуют</p>
</span>
}
onRowClicked={handleSelectArgument}
/>
</div>
2023-11-07 10:05:50 +03:00
<div className='flex items-center justify-center w-full gap-2 py-1 select-none'>
2023-11-06 18:44:14 +03:00
<span title='Выберите аргумент из списка сверху и значение из списка снизу'
className='font-semibold text-center'
>
{selectedArgument?.alias || 'ARG'}
</span>
<span>=</span>
2023-12-02 00:15:56 +03:00
<RSInput noTooltip
dimensions='max-w-[12rem] w-full'
value={argumentValue}
onChange={newValue => setArgumentValue(newValue)}
/>
2023-12-02 00:15:56 +03:00
<div className='flex'>
<MiniButton
tooltip='Подставить значение аргумента'
icon={<CheckIcon size={5} color={!argumentValue || !selectedArgument ? 'text-disabled' : 'text-success'} />}
disabled={!argumentValue || !selectedArgument}
onClick={() => handleAssignArgument(selectedArgument!, argumentValue)}
/>
<MiniButton
tooltip='Откатить значение'
disabled={!isModified}
onClick={handleReset}
icon={<ArrowsRotateIcon size={5} color={isModified ? 'text-primary' : ''} />}
/>
<MiniButton
tooltip='Очистить значение аргумента'
disabled={!selectedClearable}
icon={<CrossIcon size={5} color={!selectedClearable ? 'text-disabled' : 'text-warning'}/>}
onClick={() => selectedArgument ? handleClearArgument(selectedArgument) : undefined}
/>
</div>
</div>
<ConstituentaPicker
value={selectedCst}
data={schema?.items}
onSelectValue={handleSelectConstituenta}
prefixID={prefixes.cst_modal_list}
2023-11-07 10:05:50 +03:00
rows={7}
2023-11-06 02:20:16 +03:00
/>
2023-11-06 02:20:16 +03:00
<RSInput id='result'
placeholder='Итоговое определение'
height='5.1rem'
2023-11-10 15:34:59 +03:00
dimensions='w-full mt-[0.45rem]'
2023-11-06 02:20:16 +03:00
value={state.definition}
disabled
/>
</div>);
}
export default ArgumentsTab;