mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 21:10:38 +03:00
UI refactoring and small fixes
This commit is contained in:
parent
3d6d802cc0
commit
41eac3fc4f
|
@ -6,15 +6,13 @@ import { colorFgGrammeme } from '@/styling/color';
|
||||||
import { labelGrammeme } from '@/utils/labels';
|
import { labelGrammeme } from '@/utils/labels';
|
||||||
|
|
||||||
interface GrammemeBadgeProps {
|
interface GrammemeBadgeProps {
|
||||||
key?: string;
|
|
||||||
grammeme: GramData;
|
grammeme: GramData;
|
||||||
}
|
}
|
||||||
|
|
||||||
function GrammemeBadge({ key, grammeme }: GrammemeBadgeProps) {
|
function GrammemeBadge({ grammeme }: GrammemeBadgeProps) {
|
||||||
const { colors } = useConceptTheme();
|
const { colors } = useConceptTheme();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={key}
|
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'min-w-[3rem]', // prettier: split lines
|
'min-w-[3rem]', // prettier: split lines
|
||||||
'px-1',
|
'px-1',
|
||||||
|
|
|
@ -119,13 +119,7 @@ function ConstituentaMultiPicker({ id, schema, prefixID, rows, selected, setSele
|
||||||
noFooter
|
noFooter
|
||||||
rows={rows}
|
rows={rows}
|
||||||
contentHeight='1.3rem'
|
contentHeight='1.3rem'
|
||||||
className={clsx(
|
className={clsx('overflow-y-auto', 'border', 'text-sm', 'select-none')}
|
||||||
'min-h-[16rem]', // prettier: split lines
|
|
||||||
'overflow-y-auto',
|
|
||||||
'border',
|
|
||||||
'text-sm',
|
|
||||||
'select-none'
|
|
||||||
)}
|
|
||||||
data={schema?.items ?? []}
|
data={schema?.items ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
headPosition='0rem'
|
headPosition='0rem'
|
||||||
|
|
263
rsconcept/frontend/src/components/select/SubstitutionsPicker.tsx
Normal file
263
rsconcept/frontend/src/components/select/SubstitutionsPicker.tsx
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { BiChevronLeft, BiChevronRight, BiFirstPage, BiLastPage, BiX } from 'react-icons/bi';
|
||||||
|
import { LuLocate, LuLocateOff, LuPower, LuPowerOff, LuReplace } from 'react-icons/lu';
|
||||||
|
|
||||||
|
import ConstituentaBadge from '@/components/info/ConstituentaBadge';
|
||||||
|
import ConstituentaSelector from '@/components/select/ConstituentaSelector';
|
||||||
|
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
||||||
|
import Label from '@/components/ui/Label';
|
||||||
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
|
import { IConstituenta, IRSForm, ISubstitution } from '@/models/rsform';
|
||||||
|
import { describeConstituenta } from '@/utils/labels';
|
||||||
|
|
||||||
|
interface SubstitutionsPickerProps {
|
||||||
|
prefixID: string;
|
||||||
|
rows?: number;
|
||||||
|
|
||||||
|
schema1?: IRSForm;
|
||||||
|
schema2?: IRSForm;
|
||||||
|
filter1?: (cst: IConstituenta) => boolean;
|
||||||
|
filter2?: (cst: IConstituenta) => boolean;
|
||||||
|
|
||||||
|
items: ISubstitution[];
|
||||||
|
setItems: React.Dispatch<React.SetStateAction<ISubstitution[]>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SubstitutionIcon({ item }: { item: ISubstitution }) {
|
||||||
|
if (item.deleteRight) {
|
||||||
|
if (item.takeLeftTerm) {
|
||||||
|
return <BiChevronRight size='1.2rem' />;
|
||||||
|
} else {
|
||||||
|
return <BiLastPage size='1.2rem' />;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (item.takeLeftTerm) {
|
||||||
|
return <BiFirstPage size='1.2rem' />;
|
||||||
|
} else {
|
||||||
|
return <BiChevronLeft size='1.2rem' />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<ISubstitution>();
|
||||||
|
|
||||||
|
function SubstitutionsPicker({
|
||||||
|
items,
|
||||||
|
schema1,
|
||||||
|
schema2,
|
||||||
|
filter1,
|
||||||
|
filter2,
|
||||||
|
rows,
|
||||||
|
setItems,
|
||||||
|
prefixID
|
||||||
|
}: SubstitutionsPickerProps) {
|
||||||
|
const { colors } = useConceptTheme();
|
||||||
|
|
||||||
|
const [leftCst, setLeftCst] = useState<IConstituenta | undefined>(undefined);
|
||||||
|
const [rightCst, setRightCst] = useState<IConstituenta | undefined>(undefined);
|
||||||
|
const [deleteRight, setDeleteRight] = useState(true);
|
||||||
|
const [takeLeftTerm, setTakeLeftTerm] = useState(true);
|
||||||
|
|
||||||
|
const toggleDelete = () => setDeleteRight(prev => !prev);
|
||||||
|
const toggleTerm = () => setTakeLeftTerm(prev => !prev);
|
||||||
|
|
||||||
|
function addSubstitution() {
|
||||||
|
if (!leftCst || !rightCst) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newSubstitution: ISubstitution = {
|
||||||
|
leftCst: leftCst,
|
||||||
|
rightCst: rightCst,
|
||||||
|
deleteRight: deleteRight,
|
||||||
|
takeLeftTerm: takeLeftTerm
|
||||||
|
};
|
||||||
|
setItems([
|
||||||
|
newSubstitution,
|
||||||
|
...items.filter(
|
||||||
|
item =>
|
||||||
|
(!item.deleteRight && item.leftCst.id !== leftCst.id) ||
|
||||||
|
(item.deleteRight && item.rightCst.id !== rightCst.id)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeleteRow = useCallback(
|
||||||
|
(row: number) => {
|
||||||
|
setItems(prev => {
|
||||||
|
const newItems: ISubstitution[] = [];
|
||||||
|
prev.forEach((item, index) => {
|
||||||
|
if (index !== row) {
|
||||||
|
newItems.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newItems;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setItems]
|
||||||
|
);
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() => [
|
||||||
|
columnHelper.accessor(item => describeConstituenta(item.leftCst), {
|
||||||
|
id: 'left_text',
|
||||||
|
header: 'Описание',
|
||||||
|
size: 1000,
|
||||||
|
cell: props => <div className='text-xs text-ellipsis'>{props.getValue()}</div>
|
||||||
|
}),
|
||||||
|
columnHelper.accessor(item => item.leftCst.alias, {
|
||||||
|
id: 'left_alias',
|
||||||
|
header: 'Имя',
|
||||||
|
size: 65,
|
||||||
|
cell: props => (
|
||||||
|
<ConstituentaBadge theme={colors} value={props.row.original.leftCst} prefixID={`${prefixID}_1_`} />
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
columnHelper.display({
|
||||||
|
id: 'status',
|
||||||
|
header: '',
|
||||||
|
size: 40,
|
||||||
|
cell: props => <SubstitutionIcon item={props.row.original} />
|
||||||
|
}),
|
||||||
|
columnHelper.accessor(item => item.rightCst.alias, {
|
||||||
|
id: 'right_alias',
|
||||||
|
header: 'Имя',
|
||||||
|
size: 65,
|
||||||
|
cell: props => (
|
||||||
|
<ConstituentaBadge theme={colors} value={props.row.original.rightCst} prefixID={`${prefixID}_2_`} />
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
columnHelper.accessor(item => describeConstituenta(item.rightCst), {
|
||||||
|
id: 'right_text',
|
||||||
|
header: 'Описание',
|
||||||
|
size: 1000,
|
||||||
|
cell: props => <div className='text-xs text-ellipsis'>{props.getValue()}</div>
|
||||||
|
}),
|
||||||
|
columnHelper.display({
|
||||||
|
id: 'actions',
|
||||||
|
size: 50,
|
||||||
|
minSize: 50,
|
||||||
|
maxSize: 50,
|
||||||
|
cell: props => (
|
||||||
|
<MiniButton
|
||||||
|
noHover
|
||||||
|
title='Удалить'
|
||||||
|
icon={<BiX size='1rem' className='icon-red' />}
|
||||||
|
onClick={() => handleDeleteRow(props.row.index)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
],
|
||||||
|
[handleDeleteRow, colors, prefixID]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col'>
|
||||||
|
<div className='flex items-end gap-3 justify-stretch'>
|
||||||
|
<div className='flex-grow basis-1/2'>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<Label text={schema1?.alias ?? 'Схема 1'} />
|
||||||
|
<div>
|
||||||
|
<MiniButton
|
||||||
|
title='Сохранить конституенту'
|
||||||
|
noHover
|
||||||
|
onClick={toggleDelete}
|
||||||
|
icon={
|
||||||
|
deleteRight ? (
|
||||||
|
<LuPower size='1rem' className='clr-text-green' />
|
||||||
|
) : (
|
||||||
|
<LuPowerOff size='1rem' className='clr-text-red' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MiniButton
|
||||||
|
title='Сохранить термин'
|
||||||
|
noHover
|
||||||
|
onClick={toggleTerm}
|
||||||
|
icon={
|
||||||
|
takeLeftTerm ? (
|
||||||
|
<LuLocate size='1rem' className='clr-text-green' />
|
||||||
|
) : (
|
||||||
|
<LuLocateOff size='1rem' className='clr-text-red' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ConstituentaSelector
|
||||||
|
items={schema1?.items.filter(cst => !filter1 || filter1(cst))}
|
||||||
|
value={leftCst}
|
||||||
|
onSelectValue={setLeftCst}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MiniButton
|
||||||
|
noHover
|
||||||
|
title='Добавить в таблицу отождествлений'
|
||||||
|
className='mb-[0.375rem] grow-0'
|
||||||
|
icon={<LuReplace size='1.5rem' className='icon-primary' />}
|
||||||
|
disabled={!leftCst || !rightCst}
|
||||||
|
onClick={addSubstitution}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='flex-grow basis-1/2'>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<Label text={schema2?.alias ?? 'Схема 2'} />
|
||||||
|
<div>
|
||||||
|
<MiniButton
|
||||||
|
title='Сохранить конституенту'
|
||||||
|
noHover
|
||||||
|
onClick={toggleDelete}
|
||||||
|
icon={
|
||||||
|
!deleteRight ? (
|
||||||
|
<LuPower size='1rem' className='clr-text-green' />
|
||||||
|
) : (
|
||||||
|
<LuPowerOff size='1rem' className='clr-text-red' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MiniButton
|
||||||
|
title='Сохранить термин'
|
||||||
|
noHover
|
||||||
|
onClick={toggleTerm}
|
||||||
|
icon={
|
||||||
|
!takeLeftTerm ? (
|
||||||
|
<LuLocate size='1rem' className='clr-text-green' />
|
||||||
|
) : (
|
||||||
|
<LuLocateOff size='1rem' className='clr-text-red' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ConstituentaSelector
|
||||||
|
items={schema2?.items.filter(cst => !filter2 || filter2(cst))}
|
||||||
|
value={rightCst}
|
||||||
|
onSelectValue={setRightCst}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DataTable
|
||||||
|
dense
|
||||||
|
noFooter
|
||||||
|
className='overflow-y-auto border select-none'
|
||||||
|
rows={rows}
|
||||||
|
contentHeight='1.3rem'
|
||||||
|
data={items}
|
||||||
|
columns={columns}
|
||||||
|
headPosition='0'
|
||||||
|
noDataComponent={
|
||||||
|
<span className='p-2 text-center min-h-[2rem]'>
|
||||||
|
<p>Список пуст</p>
|
||||||
|
<p>Добавьте отождествление</p>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SubstitutionsPicker;
|
|
@ -1,19 +1,22 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { IVersionInfo } from '@/models/library';
|
import { IVersionInfo } from '@/models/library';
|
||||||
import { labelVersion } from '@/utils/labels';
|
import { labelVersion } from '@/utils/labels';
|
||||||
|
|
||||||
|
import { CProps } from '../props';
|
||||||
import SelectSingle from '../ui/SelectSingle';
|
import SelectSingle from '../ui/SelectSingle';
|
||||||
|
|
||||||
interface VersionSelectorProps {
|
interface VersionSelectorProps extends CProps.Styling {
|
||||||
|
id?: string;
|
||||||
items?: IVersionInfo[];
|
items?: IVersionInfo[];
|
||||||
value?: number;
|
value?: number;
|
||||||
onSelectValue: (newValue?: number) => void;
|
onSelectValue: (newValue?: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function VersionSelector({ items, value, onSelectValue }: VersionSelectorProps) {
|
function VersionSelector({ id, className, items, value, onSelectValue, ...restProps }: VersionSelectorProps) {
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -33,10 +36,12 @@ function VersionSelector({ items, value, onSelectValue }: VersionSelectorProps)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
className='w-full min-w-[12rem] text-ellipsis'
|
id={id}
|
||||||
|
className={clsx('w-full min-w-[12rem] text-ellipsis', className)}
|
||||||
options={options}
|
options={options}
|
||||||
value={{ value: value, label: valueLabel }}
|
value={{ value: value, label: valueLabel }}
|
||||||
onChange={data => onSelectValue(data?.value)}
|
onChange={data => onSelectValue(data?.value)}
|
||||||
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,10 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
|
||||||
onInlineSynthesis(data);
|
onInlineSynthesis(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => setSelected(source.schema ? source.schema?.items.map(cst => cst.id) : []), [source.schema]);
|
useEffect(() => {
|
||||||
|
setSelected(source.schema ? source.schema?.items.map(cst => cst.id) : []);
|
||||||
|
setSubstitutions([]);
|
||||||
|
}, [source.schema]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|
|
@ -30,7 +30,12 @@ function SchemaTab({ selected, setSelected }: SchemaTabProps) {
|
||||||
dense
|
dense
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SchemaPicker rows={15} value={selected} onSelectValue={setSelected} />
|
<SchemaPicker
|
||||||
|
id='dlg_schema_picker' // prettier: split lines
|
||||||
|
rows={15}
|
||||||
|
value={selected}
|
||||||
|
onSelectValue={setSelected}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,11 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { LuLocate, LuLocateOff, LuPower, LuPowerOff, LuReplace } from 'react-icons/lu';
|
|
||||||
|
|
||||||
import { ErrorData } from '@/components/info/InfoError';
|
import { ErrorData } from '@/components/info/InfoError';
|
||||||
import ConstituentaSelector from '@/components/select/ConstituentaSelector';
|
|
||||||
import Button from '@/components/ui/Button';
|
|
||||||
import Label from '@/components/ui/Label';
|
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
|
||||||
import Overlay from '@/components/ui/Overlay';
|
|
||||||
import DataLoader from '@/components/wrap/DataLoader';
|
import DataLoader from '@/components/wrap/DataLoader';
|
||||||
import { ConstituentaID, IConstituenta, IRSForm, ISubstitution } from '@/models/rsform';
|
import { ConstituentaID, IRSForm, ISubstitution } from '@/models/rsform';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
import SubstitutionsTable from './SubstitutionsTable';
|
import SubstitutionsPicker from '../../components/select/SubstitutionsPicker';
|
||||||
|
|
||||||
interface SubstitutionsTabProps {
|
interface SubstitutionsTabProps {
|
||||||
receiver?: IRSForm;
|
receiver?: IRSForm;
|
||||||
|
@ -38,125 +30,16 @@ function SubstitutionsTab({
|
||||||
substitutions,
|
substitutions,
|
||||||
setSubstitutions
|
setSubstitutions
|
||||||
}: SubstitutionsTabProps) {
|
}: SubstitutionsTabProps) {
|
||||||
const [leftCst, setLeftCst] = useState<IConstituenta | undefined>(undefined);
|
|
||||||
const [rightCst, setRightCst] = useState<IConstituenta | undefined>(undefined);
|
|
||||||
const [deleteRight, setDeleteRight] = useState(false);
|
|
||||||
const [takeLeftTerm, setTakeLeftTerm] = useState(false);
|
|
||||||
|
|
||||||
const toggleDelete = () => setDeleteRight(prev => !prev);
|
|
||||||
const toggleTerm = () => setTakeLeftTerm(prev => !prev);
|
|
||||||
|
|
||||||
function addSubstitution() {
|
|
||||||
if (!leftCst || !rightCst) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newSubstitution: ISubstitution = {
|
|
||||||
leftCst: leftCst,
|
|
||||||
rightCst: rightCst,
|
|
||||||
deleteRight: deleteRight,
|
|
||||||
takeLeftTerm: takeLeftTerm
|
|
||||||
};
|
|
||||||
setSubstitutions([
|
|
||||||
newSubstitution,
|
|
||||||
...substitutions.filter(
|
|
||||||
item =>
|
|
||||||
(!item.deleteRight && item.leftCst.id !== leftCst.id) ||
|
|
||||||
(item.deleteRight && item.rightCst.id !== rightCst.id)
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataLoader id='dlg-substitutions-tab' className='cc-column' isLoading={loading} error={error} hasNoData={!source}>
|
<DataLoader id='dlg-substitutions-tab' className='cc-column' isLoading={loading} error={error} hasNoData={!source}>
|
||||||
<div className='flex items-end justify-between'>
|
<SubstitutionsPicker
|
||||||
<div>
|
|
||||||
<Overlay className='flex select-none'>
|
|
||||||
<MiniButton
|
|
||||||
title='Сохранить конституенту'
|
|
||||||
noHover
|
|
||||||
onClick={toggleDelete}
|
|
||||||
icon={
|
|
||||||
deleteRight ? (
|
|
||||||
<LuPower size='1rem' className='clr-text-green' />
|
|
||||||
) : (
|
|
||||||
<LuPowerOff size='1rem' className='clr-text-red' />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<MiniButton
|
|
||||||
title='Сохранить термин'
|
|
||||||
noHover
|
|
||||||
onClick={toggleTerm}
|
|
||||||
icon={
|
|
||||||
takeLeftTerm ? (
|
|
||||||
<LuLocate size='1rem' className='clr-text-green' />
|
|
||||||
) : (
|
|
||||||
<LuLocateOff size='1rem' className='clr-text-red' />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Overlay>
|
|
||||||
<Label text='Импортируемая схема' />
|
|
||||||
<ConstituentaSelector
|
|
||||||
className='w-[15rem] mt-1'
|
|
||||||
items={source?.items.filter(cst => selected.includes(cst.id))}
|
|
||||||
value={leftCst}
|
|
||||||
onSelectValue={setLeftCst}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
title='Добавить в таблицу отождествлений'
|
|
||||||
className='h-[2.4rem] w-[5rem]'
|
|
||||||
icon={<LuReplace size='1.25rem' className='icon-primary' />}
|
|
||||||
disabled={!leftCst || !rightCst}
|
|
||||||
onClick={addSubstitution}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Overlay className='flex select-none'>
|
|
||||||
<MiniButton
|
|
||||||
title='Сохранить конституенту'
|
|
||||||
noHover
|
|
||||||
onClick={toggleDelete}
|
|
||||||
icon={
|
|
||||||
!deleteRight ? (
|
|
||||||
<LuPower size='1rem' className='clr-text-green' />
|
|
||||||
) : (
|
|
||||||
<LuPowerOff size='1rem' className='clr-text-red' />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<MiniButton
|
|
||||||
title='Сохранить термин'
|
|
||||||
noHover
|
|
||||||
onClick={toggleTerm}
|
|
||||||
icon={
|
|
||||||
!takeLeftTerm ? (
|
|
||||||
<LuLocate size='1rem' className='clr-text-green' />
|
|
||||||
) : (
|
|
||||||
<LuLocateOff size='1rem' className='clr-text-red' />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Overlay>
|
|
||||||
<Label text='Текущая схема' />
|
|
||||||
<ConstituentaSelector
|
|
||||||
className='w-[15rem] mt-1'
|
|
||||||
items={receiver?.items}
|
|
||||||
value={rightCst}
|
|
||||||
onSelectValue={setRightCst}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Таблица отождествлений</h2>
|
|
||||||
|
|
||||||
<SubstitutionsTable
|
|
||||||
items={substitutions}
|
items={substitutions}
|
||||||
setItems={setSubstitutions}
|
setItems={setSubstitutions}
|
||||||
rows={10}
|
rows={10}
|
||||||
prefixID={prefixes.cst_inline_synth_substitutes}
|
prefixID={prefixes.cst_inline_synth_substitutes}
|
||||||
|
schema1={receiver}
|
||||||
|
schema2={source}
|
||||||
|
filter2={cst => selected.includes(cst.id)}
|
||||||
/>
|
/>
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,130 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
import { BiChevronLeft, BiChevronRight, BiFirstPage, BiLastPage, BiX } from 'react-icons/bi';
|
|
||||||
|
|
||||||
import ConstituentaBadge from '@/components/info/ConstituentaBadge';
|
|
||||||
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
|
||||||
import { ISubstitution } from '@/models/rsform';
|
|
||||||
import { describeConstituenta } from '@/utils/labels';
|
|
||||||
|
|
||||||
interface SubstitutionsTableProps {
|
|
||||||
prefixID: string;
|
|
||||||
rows?: number;
|
|
||||||
items: ISubstitution[];
|
|
||||||
setItems: React.Dispatch<React.SetStateAction<ISubstitution[]>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SubstitutionIcon({ item }: { item: ISubstitution }) {
|
|
||||||
if (item.deleteRight) {
|
|
||||||
if (item.takeLeftTerm) {
|
|
||||||
return <BiChevronRight size='1.2rem' />;
|
|
||||||
} else {
|
|
||||||
return <BiLastPage size='1.2rem' />;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (item.takeLeftTerm) {
|
|
||||||
return <BiFirstPage size='1.2rem' />;
|
|
||||||
} else {
|
|
||||||
return <BiChevronLeft size='1.2rem' />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<ISubstitution>();
|
|
||||||
|
|
||||||
function SubstitutionsTable({ items, rows, setItems, prefixID }: SubstitutionsTableProps) {
|
|
||||||
const { colors } = useConceptTheme();
|
|
||||||
|
|
||||||
const handleDeleteRow = useCallback(
|
|
||||||
(row: number) => {
|
|
||||||
setItems(prev => {
|
|
||||||
const newItems: ISubstitution[] = [];
|
|
||||||
prev.forEach((item, index) => {
|
|
||||||
if (index !== row) {
|
|
||||||
newItems.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return newItems;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[setItems]
|
|
||||||
);
|
|
||||||
|
|
||||||
const columns = useMemo(
|
|
||||||
() => [
|
|
||||||
columnHelper.accessor(item => describeConstituenta(item.leftCst), {
|
|
||||||
id: 'left_text',
|
|
||||||
header: 'Описание',
|
|
||||||
size: 1000,
|
|
||||||
cell: props => <div className='text-xs text-ellipsis'>{props.getValue()}</div>
|
|
||||||
}),
|
|
||||||
columnHelper.accessor(item => item.leftCst.alias, {
|
|
||||||
id: 'left_alias',
|
|
||||||
header: 'Имя',
|
|
||||||
size: 65,
|
|
||||||
cell: props => (
|
|
||||||
<ConstituentaBadge theme={colors} value={props.row.original.leftCst} prefixID={`${prefixID}_1_`} />
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
columnHelper.display({
|
|
||||||
id: 'status',
|
|
||||||
header: '',
|
|
||||||
size: 40,
|
|
||||||
cell: props => <SubstitutionIcon item={props.row.original} />
|
|
||||||
}),
|
|
||||||
columnHelper.accessor(item => item.rightCst.alias, {
|
|
||||||
id: 'right_alias',
|
|
||||||
header: 'Имя',
|
|
||||||
size: 65,
|
|
||||||
cell: props => (
|
|
||||||
<ConstituentaBadge theme={colors} value={props.row.original.rightCst} prefixID={`${prefixID}_2_`} />
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
columnHelper.accessor(item => describeConstituenta(item.rightCst), {
|
|
||||||
id: 'right_text',
|
|
||||||
header: 'Описание',
|
|
||||||
size: 1000,
|
|
||||||
cell: props => <div className='text-xs text-ellipsis'>{props.getValue()}</div>
|
|
||||||
}),
|
|
||||||
columnHelper.display({
|
|
||||||
id: 'actions',
|
|
||||||
size: 50,
|
|
||||||
minSize: 50,
|
|
||||||
maxSize: 50,
|
|
||||||
cell: props => (
|
|
||||||
<MiniButton
|
|
||||||
noHover
|
|
||||||
title='Удалить'
|
|
||||||
icon={<BiX size='1rem' className='icon-red' />}
|
|
||||||
onClick={() => handleDeleteRow(props.row.index)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
],
|
|
||||||
[handleDeleteRow, colors, prefixID]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DataTable
|
|
||||||
dense
|
|
||||||
noFooter
|
|
||||||
className='mb-2 overflow-y-auto border select-none'
|
|
||||||
rows={rows}
|
|
||||||
contentHeight='1.3rem'
|
|
||||||
data={items}
|
|
||||||
columns={columns}
|
|
||||||
headPosition='0'
|
|
||||||
noDataComponent={
|
|
||||||
<span className='p-2 text-center min-h-[2rem]'>
|
|
||||||
<p>Список пуст</p>
|
|
||||||
<p>Добавьте отождествление</p>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SubstitutionsTable;
|
|
|
@ -140,6 +140,7 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<Label text='Версия' className='mb-2' />
|
<Label text='Версия' className='mb-2' />
|
||||||
<VersionSelector
|
<VersionSelector
|
||||||
|
id='schema_version'
|
||||||
value={schema?.version} // prettier: split lines
|
value={schema?.version} // prettier: split lines
|
||||||
items={schema?.versions}
|
items={schema?.versions}
|
||||||
onSelectValue={controller.viewVersion}
|
onSelectValue={controller.viewVersion}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user