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