mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Implementing inline synthesis pt2
This commit is contained in:
parent
bcbe35b436
commit
71e87ac9c5
145
rsconcept/frontend/src/components/ConstituentaMultiPicker.tsx
Normal file
145
rsconcept/frontend/src/components/ConstituentaMultiPicker.tsx
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import DataTable, { createColumnHelper, RowSelectionState } from '@/components/DataTable';
|
||||||
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
|
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
|
import { describeConstituenta } from '@/utils/labels';
|
||||||
|
|
||||||
|
import ConstituentaBadge from './ConstituentaBadge';
|
||||||
|
import Button from './ui/Button';
|
||||||
|
import FlexColumn from './ui/FlexColumn';
|
||||||
|
|
||||||
|
interface ConstituentaMultiPickerProps {
|
||||||
|
id?: string;
|
||||||
|
schema?: IRSForm;
|
||||||
|
prefixID: string;
|
||||||
|
rows?: number;
|
||||||
|
|
||||||
|
selected: ConstituentaID[];
|
||||||
|
setSelected: React.Dispatch<ConstituentaID[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<IConstituenta>();
|
||||||
|
|
||||||
|
function ConstituentaMultiPicker({ id, schema, prefixID, rows, selected, setSelected }: ConstituentaMultiPickerProps) {
|
||||||
|
const { colors } = useConceptTheme();
|
||||||
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (!schema || selected.length === 0) {
|
||||||
|
setRowSelection({});
|
||||||
|
} else {
|
||||||
|
const newRowSelection: RowSelectionState = {};
|
||||||
|
schema.items.forEach((cst, index) => {
|
||||||
|
newRowSelection[String(index)] = selected.includes(cst.id);
|
||||||
|
});
|
||||||
|
setRowSelection(newRowSelection);
|
||||||
|
}
|
||||||
|
}, [selected, schema]);
|
||||||
|
|
||||||
|
function handleRowSelection(updater: React.SetStateAction<RowSelectionState>) {
|
||||||
|
if (!schema) {
|
||||||
|
setSelected([]);
|
||||||
|
} else {
|
||||||
|
const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater;
|
||||||
|
const newSelection: ConstituentaID[] = [];
|
||||||
|
schema.items.forEach((cst, index) => {
|
||||||
|
if (newRowSelection[String(index)] === true) {
|
||||||
|
newSelection.push(cst.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setSelected(newSelection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectBasis = useCallback(() => {
|
||||||
|
if (!schema || selected.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const addition = schema.graph.expandInputs(selected).filter(id => !selected.includes(id));
|
||||||
|
if (addition.length > 0) {
|
||||||
|
setSelected([...selected, ...addition]);
|
||||||
|
}
|
||||||
|
}, [schema, selected, setSelected]);
|
||||||
|
|
||||||
|
const selectDependant = useCallback(() => {
|
||||||
|
if (!schema || selected.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const addition = schema.graph.expandOutputs(selected).filter(id => !selected.includes(id));
|
||||||
|
if (addition.length > 0) {
|
||||||
|
setSelected([...selected, ...addition]);
|
||||||
|
}
|
||||||
|
}, [schema, selected, setSelected]);
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() => [
|
||||||
|
columnHelper.accessor('alias', {
|
||||||
|
id: 'alias',
|
||||||
|
header: 'Имя',
|
||||||
|
size: 65,
|
||||||
|
cell: props => <ConstituentaBadge theme={colors} value={props.row.original} prefixID={prefixID} />
|
||||||
|
}),
|
||||||
|
columnHelper.accessor(cst => describeConstituenta(cst), {
|
||||||
|
id: 'description',
|
||||||
|
size: 1000,
|
||||||
|
header: 'Описание'
|
||||||
|
})
|
||||||
|
],
|
||||||
|
[colors, prefixID]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='flex gap-3 items-end mb-3'>
|
||||||
|
<span className='w-[24ch] select-none whitespace-nowrap'>
|
||||||
|
Выбраны {selected.length} из {schema?.items.length ?? 0}
|
||||||
|
</span>
|
||||||
|
<div className='flex gap-6 w-full text-sm'>
|
||||||
|
<Button
|
||||||
|
text='Поставщики'
|
||||||
|
title='Добавить все конституенты, от которых зависят выбранные'
|
||||||
|
className='w-[7rem]'
|
||||||
|
onClick={selectBasis}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
text='Потребители'
|
||||||
|
title='Добавить все конституенты, которые зависят от выбранных'
|
||||||
|
className='w-[7rem]'
|
||||||
|
onClick={selectDependant}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DataTable
|
||||||
|
id={id}
|
||||||
|
dense
|
||||||
|
noFooter
|
||||||
|
rows={rows}
|
||||||
|
contentHeight='1.3rem'
|
||||||
|
className={clsx(
|
||||||
|
'min-h-[16rem]', // prettier: split lines
|
||||||
|
'overflow-y-auto',
|
||||||
|
'border',
|
||||||
|
'text-sm',
|
||||||
|
'select-none'
|
||||||
|
)}
|
||||||
|
data={schema?.items ?? []}
|
||||||
|
columns={columns}
|
||||||
|
headPosition='0rem'
|
||||||
|
enableRowSelection
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
onRowSelectionChange={handleRowSelection}
|
||||||
|
noDataComponent={
|
||||||
|
<FlexColumn className='items-center p-3'>
|
||||||
|
<p>Список пуст</p>
|
||||||
|
</FlexColumn>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConstituentaMultiPicker;
|
|
@ -1,3 +1,5 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/DataTable';
|
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/DataTable';
|
||||||
|
@ -14,7 +16,7 @@ import FlexColumn from './ui/FlexColumn';
|
||||||
|
|
||||||
interface ConstituentaPickerProps {
|
interface ConstituentaPickerProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
prefixID?: string;
|
prefixID: string;
|
||||||
data?: IConstituenta[];
|
data?: IConstituenta[];
|
||||||
rows?: number;
|
rows?: number;
|
||||||
|
|
||||||
|
@ -74,8 +76,6 @@ function ConstituentaPicker({
|
||||||
[colors, prefixID, describeFunc]
|
[colors, prefixID, describeFunc]
|
||||||
);
|
);
|
||||||
|
|
||||||
const size = useMemo(() => `calc(2px + (2px + 1.8rem)*${rows})`, [rows]);
|
|
||||||
|
|
||||||
const conditionalRowStyles = useMemo(
|
const conditionalRowStyles = useMemo(
|
||||||
(): IConditionalStyle<IConstituenta>[] => [
|
(): IConditionalStyle<IConstituenta>[] => [
|
||||||
{
|
{
|
||||||
|
@ -96,11 +96,12 @@ function ConstituentaPicker({
|
||||||
/>
|
/>
|
||||||
<DataTable
|
<DataTable
|
||||||
id={id}
|
id={id}
|
||||||
|
rows={rows}
|
||||||
|
contentHeight='1.3rem'
|
||||||
dense
|
dense
|
||||||
noHeader
|
noHeader
|
||||||
noFooter
|
noFooter
|
||||||
className='overflow-y-auto text-sm select-none'
|
className='overflow-y-auto text-sm select-none'
|
||||||
style={{ maxHeight: size, minHeight: size }}
|
|
||||||
data={filteredData}
|
data={filteredData}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
conditionalRowStyles={conditionalRowStyles}
|
conditionalRowStyles={conditionalRowStyles}
|
||||||
|
|
|
@ -2,28 +2,34 @@ import { AnimatePresence } from 'framer-motion';
|
||||||
|
|
||||||
import AnimateFade from './AnimateFade';
|
import AnimateFade from './AnimateFade';
|
||||||
import InfoError, { ErrorData } from './InfoError';
|
import InfoError, { ErrorData } from './InfoError';
|
||||||
|
import { CProps } from './props';
|
||||||
import Loader from './ui/Loader';
|
import Loader from './ui/Loader';
|
||||||
|
|
||||||
interface DataLoaderProps {
|
interface DataLoaderProps extends CProps.AnimatedDiv {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
isLoading: boolean;
|
isLoading?: boolean;
|
||||||
error?: ErrorData;
|
error?: ErrorData;
|
||||||
hasNoData?: boolean;
|
hasNoData?: boolean;
|
||||||
|
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DataLoader({ id, isLoading, hasNoData, error, children }: DataLoaderProps) {
|
function DataLoader({ id, isLoading, hasNoData, error, children, ...restProps }: DataLoaderProps) {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence mode='wait'>
|
<AnimatePresence mode='wait'>
|
||||||
{isLoading ? <Loader key={`${id}-loader`} /> : null}
|
{isLoading ? <Loader key={`${id}-loader`} /> : null}
|
||||||
{error ? <InfoError key={`${id}-error`} error={error} /> : null}
|
{error ? <InfoError key={`${id}-error`} error={error} /> : null}
|
||||||
{!isLoading && !error && !hasNoData ? (
|
{!isLoading && !error && !hasNoData ? (
|
||||||
<AnimateFade id={id} key={`${id}-data`}>
|
<AnimateFade id={id} key={`${id}-data`} {...restProps}>
|
||||||
{children}
|
{children}
|
||||||
</AnimateFade>
|
</AnimateFade>
|
||||||
) : null}
|
) : null}
|
||||||
|
{!isLoading && !error && hasNoData ? (
|
||||||
|
<AnimateFade id={id} key={`${id}-data`} {...restProps}>
|
||||||
|
Данные не загружены
|
||||||
|
</AnimateFade>
|
||||||
|
) : null}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ export interface DataTableProps<TData extends RowData>
|
||||||
id?: string;
|
id?: string;
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
rows?: number;
|
rows?: number;
|
||||||
|
contentHeight?: string;
|
||||||
headPosition?: string;
|
headPosition?: string;
|
||||||
noHeader?: boolean;
|
noHeader?: boolean;
|
||||||
noFooter?: boolean;
|
noFooter?: boolean;
|
||||||
|
@ -73,6 +74,7 @@ function DataTable<TData extends RowData>({
|
||||||
className,
|
className,
|
||||||
dense,
|
dense,
|
||||||
rows,
|
rows,
|
||||||
|
contentHeight = '1.1875rem',
|
||||||
headPosition,
|
headPosition,
|
||||||
conditionalRowStyles,
|
conditionalRowStyles,
|
||||||
noFooter,
|
noFooter,
|
||||||
|
@ -130,11 +132,11 @@ function DataTable<TData extends RowData>({
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (dense) {
|
if (dense) {
|
||||||
return `calc(2px + (2px + 1.6875rem)*${rows} + ${noHeader ? '0px' : '(2px + 2.1875rem)'})`;
|
return `calc(2px + (2px + ${contentHeight} + 0.5rem)*${rows} + ${noHeader ? '0px' : '(2px + 2.1875rem)'})`;
|
||||||
} else {
|
} else {
|
||||||
return `calc(2px + (2px + 2.1875rem)*${rows + (noHeader ? 0 : 1)})`;
|
return `calc(2px + (2px + ${contentHeight} + 1rem)*${rows + (noHeader ? 0 : 1)})`;
|
||||||
}
|
}
|
||||||
}, [rows, dense, noHeader]);
|
}, [rows, dense, noHeader, contentHeight]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id={id} className={className} style={{ minHeight: fixedSize, maxHeight: fixedSize, ...style }}>
|
<div id={id} className={className} style={{ minHeight: fixedSize, maxHeight: fixedSize, ...style }}>
|
||||||
|
|
|
@ -19,34 +19,6 @@ import { RSLanguage } from './rslang';
|
||||||
import { getSymbolSubstitute, RSTextWrapper } from './textEditing';
|
import { getSymbolSubstitute, RSTextWrapper } from './textEditing';
|
||||||
import { rsHoverTooltip } from './tooltip';
|
import { rsHoverTooltip } from './tooltip';
|
||||||
|
|
||||||
const editorSetup: BasicSetupOptions = {
|
|
||||||
highlightSpecialChars: false,
|
|
||||||
history: true,
|
|
||||||
drawSelection: true,
|
|
||||||
syntaxHighlighting: false,
|
|
||||||
defaultKeymap: true,
|
|
||||||
historyKeymap: true,
|
|
||||||
|
|
||||||
lineNumbers: false,
|
|
||||||
highlightActiveLineGutter: false,
|
|
||||||
foldGutter: false,
|
|
||||||
dropCursor: true,
|
|
||||||
allowMultipleSelections: false,
|
|
||||||
indentOnInput: false,
|
|
||||||
bracketMatching: false,
|
|
||||||
closeBrackets: false,
|
|
||||||
autocompletion: false,
|
|
||||||
rectangularSelection: false,
|
|
||||||
crosshairCursor: false,
|
|
||||||
highlightActiveLine: false,
|
|
||||||
highlightSelectionMatches: false,
|
|
||||||
closeBracketsKeymap: false,
|
|
||||||
searchKeymap: false,
|
|
||||||
foldKeymap: false,
|
|
||||||
completionKeymap: false,
|
|
||||||
lintKeymap: false
|
|
||||||
};
|
|
||||||
|
|
||||||
interface RSInputProps
|
interface RSInputProps
|
||||||
extends Pick<
|
extends Pick<
|
||||||
ReactCodeMirrorProps,
|
ReactCodeMirrorProps,
|
||||||
|
@ -60,7 +32,22 @@ interface RSInputProps
|
||||||
}
|
}
|
||||||
|
|
||||||
const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
||||||
({ id, label, onChange, onAnalyze, disabled, noTooltip, className, style, ...restProps }, ref) => {
|
(
|
||||||
|
{
|
||||||
|
id, // prettier: split lines
|
||||||
|
label,
|
||||||
|
disabled,
|
||||||
|
noTooltip,
|
||||||
|
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
|
||||||
|
onChange,
|
||||||
|
onAnalyze,
|
||||||
|
...restProps
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
const { darkMode, colors } = useConceptTheme();
|
const { darkMode, colors } = useConceptTheme();
|
||||||
const { schema } = useRSForm();
|
const { schema } = useRSForm();
|
||||||
|
|
||||||
|
@ -167,3 +154,32 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
||||||
);
|
);
|
||||||
|
|
||||||
export default RSInput;
|
export default RSInput;
|
||||||
|
|
||||||
|
// ======= Internal ==========
|
||||||
|
const editorSetup: BasicSetupOptions = {
|
||||||
|
highlightSpecialChars: false,
|
||||||
|
history: true,
|
||||||
|
drawSelection: true,
|
||||||
|
syntaxHighlighting: false,
|
||||||
|
defaultKeymap: true,
|
||||||
|
historyKeymap: true,
|
||||||
|
|
||||||
|
lineNumbers: false,
|
||||||
|
highlightActiveLineGutter: false,
|
||||||
|
foldGutter: false,
|
||||||
|
dropCursor: true,
|
||||||
|
allowMultipleSelections: false,
|
||||||
|
indentOnInput: false,
|
||||||
|
bracketMatching: false,
|
||||||
|
closeBrackets: false,
|
||||||
|
autocompletion: false,
|
||||||
|
rectangularSelection: false,
|
||||||
|
crosshairCursor: false,
|
||||||
|
highlightActiveLine: false,
|
||||||
|
highlightSelectionMatches: false,
|
||||||
|
closeBracketsKeymap: false,
|
||||||
|
searchKeymap: false,
|
||||||
|
foldKeymap: false,
|
||||||
|
completionKeymap: false,
|
||||||
|
lintKeymap: false
|
||||||
|
};
|
||||||
|
|
|
@ -76,7 +76,7 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
|
||||||
id='dlg_cst_term'
|
id='dlg_cst_term'
|
||||||
spellCheck
|
spellCheck
|
||||||
label='Термин'
|
label='Термин'
|
||||||
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
|
placeholder='Обозначение, используемое в текстовых определениях'
|
||||||
rows={2}
|
rows={2}
|
||||||
value={cstData.term_raw}
|
value={cstData.term_raw}
|
||||||
onChange={event => updateCstData({ term_raw: event.target.value })}
|
onChange={event => updateCstData({ term_raw: event.target.value })}
|
||||||
|
@ -84,8 +84,7 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
|
||||||
<RSInput
|
<RSInput
|
||||||
id='dlg_cst_expression'
|
id='dlg_cst_expression'
|
||||||
label='Формальное определение'
|
label='Формальное определение'
|
||||||
placeholder='Родоструктурное выражение, задающее формальное определение'
|
placeholder='Родоструктурное выражение'
|
||||||
height='5.1rem'
|
|
||||||
value={cstData.definition_formal}
|
value={cstData.definition_formal}
|
||||||
onChange={value => updateCstData({ definition_formal: value })}
|
onChange={value => updateCstData({ definition_formal: value })}
|
||||||
/>
|
/>
|
||||||
|
@ -93,7 +92,7 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
|
||||||
id='dlg_cst_definition'
|
id='dlg_cst_definition'
|
||||||
spellCheck
|
spellCheck
|
||||||
label='Текстовое определение'
|
label='Текстовое определение'
|
||||||
placeholder='Лингвистическая интерпретация формального выражения'
|
placeholder='Текстовая интерпретация формального выражения'
|
||||||
rows={2}
|
rows={2}
|
||||||
value={cstData.definition_raw}
|
value={cstData.definition_raw}
|
||||||
onChange={event => updateCstData({ definition_raw: event.target.value })}
|
onChange={event => updateCstData({ definition_raw: event.target.value })}
|
||||||
|
|
|
@ -1,18 +1,31 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { LibraryItemID } from '@/models/library';
|
import ConstituentaMultiPicker from '@/components/ConstituentaMultiPicker';
|
||||||
import { IRSForm } from '@/models/rsform';
|
import DataLoader from '@/components/DataLoader';
|
||||||
|
import { ErrorData } from '@/components/InfoError';
|
||||||
|
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||||
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
interface ConstituentsTabProps {
|
interface ConstituentsTabProps {
|
||||||
schema?: IRSForm;
|
schema?: IRSForm;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
selected: LibraryItemID[];
|
error?: ErrorData;
|
||||||
setSelected: React.Dispatch<LibraryItemID[]>;
|
selected: ConstituentaID[];
|
||||||
|
setSelected: React.Dispatch<ConstituentaID[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// { schema, loading, selected, setSelected }: ConstituentsTabProps
|
function ConstituentsTab({ schema, error, loading, selected, setSelected }: ConstituentsTabProps) {
|
||||||
function ConstituentsTab(props: ConstituentsTabProps) {
|
return (
|
||||||
return <>2 - {props.loading}</>;
|
<DataLoader id='dlg-constituents-tab' isLoading={loading} error={error} hasNoData={!schema}>
|
||||||
|
<ConstituentaMultiPicker
|
||||||
|
schema={schema}
|
||||||
|
rows={16}
|
||||||
|
prefixID={prefixes.cst_inline_synth_list}
|
||||||
|
selected={selected}
|
||||||
|
setSelected={setSelected}
|
||||||
|
/>
|
||||||
|
</DataLoader>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConstituentsTab;
|
export default ConstituentsTab;
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import SchemaPicker from '@/components/SchemaPicker';
|
import SchemaPicker from '@/components/SchemaPicker';
|
||||||
import FlexColumn from '@/components/ui/FlexColumn';
|
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { LibraryItemID } from '@/models/library';
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
@ -18,18 +17,21 @@ function SchemaTab({ selected, setSelected }: SchemaTabProps) {
|
||||||
const selectedInfo = useMemo(() => library.items.find(item => item.id === selected), [selected, library.items]);
|
const selectedInfo = useMemo(() => library.items.find(item => item.id === selected), [selected, library.items]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlexColumn>
|
<div className='flex flex-col'>
|
||||||
<TextInput
|
<div className='flex gap-6 items-center'>
|
||||||
id='dlg_selected_schema_title'
|
<span className='select-none'>Выбрана</span>
|
||||||
label='Выбрана'
|
<TextInput
|
||||||
noBorder
|
id='dlg_selected_schema_title'
|
||||||
placeholder='Выберите из списка ниже'
|
disabled
|
||||||
value={selectedInfo?.title}
|
noBorder
|
||||||
disabled
|
className='w-full'
|
||||||
dense
|
placeholder='Выберите из списка ниже'
|
||||||
/>
|
value={selectedInfo?.title ?? ''}
|
||||||
|
dense
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<SchemaPicker rows={16} value={selected} onSelectValue={setSelected} />
|
<SchemaPicker rows={16} value={selected} onSelectValue={setSelected} />
|
||||||
</FlexColumn>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ function FormConstituenta({
|
||||||
<RefsInput
|
<RefsInput
|
||||||
id='cst_term'
|
id='cst_term'
|
||||||
label='Термин'
|
label='Термин'
|
||||||
placeholder='Обозначение, используемое в текстовых определениях данной схемы'
|
placeholder='Обозначение, используемое в текстовых определениях'
|
||||||
items={schema?.items}
|
items={schema?.items}
|
||||||
value={term}
|
value={term}
|
||||||
initialValue={constituenta?.term_raw ?? ''}
|
initialValue={constituenta?.term_raw ?? ''}
|
||||||
|
@ -165,7 +165,7 @@ function FormConstituenta({
|
||||||
<RefsInput
|
<RefsInput
|
||||||
id='cst_definition'
|
id='cst_definition'
|
||||||
label='Текстовое определение'
|
label='Текстовое определение'
|
||||||
placeholder='Текстовый вариант формального определения'
|
placeholder='Текстовая интерпретация формального выражения'
|
||||||
height='3.8rem'
|
height='3.8rem'
|
||||||
items={schema?.items}
|
items={schema?.items}
|
||||||
value={textDefinition}
|
value={textDefinition}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { type RowSelectionState } from '@/components/DataTable';
|
import { type RowSelectionState } from '@/components/DataTable';
|
||||||
import SelectedCounter from '@/components/SelectedCounter';
|
import SelectedCounter from '@/components/SelectedCounter';
|
||||||
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
import { ConstituentaID, CstType } from '@/models/rsform';
|
import { ConstituentaID, CstType } from '@/models/rsform';
|
||||||
|
|
||||||
import { useRSEdit } from '../RSEditContext';
|
import { useRSEdit } from '../RSEditContext';
|
||||||
|
@ -18,6 +19,7 @@ interface EditorRSListProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps) {
|
function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps) {
|
||||||
|
const { calculateHeight } = useConceptTheme();
|
||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||||
const controller = useRSEdit();
|
const controller = useRSEdit();
|
||||||
|
|
||||||
|
@ -91,6 +93,8 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tableHeight = useMemo(() => calculateHeight('4.05rem + 5px'), [calculateHeight]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div tabIndex={-1} className='outline-none' onKeyDown={handleTableKey}>
|
<div tabIndex={-1} className='outline-none' onKeyDown={handleTableKey}>
|
||||||
{controller.isContentEditable || controller.isProcessing ? (
|
{controller.isContentEditable || controller.isProcessing ? (
|
||||||
|
@ -112,8 +116,9 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps)
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<RSTable
|
<RSTable
|
||||||
enableSelection={controller.isContentEditable || controller.isProcessing}
|
|
||||||
items={controller.schema?.items}
|
items={controller.schema?.items}
|
||||||
|
maxHeight={tableHeight}
|
||||||
|
enableSelection={controller.isContentEditable || controller.isProcessing}
|
||||||
selected={rowSelection}
|
selected={rowSelection}
|
||||||
setSelected={handleRowSelection}
|
setSelected={handleRowSelection}
|
||||||
onEdit={onOpenEdit}
|
onEdit={onOpenEdit}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { labelCstTypification } from '@/utils/labels';
|
||||||
interface RSTableProps {
|
interface RSTableProps {
|
||||||
items?: IConstituenta[];
|
items?: IConstituenta[];
|
||||||
enableSelection: boolean;
|
enableSelection: boolean;
|
||||||
|
maxHeight?: string;
|
||||||
selected: RowSelectionState;
|
selected: RowSelectionState;
|
||||||
setSelected: React.Dispatch<React.SetStateAction<RowSelectionState>>;
|
setSelected: React.Dispatch<React.SetStateAction<RowSelectionState>>;
|
||||||
|
|
||||||
|
@ -29,8 +30,8 @@ const COLUMN_CONVENTION_HIDE_THRESHOLD = 1800;
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<IConstituenta>();
|
const columnHelper = createColumnHelper<IConstituenta>();
|
||||||
|
|
||||||
function RSTable({ items, enableSelection, selected, setSelected, onEdit, onCreateNew }: RSTableProps) {
|
function RSTable({ items, maxHeight, enableSelection, selected, setSelected, onEdit, onCreateNew }: RSTableProps) {
|
||||||
const { colors, calculateHeight } = useConceptTheme();
|
const { colors } = useConceptTheme();
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
||||||
|
@ -115,14 +116,12 @@ function RSTable({ items, enableSelection, selected, setSelected, onEdit, onCrea
|
||||||
[colors]
|
[colors]
|
||||||
);
|
);
|
||||||
|
|
||||||
const tableHeight = useMemo(() => calculateHeight('4.05rem + 5px'), [calculateHeight]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<DataTable
|
||||||
dense
|
dense
|
||||||
noFooter
|
noFooter
|
||||||
className={clsx('min-h-[16rem]', 'overflow-y-auto', 'text-sm', 'select-none')}
|
className={clsx('min-h-[16rem]', 'overflow-y-auto', 'text-sm', 'select-none')}
|
||||||
style={{ maxHeight: tableHeight }}
|
style={{ maxHeight: maxHeight }}
|
||||||
data={items ?? []}
|
data={items ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
headPosition='0rem'
|
headPosition='0rem'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user