2023-09-21 14:58:01 +03:00
|
|
|
|
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
2023-09-11 20:31:54 +03:00
|
|
|
|
|
2023-09-14 16:53:38 +03:00
|
|
|
|
import Divider from '../../components/Common/Divider';
|
2023-09-19 17:55:17 +03:00
|
|
|
|
import MiniButton from '../../components/Common/MiniButton';
|
2023-09-11 20:31:54 +03:00
|
|
|
|
import Modal from '../../components/Common/Modal';
|
2023-09-14 16:53:38 +03:00
|
|
|
|
import SelectMulti from '../../components/Common/SelectMulti';
|
2023-09-11 20:31:54 +03:00
|
|
|
|
import TextArea from '../../components/Common/TextArea';
|
2023-09-21 14:58:01 +03:00
|
|
|
|
import DataTable, { createColumnHelper } from '../../components/DataTable';
|
2023-09-25 14:17:52 +03:00
|
|
|
|
import { ArrowLeftIcon, ArrowRightIcon, CheckIcon, ChevronDoubleDownIcon, CrossIcon } from '../../components/Icons';
|
2023-09-21 23:09:51 +03:00
|
|
|
|
import { useConceptTheme } from '../../context/ThemeContext';
|
2023-09-25 14:17:52 +03:00
|
|
|
|
import useConceptText from '../../hooks/useConceptText';
|
2023-09-21 23:09:51 +03:00
|
|
|
|
import {
|
2023-09-27 23:36:51 +03:00
|
|
|
|
GramData, Grammeme, GrammemeGroups, ITextRequest, IWordForm,
|
|
|
|
|
IWordFormPlain, matchWordForm, NounGrams, parseGrammemes, VerbGrams
|
2023-09-21 23:09:51 +03:00
|
|
|
|
} from '../../models/language';
|
2023-09-21 14:58:01 +03:00
|
|
|
|
import { IConstituenta, TermForm } from '../../models/rsform';
|
2023-09-21 23:09:51 +03:00
|
|
|
|
import { colorfgGrammeme } from '../../utils/color';
|
2023-09-21 14:58:01 +03:00
|
|
|
|
import { labelGrammeme } from '../../utils/labels';
|
2023-09-27 23:36:51 +03:00
|
|
|
|
import { compareGrammemeOptions,IGrammemeOption, SelectorGrammemesList, SelectorGrammems } from '../../utils/selectors';
|
2023-09-11 20:31:54 +03:00
|
|
|
|
|
|
|
|
|
interface DlgEditTermProps {
|
|
|
|
|
hideWindow: () => void
|
|
|
|
|
target: IConstituenta
|
2023-09-21 14:58:01 +03:00
|
|
|
|
onSave: (data: TermForm[]) => void
|
2023-09-11 20:31:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 14:58:01 +03:00
|
|
|
|
const columnHelper = createColumnHelper<IWordForm>();
|
|
|
|
|
|
2023-09-21 23:09:51 +03:00
|
|
|
|
function DlgEditTerm({ hideWindow, target, onSave }: DlgEditTermProps) {
|
2023-09-25 14:17:52 +03:00
|
|
|
|
const textProcessor = useConceptText();
|
2023-09-21 23:09:51 +03:00
|
|
|
|
const { colors } = useConceptTheme();
|
2023-09-11 20:31:54 +03:00
|
|
|
|
const [term, setTerm] = useState('');
|
|
|
|
|
|
2023-09-19 17:55:17 +03:00
|
|
|
|
const [inputText, setInputText] = useState('');
|
2023-09-21 14:58:01 +03:00
|
|
|
|
const [inputGrams, setInputGrams] = useState<IGrammemeOption[]>([]);
|
|
|
|
|
const [options, setOptions] = useState<IGrammemeOption[]>([]);
|
2023-09-19 17:55:17 +03:00
|
|
|
|
|
2023-09-21 14:58:01 +03:00
|
|
|
|
const [forms, setForms] = useState<IWordForm[]>([]);
|
2023-09-19 17:55:17 +03:00
|
|
|
|
|
2023-09-21 14:58:01 +03:00
|
|
|
|
function getData(): TermForm[] {
|
|
|
|
|
const result: TermForm[] = [];
|
|
|
|
|
forms.forEach(
|
|
|
|
|
({text, grams}) => result.push({
|
|
|
|
|
text: text,
|
2023-09-27 23:36:51 +03:00
|
|
|
|
tags: grams.join(',')
|
2023-09-21 14:58:01 +03:00
|
|
|
|
}));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2023-09-11 20:31:54 +03:00
|
|
|
|
|
2023-09-21 14:58:01 +03:00
|
|
|
|
// Initialization
|
|
|
|
|
useLayoutEffect(
|
|
|
|
|
() => {
|
|
|
|
|
const initForms: IWordForm[] = [];
|
|
|
|
|
target.term_forms.forEach(
|
|
|
|
|
term => initForms.push({
|
|
|
|
|
text: term.text,
|
|
|
|
|
grams: parseGrammemes(term.tags),
|
|
|
|
|
}));
|
|
|
|
|
setForms(initForms);
|
|
|
|
|
setTerm(target.term_resolved);
|
2023-09-21 23:09:51 +03:00
|
|
|
|
setInputText(target.term_resolved);
|
2023-09-25 14:17:52 +03:00
|
|
|
|
setInputGrams([]);
|
2023-09-21 14:58:01 +03:00
|
|
|
|
}, [target]);
|
2023-09-11 20:31:54 +03:00
|
|
|
|
|
2023-09-21 14:58:01 +03:00
|
|
|
|
// Filter grammemes when input changes
|
|
|
|
|
useEffect(
|
|
|
|
|
() => {
|
2023-09-27 23:36:51 +03:00
|
|
|
|
let newFilter: GramData[] = [];
|
|
|
|
|
inputGrams.forEach(({value: gram}) => {
|
2023-09-21 14:58:01 +03:00
|
|
|
|
if (!newFilter.includes(gram)) {
|
2023-09-27 23:36:51 +03:00
|
|
|
|
if (NounGrams.includes(gram as Grammeme)) {
|
2023-09-21 14:58:01 +03:00
|
|
|
|
newFilter.push(...NounGrams);
|
|
|
|
|
}
|
2023-09-27 23:36:51 +03:00
|
|
|
|
if (VerbGrams.includes(gram as Grammeme)) {
|
2023-09-21 23:09:51 +03:00
|
|
|
|
newFilter.push(...VerbGrams);
|
2023-09-21 14:58:01 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2023-09-27 23:36:51 +03:00
|
|
|
|
inputGrams.forEach(({value: gram}) =>
|
2023-09-21 14:58:01 +03:00
|
|
|
|
GrammemeGroups.forEach(group => {
|
2023-09-27 23:36:51 +03:00
|
|
|
|
if (group.includes(gram as Grammeme)) {
|
|
|
|
|
newFilter = newFilter.filter(item => !group.includes(item as Grammeme) || item === gram);
|
2023-09-21 14:58:01 +03:00
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
2023-09-27 23:36:51 +03:00
|
|
|
|
newFilter.push(...inputGrams.map(({value}) => value));
|
2023-09-21 14:58:01 +03:00
|
|
|
|
if (newFilter.length === 0) {
|
|
|
|
|
newFilter = [...VerbGrams, ...NounGrams];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newFilter = [... new Set(newFilter)];
|
2023-09-27 23:36:51 +03:00
|
|
|
|
setOptions(SelectorGrammems.filter(({value}) => newFilter.includes(value)));
|
2023-09-21 14:58:01 +03:00
|
|
|
|
}, [inputGrams]);
|
|
|
|
|
|
|
|
|
|
const handleSubmit = () => onSave(getData());
|
2023-09-11 20:31:54 +03:00
|
|
|
|
|
2023-09-19 17:55:17 +03:00
|
|
|
|
function handleAddForm() {
|
2023-09-21 23:09:51 +03:00
|
|
|
|
const newForm: IWordForm = {
|
|
|
|
|
text: inputText,
|
2023-09-27 23:36:51 +03:00
|
|
|
|
grams: inputGrams.map(item => item.value)
|
2023-09-21 23:09:51 +03:00
|
|
|
|
};
|
2023-09-21 14:58:01 +03:00
|
|
|
|
setForms(forms => [
|
2023-09-25 14:17:52 +03:00
|
|
|
|
newForm,
|
|
|
|
|
...forms.filter(value => !matchWordForm(value, newForm))
|
2023-09-21 14:58:01 +03:00
|
|
|
|
]);
|
2023-09-19 17:55:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 23:09:51 +03:00
|
|
|
|
function handleDeleteRow(row: number) {
|
|
|
|
|
setForms(
|
|
|
|
|
(prev) => {
|
|
|
|
|
const newForms: IWordForm[] = [];
|
|
|
|
|
prev.forEach(
|
|
|
|
|
(form, index) => {
|
|
|
|
|
if (index !== row) {
|
|
|
|
|
newForms.push(form);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return newForms;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-25 14:17:52 +03:00
|
|
|
|
function handleRowClicked(form: IWordForm) {
|
|
|
|
|
setInputText(form.text);
|
2023-09-27 23:36:51 +03:00
|
|
|
|
setInputGrams(SelectorGrammems.filter(gram => form.grams.find(test => test === gram.value)));
|
2023-09-25 14:17:52 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-19 17:55:17 +03:00
|
|
|
|
function handleResetForm() {
|
2023-09-21 23:09:51 +03:00
|
|
|
|
setInputText('');
|
|
|
|
|
setInputGrams([]);
|
2023-09-19 17:55:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-25 14:17:52 +03:00
|
|
|
|
function handleInflect() {
|
|
|
|
|
const data: IWordFormPlain = {
|
|
|
|
|
text: term,
|
2023-09-27 23:36:51 +03:00
|
|
|
|
grams: inputGrams.map(gram => gram.value).join(',')
|
2023-09-25 14:17:52 +03:00
|
|
|
|
}
|
|
|
|
|
textProcessor.inflect(data, response => setInputText(response.result));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleParse() {
|
|
|
|
|
const data: ITextRequest = {
|
|
|
|
|
text: inputText
|
|
|
|
|
}
|
|
|
|
|
textProcessor.parse(data, response => {
|
|
|
|
|
const grams = parseGrammemes(response.result);
|
2023-09-27 23:36:51 +03:00
|
|
|
|
setInputGrams(SelectorGrammems.filter(gram => grams.find(test => test === gram.value)));
|
2023-09-25 14:17:52 +03:00
|
|
|
|
});
|
2023-09-19 17:55:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-25 14:17:52 +03:00
|
|
|
|
function handleGenerateLexeme() {
|
2023-09-21 23:09:51 +03:00
|
|
|
|
if (forms.length > 0) {
|
|
|
|
|
if (!window.confirm('Данное действие приведет к перезаписи словоформ при совпадении граммем. Продолжить?')) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-25 14:17:52 +03:00
|
|
|
|
const data: ITextRequest = {
|
|
|
|
|
text: inputText
|
|
|
|
|
}
|
|
|
|
|
textProcessor.generateLexeme(data, response => {
|
2023-09-28 16:31:10 +03:00
|
|
|
|
const lexeme: IWordForm[] = [];
|
|
|
|
|
response.items.forEach(
|
|
|
|
|
form => {
|
|
|
|
|
const newForm: IWordForm = {
|
|
|
|
|
text: form.text,
|
|
|
|
|
grams: parseGrammemes(form.grams).filter(gram => SelectorGrammemesList.find(item => item === gram as Grammeme))
|
|
|
|
|
}
|
|
|
|
|
if (newForm.grams.length === 2 && !lexeme.some(test => matchWordForm(test, newForm))) {
|
|
|
|
|
lexeme.push(newForm);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
setForms(lexeme);
|
2023-09-25 14:17:52 +03:00
|
|
|
|
});
|
2023-09-19 17:55:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 14:58:01 +03:00
|
|
|
|
const columns = useMemo(
|
|
|
|
|
() => [
|
|
|
|
|
columnHelper.accessor('text', {
|
|
|
|
|
id: 'text',
|
|
|
|
|
header: 'Текст',
|
|
|
|
|
size: 350,
|
|
|
|
|
minSize: 350,
|
2023-09-21 23:09:51 +03:00
|
|
|
|
maxSize: 350,
|
|
|
|
|
cell: props => <div className='min-w-[20rem]'>{props.getValue()}</div>
|
2023-09-21 14:58:01 +03:00
|
|
|
|
}),
|
|
|
|
|
columnHelper.accessor('grams', {
|
|
|
|
|
id: 'grams',
|
|
|
|
|
header: 'Граммемы',
|
|
|
|
|
size: 250,
|
|
|
|
|
minSize: 250,
|
|
|
|
|
maxSize: 250,
|
2023-09-21 23:09:51 +03:00
|
|
|
|
cell: props =>
|
2023-09-25 14:17:52 +03:00
|
|
|
|
<div className='flex flex-wrap justify-start gap-1 select-none'>
|
2023-09-21 14:58:01 +03:00
|
|
|
|
{ props.getValue().map(
|
2023-09-21 23:09:51 +03:00
|
|
|
|
gram =>
|
2023-09-21 14:58:01 +03:00
|
|
|
|
<div
|
2023-09-27 23:36:51 +03:00
|
|
|
|
key={`${props.cell.id}-${gram}`}
|
2023-09-21 23:09:51 +03:00
|
|
|
|
className='min-w-[3rem] px-1 text-sm text-center rounded-md whitespace-nowrap'
|
2023-09-21 14:58:01 +03:00
|
|
|
|
title=''
|
2023-09-21 23:09:51 +03:00
|
|
|
|
style={{
|
|
|
|
|
borderWidth: '1px',
|
2023-09-27 23:36:51 +03:00
|
|
|
|
borderColor: colorfgGrammeme(gram, colors),
|
|
|
|
|
color: colorfgGrammeme(gram, colors),
|
2023-09-21 23:09:51 +03:00
|
|
|
|
fontWeight: 600,
|
|
|
|
|
backgroundColor: colors.bgInput
|
|
|
|
|
}}
|
2023-09-21 14:58:01 +03:00
|
|
|
|
>
|
2023-09-21 23:09:51 +03:00
|
|
|
|
{labelGrammeme(gram)}
|
2023-09-21 14:58:01 +03:00
|
|
|
|
</div>
|
2023-09-21 23:09:51 +03:00
|
|
|
|
)}
|
|
|
|
|
</div>
|
2023-09-21 14:58:01 +03:00
|
|
|
|
}),
|
2023-09-21 23:09:51 +03:00
|
|
|
|
columnHelper.display({
|
|
|
|
|
id: 'actions',
|
|
|
|
|
size: 50,
|
|
|
|
|
minSize: 50,
|
|
|
|
|
maxSize: 50,
|
|
|
|
|
cell: props =>
|
|
|
|
|
<div>
|
|
|
|
|
<MiniButton
|
|
|
|
|
tooltip='Удалить словоформу'
|
|
|
|
|
icon={<CrossIcon size={4} color='text-warning'/>}
|
|
|
|
|
noHover
|
|
|
|
|
onClick={() => handleDeleteRow(props.row.index)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
})
|
|
|
|
|
], [colors]);
|
2023-09-11 20:31:54 +03:00
|
|
|
|
|
|
|
|
|
return (
|
2023-09-19 17:55:17 +03:00
|
|
|
|
<Modal
|
|
|
|
|
title='Редактирование словоформ'
|
|
|
|
|
hideWindow={hideWindow}
|
|
|
|
|
submitText='Сохранить данные'
|
|
|
|
|
canSubmit
|
|
|
|
|
onSubmit={handleSubmit}
|
|
|
|
|
>
|
|
|
|
|
<div className='min-w-[40rem]'>
|
|
|
|
|
<TextArea id='nominal' label='Начальная форма'
|
|
|
|
|
placeholder='Начальная форма'
|
|
|
|
|
rows={1}
|
|
|
|
|
|
|
|
|
|
value={term}
|
|
|
|
|
disabled={true}
|
|
|
|
|
spellCheck
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<Divider margins='my-4'/>
|
|
|
|
|
|
|
|
|
|
<div className='flex items-start gap-2 justify-stretch min-h-[6.3rem]'>
|
|
|
|
|
<div className='flex flex-col gap-1'>
|
|
|
|
|
<TextArea
|
|
|
|
|
placeholder='Введите текст'
|
|
|
|
|
rows={2}
|
2023-09-25 14:17:52 +03:00
|
|
|
|
dimensions='min-w-[20rem] min-h-[4.2rem]'
|
|
|
|
|
|
|
|
|
|
disabled={textProcessor.loading}
|
2023-09-19 17:55:17 +03:00
|
|
|
|
value={inputText}
|
|
|
|
|
onChange={event => setInputText(event.target.value)}
|
2023-09-14 16:53:38 +03:00
|
|
|
|
/>
|
2023-09-27 23:36:51 +03:00
|
|
|
|
<div className='flex items-center justify-between select-none'>
|
2023-09-25 14:17:52 +03:00
|
|
|
|
<div className='flex items-center justify-start'>
|
|
|
|
|
<MiniButton
|
|
|
|
|
tooltip='Добавить словоформу'
|
|
|
|
|
icon={<CheckIcon size={6} color={!inputText || inputGrams.length == 0 ? 'text-disabled' : 'text-success'}/>}
|
|
|
|
|
disabled={textProcessor.loading || !inputText || inputGrams.length == 0}
|
|
|
|
|
onClick={handleAddForm}
|
|
|
|
|
/>
|
|
|
|
|
<MiniButton
|
|
|
|
|
tooltip='Сбросить словоформу'
|
|
|
|
|
icon={<CrossIcon size={6} color='text-warning'/>}
|
|
|
|
|
disabled={textProcessor.loading}
|
|
|
|
|
onClick={handleResetForm}
|
|
|
|
|
/>
|
|
|
|
|
<MiniButton
|
|
|
|
|
tooltip='Генерировать все словоформы'
|
|
|
|
|
icon={<ChevronDoubleDownIcon size={6} color='text-primary'/>}
|
|
|
|
|
disabled={textProcessor.loading}
|
|
|
|
|
onClick={handleGenerateLexeme}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2023-09-27 23:36:51 +03:00
|
|
|
|
<div className='text-sm'>
|
|
|
|
|
Словоформ: {forms.length}
|
|
|
|
|
</div>
|
2023-09-25 14:17:52 +03:00
|
|
|
|
<div className='flex items-center justify-start'>
|
|
|
|
|
<MiniButton
|
|
|
|
|
tooltip='Генерировать словоформу'
|
|
|
|
|
icon={<ArrowLeftIcon size={6} color={inputGrams.length == 0 ? 'text-disabled' : 'text-primary'}/>}
|
|
|
|
|
disabled={textProcessor.loading || inputGrams.length == 0}
|
|
|
|
|
onClick={handleInflect}
|
|
|
|
|
/>
|
|
|
|
|
<MiniButton
|
|
|
|
|
tooltip='Определить граммемы'
|
|
|
|
|
icon={<ArrowRightIcon size={6} color={!inputText ? 'text-disabled' : 'text-primary'}/>}
|
|
|
|
|
disabled={textProcessor.loading || !inputText}
|
|
|
|
|
onClick={handleParse}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2023-09-19 17:55:17 +03:00
|
|
|
|
</div>
|
2023-09-14 16:53:38 +03:00
|
|
|
|
</div>
|
2023-09-19 17:55:17 +03:00
|
|
|
|
<SelectMulti
|
|
|
|
|
className='z-modal-top min-w-[20rem] max-w-[20rem] h-full flex-grow'
|
2023-09-21 14:58:01 +03:00
|
|
|
|
options={options}
|
2023-09-19 17:55:17 +03:00
|
|
|
|
placeholder='Выберите граммемы'
|
|
|
|
|
|
2023-09-21 14:58:01 +03:00
|
|
|
|
value={inputGrams}
|
2023-09-25 14:17:52 +03:00
|
|
|
|
isDisabled={textProcessor.loading}
|
2023-09-27 23:36:51 +03:00
|
|
|
|
onChange={newValue => setInputGrams([...newValue].sort(compareGrammemeOptions))}
|
2023-09-19 17:55:17 +03:00
|
|
|
|
/>
|
|
|
|
|
</div>
|
2023-09-21 14:58:01 +03:00
|
|
|
|
|
2023-09-21 23:09:51 +03:00
|
|
|
|
<div className='border overflow-y-auto max-h-[17.4rem] min-h-[17.4rem]'>
|
2023-09-21 14:58:01 +03:00
|
|
|
|
<DataTable
|
|
|
|
|
data={forms}
|
|
|
|
|
columns={columns}
|
|
|
|
|
dense
|
|
|
|
|
|
|
|
|
|
noDataComponent={
|
|
|
|
|
<span className='flex flex-col justify-center p-2 text-center min-h-[2rem]'>
|
|
|
|
|
<p>Список пуст</p>
|
2023-09-21 23:09:51 +03:00
|
|
|
|
<p>Добавьте словоформу</p>
|
2023-09-21 14:58:01 +03:00
|
|
|
|
</span>
|
|
|
|
|
}
|
2023-09-25 14:17:52 +03:00
|
|
|
|
|
|
|
|
|
onRowDoubleClicked={handleRowClicked}
|
2023-09-21 14:58:01 +03:00
|
|
|
|
/>
|
2023-09-14 16:53:38 +03:00
|
|
|
|
</div>
|
2023-09-19 17:55:17 +03:00
|
|
|
|
</div>
|
|
|
|
|
</Modal>);
|
2023-09-11 20:31:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default DlgEditTerm;
|