mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 21:10:38 +03:00
Refactor constituenta selection and tooltip visibility
This commit is contained in:
parent
5b3862d46c
commit
2b7a4c04ce
|
@ -1,19 +1,19 @@
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
|
|
||||||
interface SelectedCounterProps {
|
interface SelectedCounterProps {
|
||||||
total: number;
|
totalCount: number;
|
||||||
selected: number;
|
selectedCount: number;
|
||||||
position?: string;
|
position?: string;
|
||||||
hideZero?: boolean;
|
hideZero?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectedCounter({ total, selected, hideZero, position = 'top-0 left-0' }: SelectedCounterProps) {
|
function SelectedCounter({ totalCount, selectedCount, hideZero, position = 'top-0 left-0' }: SelectedCounterProps) {
|
||||||
if (selected === 0 && hideZero) {
|
if (selectedCount === 0 && hideZero) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Overlay position={`px-2 ${position}`} className='select-none whitespace-nowrap clr-app'>
|
<Overlay position={`px-2 ${position}`} className='select-none whitespace-nowrap clr-app'>
|
||||||
Выбор {selected} из {total}
|
Выбор {selectedCount} из {totalCount}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ interface ButtonProps extends CProps.Control, CProps.Colors, CProps.Button {
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
|
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
|
hideTitle?: boolean;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ function Button({
|
||||||
loading,
|
loading,
|
||||||
dense,
|
dense,
|
||||||
disabled,
|
disabled,
|
||||||
|
hideTitle,
|
||||||
noBorder,
|
noBorder,
|
||||||
noOutline,
|
noOutline,
|
||||||
colors = 'clr-btn-default',
|
colors = 'clr-btn-default',
|
||||||
|
@ -46,6 +48,7 @@ function Button({
|
||||||
)}
|
)}
|
||||||
data-tooltip-id={title ? globalIDs.tooltip : undefined}
|
data-tooltip-id={title ? globalIDs.tooltip : undefined}
|
||||||
data-tooltip-content={title}
|
data-tooltip-content={title}
|
||||||
|
data-tooltip-hidden={hideTitle}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{icon ? icon : null}
|
{icon ? icon : null}
|
||||||
|
|
|
@ -7,9 +7,10 @@ import { CProps } from '../props';
|
||||||
interface MiniButtonProps extends CProps.Button {
|
interface MiniButtonProps extends CProps.Button {
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
noHover?: boolean;
|
noHover?: boolean;
|
||||||
|
hideTitle?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function MiniButton({ icon, noHover, tabIndex, title, className, ...restProps }: MiniButtonProps) {
|
function MiniButton({ icon, noHover, hideTitle, tabIndex, title, className, ...restProps }: MiniButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
|
@ -27,6 +28,7 @@ function MiniButton({ icon, noHover, tabIndex, title, className, ...restProps }:
|
||||||
)}
|
)}
|
||||||
data-tooltip-id={title ? globalIDs.tooltip : undefined}
|
data-tooltip-id={title ? globalIDs.tooltip : undefined}
|
||||||
data-tooltip-content={title}
|
data-tooltip-content={title}
|
||||||
|
data-tooltip-hidden={hideTitle}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
|
|
|
@ -10,6 +10,7 @@ interface SelectorButtonProps extends CProps.Button {
|
||||||
|
|
||||||
colors?: string;
|
colors?: string;
|
||||||
transparent?: boolean;
|
transparent?: boolean;
|
||||||
|
hideTitle?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectorButton({
|
function SelectorButton({
|
||||||
|
@ -19,13 +20,12 @@ function SelectorButton({
|
||||||
colors = 'clr-btn-default',
|
colors = 'clr-btn-default',
|
||||||
className,
|
className,
|
||||||
transparent,
|
transparent,
|
||||||
|
hideTitle,
|
||||||
...restProps
|
...restProps
|
||||||
}: SelectorButtonProps) {
|
}: SelectorButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
data-tooltip-id={title ? globalIDs.tooltip : undefined}
|
|
||||||
data-tooltip-content={title}
|
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'px-1 flex flex-start items-center gap-1',
|
'px-1 flex flex-start items-center gap-1',
|
||||||
'text-sm font-controls select-none',
|
'text-sm font-controls select-none',
|
||||||
|
@ -38,6 +38,9 @@ function SelectorButton({
|
||||||
className,
|
className,
|
||||||
!transparent && colors
|
!transparent && colors
|
||||||
)}
|
)}
|
||||||
|
data-tooltip-id={title ? globalIDs.tooltip : undefined}
|
||||||
|
data-tooltip-content={title}
|
||||||
|
data-tooltip-hidden={hideTitle}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{icon ? icon : null}
|
{icon ? icon : null}
|
||||||
|
|
|
@ -47,6 +47,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
transparent
|
transparent
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title='Список фильтров'
|
title='Список фильтров'
|
||||||
|
hideTitle={strategyMenu.isOpen}
|
||||||
className='h-full'
|
className='h-full'
|
||||||
icon={<BiFilterAlt size='1.25rem' />}
|
icon={<BiFilterAlt size='1.25rem' />}
|
||||||
text={labelLibraryFilter(value)}
|
text={labelLibraryFilter(value)}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { AnimatePresence } from 'framer-motion';
|
import { AnimatePresence } from 'framer-motion';
|
||||||
import { Dispatch, SetStateAction, useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { CstType, IConstituenta, ICstCreateData, ICstRenameData } from '@/models/rsform';
|
import { CstType, IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
import { globalIDs } from '@/utils/constants';
|
import { globalIDs } from '@/utils/constants';
|
||||||
|
|
||||||
import ViewConstituents from '../ViewConstituents';
|
import ViewConstituents from '../ViewConstituents';
|
||||||
|
@ -20,79 +19,43 @@ const UNFOLDED_HEIGHT = '59.1rem';
|
||||||
const SIDELIST_HIDE_THRESHOLD = 1100; // px
|
const SIDELIST_HIDE_THRESHOLD = 1100; // px
|
||||||
|
|
||||||
interface EditorConstituentaProps {
|
interface EditorConstituentaProps {
|
||||||
|
schema?: IRSForm;
|
||||||
isMutable: boolean;
|
isMutable: boolean;
|
||||||
|
|
||||||
activeID?: number;
|
activeCst?: IConstituenta;
|
||||||
activeCst?: IConstituenta | undefined;
|
|
||||||
isModified: boolean;
|
isModified: boolean;
|
||||||
setIsModified: Dispatch<SetStateAction<boolean>>;
|
setIsModified: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
|
||||||
onOpenEdit: (cstID: number) => void;
|
onOpenEdit: (cstID: number) => void;
|
||||||
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void;
|
onClone: () => void;
|
||||||
onRenameCst: (initial: ICstRenameData) => void;
|
onCreate: (type?: CstType) => void;
|
||||||
|
onRename: () => void;
|
||||||
onEditTerm: () => void;
|
onEditTerm: () => void;
|
||||||
onDeleteCst: (selected: number[], callback?: (items: number[]) => void) => void;
|
onDelete: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorConstituenta({
|
function EditorConstituenta({
|
||||||
|
schema,
|
||||||
isMutable,
|
isMutable,
|
||||||
isModified,
|
isModified,
|
||||||
setIsModified,
|
setIsModified,
|
||||||
activeID,
|
|
||||||
activeCst,
|
activeCst,
|
||||||
onEditTerm,
|
onEditTerm,
|
||||||
onCreateCst,
|
onClone,
|
||||||
onRenameCst,
|
onCreate,
|
||||||
|
onRename,
|
||||||
onOpenEdit,
|
onOpenEdit,
|
||||||
onDeleteCst
|
onDelete
|
||||||
}: EditorConstituentaProps) {
|
}: EditorConstituentaProps) {
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const { schema } = useRSForm();
|
|
||||||
|
|
||||||
const [showList, setShowList] = useLocalStorage('rseditor-show-list', true);
|
const [showList, setShowList] = useLocalStorage('rseditor-show-list', true);
|
||||||
const [toggleReset, setToggleReset] = useState(false);
|
const [toggleReset, setToggleReset] = useState(false);
|
||||||
|
|
||||||
const disabled = useMemo(() => !activeCst || !isMutable, [activeCst, isMutable]);
|
const disabled = useMemo(() => !activeCst || !isMutable, [activeCst, isMutable]);
|
||||||
|
|
||||||
function handleDelete() {
|
|
||||||
if (!schema || !activeID) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onDeleteCst([activeID]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
if (!activeID || !schema) {
|
onCreate(activeCst?.cst_type);
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data: ICstCreateData = {
|
|
||||||
insert_after: activeID,
|
|
||||||
cst_type: activeCst?.cst_type ?? CstType.BASE,
|
|
||||||
alias: '',
|
|
||||||
term_raw: '',
|
|
||||||
definition_formal: '',
|
|
||||||
definition_raw: '',
|
|
||||||
convention: '',
|
|
||||||
term_forms: []
|
|
||||||
};
|
|
||||||
onCreateCst(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleClone() {
|
|
||||||
if (!activeID || !schema || !activeCst) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data: ICstCreateData = {
|
|
||||||
insert_after: activeID,
|
|
||||||
cst_type: activeCst.cst_type,
|
|
||||||
alias: '',
|
|
||||||
term_raw: activeCst.term_raw,
|
|
||||||
definition_formal: activeCst.definition_formal,
|
|
||||||
definition_raw: activeCst.definition_raw,
|
|
||||||
convention: activeCst.convention,
|
|
||||||
term_forms: activeCst.term_forms
|
|
||||||
};
|
|
||||||
onCreateCst(data, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleInput(event: React.KeyboardEvent<HTMLDivElement>) {
|
function handleInput(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||||
|
@ -125,7 +88,7 @@ function EditorConstituenta({
|
||||||
function processAltKey(code: string): boolean {
|
function processAltKey(code: string): boolean {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'KeyV':
|
case 'KeyV':
|
||||||
handleClone();
|
onClone();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -138,8 +101,8 @@ function EditorConstituenta({
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
onSubmit={initiateSubmit}
|
onSubmit={initiateSubmit}
|
||||||
onReset={() => setToggleReset(prev => !prev)}
|
onReset={() => setToggleReset(prev => !prev)}
|
||||||
onDelete={handleDelete}
|
onDelete={onDelete}
|
||||||
onClone={handleClone}
|
onClone={onClone}
|
||||||
onCreate={handleCreate}
|
onCreate={handleCreate}
|
||||||
/>
|
/>
|
||||||
<div tabIndex={-1} className='flex max-w-[95rem]' onKeyDown={handleInput}>
|
<div tabIndex={-1} className='flex max-w-[95rem]' onKeyDown={handleInput}>
|
||||||
|
@ -153,7 +116,7 @@ function EditorConstituenta({
|
||||||
onToggleList={() => setShowList(prev => !prev)}
|
onToggleList={() => setShowList(prev => !prev)}
|
||||||
setIsModified={setIsModified}
|
setIsModified={setIsModified}
|
||||||
onEditTerm={onEditTerm}
|
onEditTerm={onEditTerm}
|
||||||
onRenameCst={onRenameCst}
|
onRename={onRename}
|
||||||
/>
|
/>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{showList && windowSize.width && windowSize.width >= SIDELIST_HIDE_THRESHOLD ? (
|
{showList && windowSize.width && windowSize.width >= SIDELIST_HIDE_THRESHOLD ? (
|
||||||
|
@ -161,7 +124,7 @@ function EditorConstituenta({
|
||||||
schema={schema}
|
schema={schema}
|
||||||
expression={activeCst?.definition_formal ?? ''}
|
expression={activeCst?.definition_formal ?? ''}
|
||||||
baseHeight={UNFOLDED_HEIGHT}
|
baseHeight={UNFOLDED_HEIGHT}
|
||||||
activeID={activeID}
|
activeID={activeCst?.id}
|
||||||
onOpenEdit={onOpenEdit}
|
onOpenEdit={onOpenEdit}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { Dispatch, SetStateAction, useEffect, useLayoutEffect, useState } from 'react';
|
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||||
import { FiSave } from 'react-icons/fi';
|
import { FiSave } from 'react-icons/fi';
|
||||||
import { LiaEdit } from 'react-icons/lia';
|
import { LiaEdit } from 'react-icons/lia';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
@ -12,7 +12,7 @@ import Overlay from '@/components/ui/Overlay';
|
||||||
import SubmitButton from '@/components/ui/SubmitButton';
|
import SubmitButton from '@/components/ui/SubmitButton';
|
||||||
import TextArea from '@/components/ui/TextArea';
|
import TextArea from '@/components/ui/TextArea';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import { IConstituenta, ICstRenameData, ICstUpdateData } from '@/models/rsform';
|
import { IConstituenta, ICstUpdateData } from '@/models/rsform';
|
||||||
import { classnames } from '@/utils/constants';
|
import { classnames } from '@/utils/constants';
|
||||||
import { labelCstTypification } from '@/utils/labels';
|
import { labelCstTypification } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -27,10 +27,10 @@ interface FormConstituentaProps {
|
||||||
|
|
||||||
isModified: boolean;
|
isModified: boolean;
|
||||||
toggleReset: boolean;
|
toggleReset: boolean;
|
||||||
setIsModified: Dispatch<SetStateAction<boolean>>;
|
setIsModified: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
|
||||||
onToggleList: () => void;
|
onToggleList: () => void;
|
||||||
onRenameCst: (initial: ICstRenameData) => void;
|
onRename: () => void;
|
||||||
onEditTerm: () => void;
|
onEditTerm: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ function FormConstituenta({
|
||||||
setIsModified,
|
setIsModified,
|
||||||
constituenta,
|
constituenta,
|
||||||
toggleReset,
|
toggleReset,
|
||||||
onRenameCst,
|
onRename,
|
||||||
onEditTerm,
|
onEditTerm,
|
||||||
onToggleList
|
onToggleList
|
||||||
}: FormConstituentaProps) {
|
}: FormConstituentaProps) {
|
||||||
|
@ -109,18 +109,6 @@ function FormConstituenta({
|
||||||
cstUpdate(data, () => toast.success('Изменения сохранены'));
|
cstUpdate(data, () => toast.success('Изменения сохранены'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRename() {
|
|
||||||
if (!constituenta) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data: ICstRenameData = {
|
|
||||||
id: constituenta.id,
|
|
||||||
alias: constituenta.alias,
|
|
||||||
cst_type: constituenta.cst_type
|
|
||||||
};
|
|
||||||
onRenameCst(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Overlay position='top-1 left-[4.1rem]' className='flex select-none'>
|
<Overlay position='top-1 left-[4.1rem]' className='flex select-none'>
|
||||||
|
@ -139,7 +127,7 @@ function FormConstituenta({
|
||||||
noHover
|
noHover
|
||||||
title='Переименовать конституенту'
|
title='Переименовать конституенту'
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={handleRename}
|
onClick={onRename}
|
||||||
icon={<LiaEdit size='1rem' className={!disabled ? 'clr-text-primary' : ''} />}
|
icon={<LiaEdit size='1rem' className={!disabled ? 'clr-text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
|
|
||||||
import InfoLibraryItem from '@/components/InfoLibraryItem';
|
import InfoLibraryItem from '@/components/InfoLibraryItem';
|
||||||
import Divider from '@/components/ui/Divider';
|
import Divider from '@/components/ui/Divider';
|
||||||
import FlexColumn from '@/components/ui/FlexColumn';
|
import FlexColumn from '@/components/ui/FlexColumn';
|
||||||
|
@ -17,7 +15,7 @@ interface EditorRSFormProps {
|
||||||
isModified: boolean;
|
isModified: boolean;
|
||||||
isMutable: boolean;
|
isMutable: boolean;
|
||||||
|
|
||||||
setIsModified: Dispatch<SetStateAction<boolean>>;
|
setIsModified: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
onDestroy: () => void;
|
onDestroy: () => void;
|
||||||
onClaim: () => void;
|
onClaim: () => void;
|
||||||
onShare: () => void;
|
onShare: () => void;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { Dispatch, SetStateAction, useEffect, useLayoutEffect, useState } from 'react';
|
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||||
import { FiSave } from 'react-icons/fi';
|
import { FiSave } from 'react-icons/fi';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ interface FormRSFormProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
isModified: boolean;
|
isModified: boolean;
|
||||||
setIsModified: Dispatch<SetStateAction<boolean>>;
|
setIsModified: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormRSForm({ id, disabled, isModified, setIsModified }: FormRSFormProps) {
|
function FormRSForm({ id, disabled, isModified, setIsModified }: FormRSFormProps) {
|
||||||
|
|
|
@ -5,46 +5,59 @@ import { useLayoutEffect, 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 { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import { CstType, ICstCreateData, ICstMovetoData } from '@/models/rsform';
|
import { CstType, ICstMovetoData } from '@/models/rsform';
|
||||||
|
|
||||||
import RSListToolbar from './RSListToolbar';
|
import RSListToolbar from './RSListToolbar';
|
||||||
import RSTable from './RSTable';
|
import RSTable from './RSTable';
|
||||||
|
|
||||||
interface EditorRSListProps {
|
interface EditorRSListProps {
|
||||||
isMutable: boolean;
|
isMutable: boolean;
|
||||||
|
selected: number[];
|
||||||
|
setSelected: React.Dispatch<React.SetStateAction<number[]>>;
|
||||||
onOpenEdit: (cstID: number) => void;
|
onOpenEdit: (cstID: number) => void;
|
||||||
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void;
|
onClone: () => void;
|
||||||
onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void;
|
onCreate: (type?: CstType) => void;
|
||||||
|
onDelete: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: EditorRSListProps) {
|
function EditorRSList({
|
||||||
|
selected,
|
||||||
|
setSelected,
|
||||||
|
isMutable,
|
||||||
|
onOpenEdit,
|
||||||
|
onClone,
|
||||||
|
onCreate,
|
||||||
|
onDelete
|
||||||
|
}: EditorRSListProps) {
|
||||||
const { schema, cstMoveTo } = useRSForm();
|
const { schema, cstMoveTo } = useRSForm();
|
||||||
const [selected, setSelected] = useState<number[]>([]);
|
|
||||||
|
|
||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!schema || Object.keys(rowSelection).length === 0) {
|
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([]);
|
setSelected([]);
|
||||||
} else {
|
} else {
|
||||||
const selected: number[] = [];
|
const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater;
|
||||||
schema.items.forEach((cst, index) => {
|
const newSelection: number[] = [];
|
||||||
if (rowSelection[String(index)] === true) {
|
schema?.items.forEach((cst, index) => {
|
||||||
selected.push(cst.id);
|
if (newRowSelection[String(index)] === true) {
|
||||||
|
newSelection.push(cst.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setSelected(selected);
|
setSelected(newSelection);
|
||||||
}
|
}
|
||||||
}, [rowSelection, schema]);
|
|
||||||
|
|
||||||
// Delete selected constituents
|
|
||||||
function handleDelete() {
|
|
||||||
if (!schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onDeleteCst(selected, () => {
|
|
||||||
setRowSelection({});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move selected cst up
|
// Move selected cst up
|
||||||
|
@ -65,13 +78,7 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
|
||||||
items: selected,
|
items: selected,
|
||||||
move_to: target
|
move_to: target
|
||||||
};
|
};
|
||||||
cstMoveTo(data, () => {
|
cstMoveTo(data);
|
||||||
const newSelection: RowSelectionState = {};
|
|
||||||
selected.forEach((_, index) => {
|
|
||||||
newSelection[String(target + index - 1)] = true;
|
|
||||||
});
|
|
||||||
setRowSelection(newSelection);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move selected cst down
|
// Move selected cst down
|
||||||
|
@ -96,57 +103,7 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
|
||||||
items: selected,
|
items: selected,
|
||||||
move_to: target
|
move_to: target
|
||||||
};
|
};
|
||||||
cstMoveTo(data, () => {
|
cstMoveTo(data);
|
||||||
const newSelection: RowSelectionState = {};
|
|
||||||
selected.forEach((_, index) => {
|
|
||||||
newSelection[String(target + index - 1)] = true;
|
|
||||||
});
|
|
||||||
setRowSelection(newSelection);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCreateCst(type?: CstType) {
|
|
||||||
if (!schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const selectedPosition = selected.reduce((prev, cstID) => {
|
|
||||||
const position = schema.items.findIndex(cst => cst.id === cstID);
|
|
||||||
return Math.max(position, prev);
|
|
||||||
}, -1);
|
|
||||||
const insert_where = selectedPosition >= 0 ? schema.items[selectedPosition].id : undefined;
|
|
||||||
const data: ICstCreateData = {
|
|
||||||
insert_after: insert_where ?? null,
|
|
||||||
cst_type: type ?? CstType.BASE,
|
|
||||||
alias: '',
|
|
||||||
term_raw: '',
|
|
||||||
definition_formal: '',
|
|
||||||
definition_raw: '',
|
|
||||||
convention: '',
|
|
||||||
term_forms: []
|
|
||||||
};
|
|
||||||
onCreateCst(data, type !== undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone selected
|
|
||||||
function handleClone() {
|
|
||||||
if (selected.length < 1 || !schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const activeCst = schema.items.find(cst => cst.id === selected[0]);
|
|
||||||
if (!activeCst) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data: ICstCreateData = {
|
|
||||||
insert_after: activeCst.id,
|
|
||||||
cst_type: activeCst.cst_type,
|
|
||||||
alias: '',
|
|
||||||
term_raw: activeCst.term_raw,
|
|
||||||
definition_formal: activeCst.definition_formal,
|
|
||||||
definition_raw: activeCst.definition_raw,
|
|
||||||
convention: activeCst.convention,
|
|
||||||
term_forms: activeCst.term_forms
|
|
||||||
};
|
|
||||||
onCreateCst(data, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement hotkeys for working with constituents table
|
// Implement hotkeys for working with constituents table
|
||||||
|
@ -156,7 +113,7 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
|
||||||
}
|
}
|
||||||
if (event.key === 'Delete' && selected.length > 0) {
|
if (event.key === 'Delete' && selected.length > 0) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
handleDelete();
|
onDelete();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!event.altKey || event.shiftKey) {
|
if (!event.altKey || event.shiftKey) {
|
||||||
|
@ -174,21 +131,21 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'ArrowUp': handleMoveUp(); return true;
|
case 'ArrowUp': handleMoveUp(); return true;
|
||||||
case 'ArrowDown': handleMoveDown(); return true;
|
case 'ArrowDown': handleMoveDown(); return true;
|
||||||
case 'KeyV': handleClone(); return true;
|
case 'KeyV': onClone(); return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'Backquote': handleCreateCst(); return true;
|
case 'Backquote': onCreate(); return true;
|
||||||
|
|
||||||
case 'Digit1': handleCreateCst(CstType.BASE); return true;
|
case 'Digit1': onCreate(CstType.BASE); return true;
|
||||||
case 'Digit2': handleCreateCst(CstType.STRUCTURED); return true;
|
case 'Digit2': onCreate(CstType.STRUCTURED); return true;
|
||||||
case 'Digit3': handleCreateCst(CstType.TERM); return true;
|
case 'Digit3': onCreate(CstType.TERM); return true;
|
||||||
case 'Digit4': handleCreateCst(CstType.AXIOM); return true;
|
case 'Digit4': onCreate(CstType.AXIOM); return true;
|
||||||
case 'KeyQ': handleCreateCst(CstType.FUNCTION); return true;
|
case 'KeyQ': onCreate(CstType.FUNCTION); return true;
|
||||||
case 'KeyW': handleCreateCst(CstType.PREDICATE); return true;
|
case 'KeyW': onCreate(CstType.PREDICATE); return true;
|
||||||
case 'Digit5': handleCreateCst(CstType.CONSTANT); return true;
|
case 'Digit5': onCreate(CstType.CONSTANT); return true;
|
||||||
case 'Digit6': handleCreateCst(CstType.THEOREM); return true;
|
case 'Digit6': onCreate(CstType.THEOREM); return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -196,8 +153,8 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
|
||||||
return (
|
return (
|
||||||
<div tabIndex={-1} className='outline-none' onKeyDown={handleTableKey}>
|
<div tabIndex={-1} className='outline-none' onKeyDown={handleTableKey}>
|
||||||
<SelectedCounter
|
<SelectedCounter
|
||||||
total={schema?.stats?.count_all ?? 0}
|
totalCount={schema?.stats?.count_all ?? 0}
|
||||||
selected={selected.length}
|
selectedCount={selected.length}
|
||||||
position='top-[0.3rem] left-2'
|
position='top-[0.3rem] left-2'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -206,9 +163,9 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
onMoveUp={handleMoveUp}
|
onMoveUp={handleMoveUp}
|
||||||
onMoveDown={handleMoveDown}
|
onMoveDown={handleMoveDown}
|
||||||
onClone={handleClone}
|
onClone={onClone}
|
||||||
onCreate={handleCreateCst}
|
onCreate={onCreate}
|
||||||
onDelete={handleDelete}
|
onDelete={onDelete}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='pt-[2.3rem] border-b' />
|
<div className='pt-[2.3rem] border-b' />
|
||||||
|
@ -216,9 +173,9 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
|
||||||
<RSTable
|
<RSTable
|
||||||
items={schema?.items}
|
items={schema?.items}
|
||||||
selected={rowSelection}
|
selected={rowSelection}
|
||||||
setSelected={setRowSelection}
|
setSelected={handleRowSelection}
|
||||||
onEdit={onOpenEdit}
|
onEdit={onOpenEdit}
|
||||||
onCreateNew={() => handleCreateCst()}
|
onCreateNew={onCreate}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -67,6 +67,7 @@ function RSListToolbar({
|
||||||
<div ref={insertMenu.ref}>
|
<div ref={insertMenu.ref}>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Добавить пустую конституенту'
|
title='Добавить пустую конституенту'
|
||||||
|
hideTitle={insertMenu.isOpen}
|
||||||
icon={<BiDownArrowCircle size='1.25rem' className={isMutable ? 'clr-text-success' : ''} />}
|
icon={<BiDownArrowCircle size='1.25rem' className={isMutable ? 'clr-text-success' : ''} />}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={insertMenu.toggle}
|
onClick={insertMenu.toggle}
|
||||||
|
|
|
@ -8,12 +8,11 @@ import { GraphEdge, GraphNode, LayoutTypes } from 'reagraph';
|
||||||
import InfoConstituenta from '@/components/InfoConstituenta';
|
import InfoConstituenta from '@/components/InfoConstituenta';
|
||||||
import SelectedCounter from '@/components/SelectedCounter';
|
import SelectedCounter from '@/components/SelectedCounter';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
import DlgGraphParams from '@/dialogs/DlgGraphParams';
|
import DlgGraphParams from '@/dialogs/DlgGraphParams';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import { GraphColoringScheme, GraphFilterParams } from '@/models/miscellaneous';
|
import { GraphColoringScheme, GraphFilterParams } from '@/models/miscellaneous';
|
||||||
import { CstType, ICstCreateData } from '@/models/rsform';
|
import { CstType, IRSForm } from '@/models/rsform';
|
||||||
import { colorBgGraphNode } from '@/styling/color';
|
import { colorBgGraphNode } from '@/styling/color';
|
||||||
import { classnames, TIMEOUT_GRAPH_REFRESH } from '@/utils/constants';
|
import { classnames, TIMEOUT_GRAPH_REFRESH } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -25,16 +24,25 @@ import ViewHidden from './ViewHidden';
|
||||||
|
|
||||||
interface EditorTermGraphProps {
|
interface EditorTermGraphProps {
|
||||||
isMutable: boolean;
|
isMutable: boolean;
|
||||||
|
selected: number[];
|
||||||
|
schema?: IRSForm;
|
||||||
|
setSelected: React.Dispatch<React.SetStateAction<number[]>>;
|
||||||
onOpenEdit: (cstID: number) => void;
|
onOpenEdit: (cstID: number) => void;
|
||||||
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void;
|
onCreate: (type: CstType, definition: string) => void;
|
||||||
onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void;
|
onDelete: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGraphProps) {
|
function EditorTermGraph({
|
||||||
const { schema } = useRSForm();
|
schema,
|
||||||
|
selected,
|
||||||
|
setSelected,
|
||||||
|
isMutable,
|
||||||
|
onOpenEdit,
|
||||||
|
onCreate,
|
||||||
|
onDelete
|
||||||
|
}: EditorTermGraphProps) {
|
||||||
const { colors } = useConceptTheme();
|
const { colors } = useConceptTheme();
|
||||||
|
|
||||||
const [toggleDataUpdate, setToggleDataUpdate] = useState(false);
|
|
||||||
const [filterParams, setFilterParams] = useLocalStorage<GraphFilterParams>('graph_filter', {
|
const [filterParams, setFilterParams] = useLocalStorage<GraphFilterParams>('graph_filter', {
|
||||||
noHermits: true,
|
noHermits: true,
|
||||||
noTemplates: false,
|
noTemplates: false,
|
||||||
|
@ -51,14 +59,10 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
|
||||||
allowTheorem: true
|
allowTheorem: true
|
||||||
});
|
});
|
||||||
const [showParamsDialog, setShowParamsDialog] = useState(false);
|
const [showParamsDialog, setShowParamsDialog] = useState(false);
|
||||||
const filtered = useGraphFilter(schema, filterParams, toggleDataUpdate);
|
const filtered = useGraphFilter(schema, filterParams);
|
||||||
|
|
||||||
const [selectedGraph, setSelectedGraph] = useState<number[]>([]);
|
|
||||||
const [hidden, setHidden] = useState<number[]>([]);
|
const [hidden, setHidden] = useState<number[]>([]);
|
||||||
const [selectedHidden, setSelectedHidden] = useState<number[]>([]);
|
|
||||||
const selected: number[] = useMemo(() => {
|
|
||||||
return [...selectedHidden, ...selectedGraph];
|
|
||||||
}, [selectedHidden, selectedGraph]);
|
|
||||||
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
|
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
|
||||||
|
|
||||||
const [layout, setLayout] = useLocalStorage<LayoutTypes>('graph_layout', 'treeTd2d');
|
const [layout, setLayout] = useLocalStorage<LayoutTypes>('graph_layout', 'treeTd2d');
|
||||||
|
@ -72,7 +76,6 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
|
||||||
}, [schema?.items, hoverID]);
|
}, [schema?.items, hoverID]);
|
||||||
|
|
||||||
const [toggleResetView, setToggleResetView] = useState(false);
|
const [toggleResetView, setToggleResetView] = useState(false);
|
||||||
const [toggleResetSelection, setToggleResetSelection] = useState(false);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
|
@ -85,9 +88,8 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setHidden(newDismissed);
|
setHidden(newDismissed);
|
||||||
setSelectedHidden([]);
|
|
||||||
setHoverID(undefined);
|
setHoverID(undefined);
|
||||||
}, [schema, filtered, toggleDataUpdate]);
|
}, [schema, filtered]);
|
||||||
|
|
||||||
const nodes: GraphNode[] = useMemo(() => {
|
const nodes: GraphNode[] = useMemo(() => {
|
||||||
const result: GraphNode[] = [];
|
const result: GraphNode[] = [];
|
||||||
|
@ -123,15 +125,20 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
|
||||||
return result;
|
return result;
|
||||||
}, [filtered.nodes]);
|
}, [filtered.nodes]);
|
||||||
|
|
||||||
|
const handleGraphSelection = useCallback(
|
||||||
|
(newID: number) => {
|
||||||
|
setSelected(prev => [...prev, newID]);
|
||||||
|
},
|
||||||
|
[setSelected]
|
||||||
|
);
|
||||||
|
|
||||||
function toggleDismissed(cstID: number) {
|
function toggleDismissed(cstID: number) {
|
||||||
setSelectedHidden(prev => {
|
setSelected(prev => {
|
||||||
const index = prev.findIndex(id => cstID === id);
|
if (prev.includes(cstID)) {
|
||||||
if (index !== -1) {
|
return [...prev.filter(id => id !== cstID)];
|
||||||
prev.splice(index, 1);
|
|
||||||
} else {
|
} else {
|
||||||
prev.push(cstID);
|
return [...prev, cstID];
|
||||||
}
|
}
|
||||||
return [...prev];
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,29 +146,15 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data: ICstCreateData = {
|
const definition = selected.map(id => schema.items.find(cst => cst.id === id)!.alias).join(' ');
|
||||||
insert_after: null,
|
onCreate(selected.length === 0 ? CstType.BASE : CstType.TERM, definition);
|
||||||
cst_type: selected.length === 0 ? CstType.BASE : CstType.TERM,
|
|
||||||
alias: '',
|
|
||||||
term_raw: '',
|
|
||||||
definition_formal: selected.map(id => schema.items.find(cst => cst.id === id)!.alias).join(' '),
|
|
||||||
definition_raw: '',
|
|
||||||
convention: '',
|
|
||||||
term_forms: []
|
|
||||||
};
|
|
||||||
onCreateCst(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDeleteCst() {
|
function handleDeleteCst() {
|
||||||
if (!schema || selected.length === 0) {
|
if (!schema || selected.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onDeleteCst(selected, () => {
|
onDelete();
|
||||||
setHidden([]);
|
|
||||||
setSelectedHidden([]);
|
|
||||||
setToggleResetSelection(prev => !prev);
|
|
||||||
setToggleDataUpdate(prev => !prev);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChangeLayout(newLayout: LayoutTypes) {
|
function handleChangeLayout(newLayout: LayoutTypes) {
|
||||||
|
@ -206,8 +199,8 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
|
||||||
|
|
||||||
<SelectedCounter
|
<SelectedCounter
|
||||||
hideZero
|
hideZero
|
||||||
total={schema?.stats?.count_all ?? 0}
|
totalCount={schema?.stats?.count_all ?? 0}
|
||||||
selected={selected.length}
|
selectedCount={selected.length}
|
||||||
position='top-[0.3rem] left-0'
|
position='top-[0.3rem] left-0'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -249,7 +242,7 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
|
||||||
/>
|
/>
|
||||||
<ViewHidden
|
<ViewHidden
|
||||||
items={hidden}
|
items={hidden}
|
||||||
selected={selectedHidden}
|
selected={selected}
|
||||||
schema={schema!}
|
schema={schema!}
|
||||||
coloringScheme={coloringScheme}
|
coloringScheme={coloringScheme}
|
||||||
toggleSelection={toggleDismissed}
|
toggleSelection={toggleDismissed}
|
||||||
|
@ -260,15 +253,15 @@ function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Ed
|
||||||
<TermGraph
|
<TermGraph
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
|
selectedIDs={selected}
|
||||||
layout={layout}
|
layout={layout}
|
||||||
is3D={is3D}
|
is3D={is3D}
|
||||||
orbit={orbit}
|
orbit={orbit}
|
||||||
setSelected={setSelectedGraph}
|
onSelect={handleGraphSelection}
|
||||||
setHoverID={setHoverID}
|
setHoverID={setHoverID}
|
||||||
onEdit={onOpenEdit}
|
onEdit={onOpenEdit}
|
||||||
onDeselect={() => setSelectedHidden([])}
|
onDeselectAll={() => setSelected([])}
|
||||||
toggleResetView={toggleResetView}
|
toggleResetView={toggleResetView}
|
||||||
toggleResetSelection={toggleResetSelection}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,18 +10,18 @@ import { resources } from '@/utils/constants';
|
||||||
interface TermGraphProps {
|
interface TermGraphProps {
|
||||||
nodes: GraphNode[];
|
nodes: GraphNode[];
|
||||||
edges: GraphEdge[];
|
edges: GraphEdge[];
|
||||||
|
selectedIDs: number[];
|
||||||
|
|
||||||
layout: LayoutTypes;
|
layout: LayoutTypes;
|
||||||
is3D: boolean;
|
is3D: boolean;
|
||||||
orbit: boolean;
|
orbit: boolean;
|
||||||
|
|
||||||
setSelected: (selected: number[]) => void;
|
|
||||||
setHoverID: (newID: number | undefined) => void;
|
setHoverID: (newID: number | undefined) => void;
|
||||||
onEdit: (cstID: number) => void;
|
onEdit: (cstID: number) => void;
|
||||||
onDeselect: () => void;
|
onSelect: (newID: number) => void;
|
||||||
|
onDeselectAll: () => void;
|
||||||
|
|
||||||
toggleResetView: boolean;
|
toggleResetView: boolean;
|
||||||
toggleResetSelection: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const TREE_SIZE_MILESTONE = 50;
|
const TREE_SIZE_MILESTONE = 50;
|
||||||
|
@ -29,29 +29,28 @@ const TREE_SIZE_MILESTONE = 50;
|
||||||
function TermGraph({
|
function TermGraph({
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
|
selectedIDs,
|
||||||
layout,
|
layout,
|
||||||
is3D,
|
is3D,
|
||||||
orbit,
|
orbit,
|
||||||
toggleResetView,
|
toggleResetView,
|
||||||
toggleResetSelection,
|
|
||||||
setHoverID,
|
setHoverID,
|
||||||
onEdit,
|
onEdit,
|
||||||
setSelected,
|
onSelect,
|
||||||
onDeselect
|
onDeselectAll
|
||||||
}: TermGraphProps) {
|
}: TermGraphProps) {
|
||||||
const { noNavigation, darkMode } = useConceptTheme();
|
const { noNavigation, darkMode } = useConceptTheme();
|
||||||
const graphRef = useRef<GraphCanvasRef | null>(null);
|
const graphRef = useRef<GraphCanvasRef | null>(null);
|
||||||
|
|
||||||
const { selections, actives, onNodeClick, clearSelections, onCanvasClick, onNodePointerOver, onNodePointerOut } =
|
const { selections, actives, setSelections, onCanvasClick, onNodePointerOver, onNodePointerOut } = useSelection({
|
||||||
useSelection({
|
ref: graphRef,
|
||||||
ref: graphRef,
|
nodes,
|
||||||
nodes,
|
edges,
|
||||||
edges,
|
type: 'multi', // 'single' | 'multi' | 'multiModifier'
|
||||||
type: 'multi', // 'single' | 'multi' | 'multiModifier'
|
pathSelectionType: 'out',
|
||||||
pathSelectionType: 'out',
|
pathHoverType: 'all',
|
||||||
pathHoverType: 'all',
|
focusOnSelect: false
|
||||||
focusOnSelect: false
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const handleHoverIn = useCallback(
|
const handleHoverIn = useCallback(
|
||||||
(node: GraphNode) => {
|
(node: GraphNode) => {
|
||||||
|
@ -73,19 +72,19 @@ function TermGraph({
|
||||||
(node: GraphNode) => {
|
(node: GraphNode) => {
|
||||||
if (selections.includes(node.id)) {
|
if (selections.includes(node.id)) {
|
||||||
onEdit(Number(node.id));
|
onEdit(Number(node.id));
|
||||||
return;
|
} else {
|
||||||
|
onSelect(Number(node.id));
|
||||||
}
|
}
|
||||||
if (onNodeClick) onNodeClick(node);
|
|
||||||
},
|
},
|
||||||
[onNodeClick, selections, onEdit]
|
[onSelect, selections, onEdit]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCanvasClick = useCallback(
|
const handleCanvasClick = useCallback(
|
||||||
(event: MouseEvent) => {
|
(event: MouseEvent) => {
|
||||||
onDeselect();
|
onDeselectAll();
|
||||||
if (onCanvasClick) onCanvasClick(event);
|
if (onCanvasClick) onCanvasClick(event);
|
||||||
},
|
},
|
||||||
[onCanvasClick, onDeselect]
|
[onCanvasClick, onDeselectAll]
|
||||||
);
|
);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
@ -94,12 +93,9 @@ function TermGraph({
|
||||||
}, [toggleResetView]);
|
}, [toggleResetView]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
clearSelections();
|
const newSelections = nodes.filter(node => selectedIDs.includes(Number(node.id))).map(node => node.id);
|
||||||
}, [toggleResetSelection, clearSelections]);
|
setSelections(newSelections);
|
||||||
|
}, [selectedIDs, setSelections, nodes]);
|
||||||
useLayoutEffect(() => {
|
|
||||||
setSelected(selections.map(id => Number(id)));
|
|
||||||
}, [selections, setSelected]);
|
|
||||||
|
|
||||||
const canvasWidth = useMemo(() => {
|
const canvasWidth = useMemo(() => {
|
||||||
return 'calc(100vw - 1.1rem)';
|
return 'calc(100vw - 1.1rem)';
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { useLayoutEffect, useMemo, useState } from 'react';
|
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { Graph } from '@/models/Graph';
|
||||||
import { GraphFilterParams } from '@/models/miscellaneous';
|
import { GraphFilterParams } from '@/models/miscellaneous';
|
||||||
import { CstType, IRSForm } from '@/models/rsform';
|
import { CstType, IRSForm } from '@/models/rsform';
|
||||||
import { Graph } from '@/models/Graph';
|
|
||||||
|
|
||||||
function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams, toggleUpdate: boolean) {
|
function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams) {
|
||||||
const [filtered, setFiltered] = useState<Graph>(new Graph());
|
const [filtered, setFiltered] = useState<Graph>(new Graph());
|
||||||
|
|
||||||
const allowedTypes: CstType[] = useMemo(() => {
|
const allowedTypes: CstType[] = useMemo(() => {
|
||||||
|
@ -47,7 +47,7 @@ function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setFiltered(graph);
|
setFiltered(graph);
|
||||||
}, [schema, params, allowedTypes, toggleUpdate]);
|
}, [schema, params, allowedTypes]);
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import DlgRenameCst from '@/dialogs/DlgRenameCst';
|
||||||
import DlgUploadRSForm from '@/dialogs/DlgUploadRSForm';
|
import DlgUploadRSForm from '@/dialogs/DlgUploadRSForm';
|
||||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
import { UserAccessMode } from '@/models/miscellaneous';
|
import { UserAccessMode } from '@/models/miscellaneous';
|
||||||
import { IConstituenta, ICstCreateData, ICstRenameData, ICstUpdateData, TermForm } from '@/models/rsform';
|
import { CstType, IConstituenta, ICstCreateData, ICstRenameData, ICstUpdateData, TermForm } from '@/models/rsform';
|
||||||
import { generateAlias } from '@/models/rsformAPI';
|
import { generateAlias } from '@/models/rsformAPI';
|
||||||
import { EXTEOR_TRS_FILE, prefixes, TIMEOUT_UI_REFRESH } from '@/utils/constants';
|
import { EXTEOR_TRS_FILE, prefixes, TIMEOUT_UI_REFRESH } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -85,14 +85,18 @@ function RSTabs() {
|
||||||
);
|
);
|
||||||
}, [user?.is_staff, mode, isOwned, loading, processing]);
|
}, [user?.is_staff, mode, isOwned, loading, processing]);
|
||||||
|
|
||||||
const [activeID, setActiveID] = useState<number | undefined>(undefined);
|
const [selected, setSelected] = useState<number[]>([]);
|
||||||
const activeCst = useMemo(() => schema?.items?.find(cst => cst.id === activeID), [schema?.items, activeID]);
|
const activeCst: IConstituenta | undefined = useMemo(() => {
|
||||||
|
if (!schema || selected.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
return schema.items.find(cst => cst.id === selected.at(-1));
|
||||||
|
}
|
||||||
|
}, [schema, selected]);
|
||||||
|
|
||||||
const [showUpload, setShowUpload] = useState(false);
|
const [showUpload, setShowUpload] = useState(false);
|
||||||
const [showClone, setShowClone] = useState(false);
|
const [showClone, setShowClone] = useState(false);
|
||||||
|
|
||||||
const [afterDelete, setAfterDelete] = useState<((items: number[]) => void) | undefined>(undefined);
|
|
||||||
const [toBeDeleted, setToBeDeleted] = useState<number[]>([]);
|
|
||||||
const [showDeleteCst, setShowDeleteCst] = useState(false);
|
const [showDeleteCst, setShowDeleteCst] = useState(false);
|
||||||
|
|
||||||
const [createInitialData, setCreateInitialData] = useState<ICstCreateData>();
|
const [createInitialData, setCreateInitialData] = useState<ICstCreateData>();
|
||||||
|
@ -118,10 +122,19 @@ function RSTabs() {
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setNoFooter(activeTab === RSTabID.CST_EDIT || activeTab === RSTabID.CST_LIST);
|
setNoFooter(activeTab === RSTabID.CST_EDIT || activeTab === RSTabID.CST_LIST);
|
||||||
setActiveID(Number(cstQuery) ?? (schema && schema?.items.length > 0 ? schema.items[0].id : undefined));
|
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
|
if (activeTab === RSTabID.CST_EDIT) {
|
||||||
|
const cstID = Number(cstQuery);
|
||||||
|
if (cstID && schema && schema.items.find(cst => cst.id === cstID)) {
|
||||||
|
setSelected([cstID]);
|
||||||
|
} else if (schema && schema?.items.length > 0) {
|
||||||
|
setSelected([schema.items[0].id]);
|
||||||
|
} else {
|
||||||
|
setSelected([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
return () => setNoFooter(false);
|
return () => setNoFooter(false);
|
||||||
}, [activeTab, cstQuery, setActiveID, schema, setNoFooter, setIsModified]);
|
}, [activeTab, cstQuery, setSelected, schema, setNoFooter, setIsModified]);
|
||||||
|
|
||||||
useLayoutEffect(
|
useLayoutEffect(
|
||||||
() =>
|
() =>
|
||||||
|
@ -137,10 +150,6 @@ function RSTabs() {
|
||||||
[schema, setMode, isOwned]
|
[schema, setMode, isOwned]
|
||||||
);
|
);
|
||||||
|
|
||||||
function onSelectTab(index: number) {
|
|
||||||
navigateTab(index, activeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
const navigateTab = useCallback(
|
const navigateTab = useCallback(
|
||||||
(tab: RSTabID, activeID?: number) => {
|
(tab: RSTabID, activeID?: number) => {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
|
@ -162,6 +171,10 @@ function RSTabs() {
|
||||||
[router, schema, activeTab]
|
[router, schema, activeTab]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function onSelectTab(index: number) {
|
||||||
|
navigateTab(index, selected.length > 0 ? selected.at(-1) : undefined);
|
||||||
|
}
|
||||||
|
|
||||||
const handleCreateCst = useCallback(
|
const handleCreateCst = useCallback(
|
||||||
(data: ICstCreateData) => {
|
(data: ICstCreateData) => {
|
||||||
if (!schema?.items) {
|
if (!schema?.items) {
|
||||||
|
@ -189,17 +202,44 @@ function RSTabs() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const promptCreateCst = useCallback(
|
const promptCreateCst = useCallback(
|
||||||
(initialData: ICstCreateData, skipDialog?: boolean) => {
|
(type: CstType | undefined, skipDialog: boolean, definition?: string) => {
|
||||||
|
const data: ICstCreateData = {
|
||||||
|
insert_after: activeCst?.id ?? null,
|
||||||
|
cst_type: type ?? activeCst?.cst_type ?? CstType.BASE,
|
||||||
|
alias: '',
|
||||||
|
term_raw: '',
|
||||||
|
definition_formal: definition ?? '',
|
||||||
|
definition_raw: '',
|
||||||
|
convention: '',
|
||||||
|
term_forms: []
|
||||||
|
};
|
||||||
if (skipDialog) {
|
if (skipDialog) {
|
||||||
handleCreateCst(initialData);
|
handleCreateCst(data);
|
||||||
} else {
|
} else {
|
||||||
setCreateInitialData(initialData);
|
setCreateInitialData(data);
|
||||||
setShowCreateCst(true);
|
setShowCreateCst(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleCreateCst]
|
[handleCreateCst, activeCst]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCloneCst = useCallback(() => {
|
||||||
|
if (!activeCst) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data: ICstCreateData = {
|
||||||
|
insert_after: activeCst.id,
|
||||||
|
cst_type: activeCst.cst_type,
|
||||||
|
alias: '',
|
||||||
|
term_raw: activeCst.term_raw,
|
||||||
|
definition_formal: activeCst.definition_formal,
|
||||||
|
definition_raw: activeCst.definition_raw,
|
||||||
|
convention: activeCst.convention,
|
||||||
|
term_forms: activeCst.term_forms
|
||||||
|
};
|
||||||
|
handleCreateCst(data);
|
||||||
|
}, [activeCst, handleCreateCst]);
|
||||||
|
|
||||||
const handleRenameCst = useCallback(
|
const handleRenameCst = useCallback(
|
||||||
(data: ICstRenameData) => {
|
(data: ICstRenameData) => {
|
||||||
cstRename(data, () => toast.success(`Переименование: ${renameInitialData!.alias} -> ${data.alias}`));
|
cstRename(data, () => toast.success(`Переименование: ${renameInitialData!.alias} -> ${data.alias}`));
|
||||||
|
@ -207,10 +247,18 @@ function RSTabs() {
|
||||||
[cstRename, renameInitialData]
|
[cstRename, renameInitialData]
|
||||||
);
|
);
|
||||||
|
|
||||||
const promptRenameCst = useCallback((initialData: ICstRenameData) => {
|
const promptRenameCst = useCallback(() => {
|
||||||
setRenameInitialData(initialData);
|
if (!activeCst) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data: ICstRenameData = {
|
||||||
|
id: activeCst.id,
|
||||||
|
alias: activeCst.alias,
|
||||||
|
cst_type: activeCst.cst_type
|
||||||
|
};
|
||||||
|
setRenameInitialData(data);
|
||||||
setShowRenameCst(true);
|
setShowRenameCst(true);
|
||||||
}, []);
|
}, [activeCst]);
|
||||||
|
|
||||||
const onReindex = useCallback(() => resetAliases(() => toast.success('Имена конституент обновлены')), [resetAliases]);
|
const onReindex = useCallback(() => resetAliases(() => toast.success('Имена конституент обновлены')), [resetAliases]);
|
||||||
|
|
||||||
|
@ -225,33 +273,26 @@ function RSTabs() {
|
||||||
|
|
||||||
const deletedNames = deleted.map(id => schema.items.find(cst => cst.id === id)?.alias).join(', ');
|
const deletedNames = deleted.map(id => schema.items.find(cst => cst.id === id)?.alias).join(', ');
|
||||||
const isEmpty = deleted.length === schema.items.length;
|
const isEmpty = deleted.length === schema.items.length;
|
||||||
const nextActive = isEmpty ? undefined : getNextActiveOnDelete(activeID, schema.items, deleted);
|
const nextActive = isEmpty ? undefined : getNextActiveOnDelete(activeCst?.id, schema.items, deleted);
|
||||||
|
|
||||||
cstDelete(data, () => {
|
cstDelete(data, () => {
|
||||||
toast.success(`Конституенты удалены: ${deletedNames}`);
|
toast.success(`Конституенты удалены: ${deletedNames}`);
|
||||||
if (isEmpty) {
|
if (isEmpty) {
|
||||||
navigateTab(RSTabID.CST_LIST);
|
navigateTab(RSTabID.CST_LIST);
|
||||||
} else if (!nextActive) {
|
} else if (activeTab === RSTabID.CST_EDIT) {
|
||||||
navigateTab(activeTab);
|
|
||||||
} else {
|
|
||||||
navigateTab(activeTab, nextActive);
|
navigateTab(activeTab, nextActive);
|
||||||
|
} else {
|
||||||
|
setSelected(nextActive ? [nextActive] : []);
|
||||||
|
navigateTab(activeTab);
|
||||||
}
|
}
|
||||||
if (afterDelete) afterDelete(deleted);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[afterDelete, cstDelete, schema, activeID, activeTab, navigateTab]
|
[cstDelete, schema, activeTab, activeCst, navigateTab]
|
||||||
);
|
);
|
||||||
|
|
||||||
const promptDeleteCst = useCallback((selected: number[], callback?: (items: number[]) => void) => {
|
|
||||||
setAfterDelete(() => (items: number[]) => {
|
|
||||||
if (callback) callback(items);
|
|
||||||
});
|
|
||||||
setToBeDeleted(selected);
|
|
||||||
setShowDeleteCst(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onOpenCst = useCallback(
|
const onOpenCst = useCallback(
|
||||||
(cstID: number) => {
|
(cstID: number) => {
|
||||||
|
setSelected([cstID]);
|
||||||
navigateTab(RSTabID.CST_EDIT, cstID);
|
navigateTab(RSTabID.CST_EDIT, cstID);
|
||||||
},
|
},
|
||||||
[navigateTab]
|
[navigateTab]
|
||||||
|
@ -334,16 +375,16 @@ function RSTabs() {
|
||||||
|
|
||||||
const handleSaveWordforms = useCallback(
|
const handleSaveWordforms = useCallback(
|
||||||
(forms: TermForm[]) => {
|
(forms: TermForm[]) => {
|
||||||
if (!activeID) {
|
if (!activeCst) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data: ICstUpdateData = {
|
const data: ICstUpdateData = {
|
||||||
id: activeID,
|
id: activeCst.id,
|
||||||
term_forms: forms
|
term_forms: forms
|
||||||
};
|
};
|
||||||
cstUpdate(data, () => toast.success('Изменения сохранены'));
|
cstUpdate(data, () => toast.success('Изменения сохранены'));
|
||||||
},
|
},
|
||||||
[cstUpdate, activeID]
|
[cstUpdate, activeCst]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -371,7 +412,7 @@ function RSTabs() {
|
||||||
schema={schema!}
|
schema={schema!}
|
||||||
hideWindow={() => setShowDeleteCst(false)}
|
hideWindow={() => setShowDeleteCst(false)}
|
||||||
onDelete={handleDeleteCst}
|
onDelete={handleDeleteCst}
|
||||||
selected={toBeDeleted}
|
selected={selected}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{showEditTerm ? (
|
{showEditTerm ? (
|
||||||
|
@ -438,34 +479,41 @@ function RSTabs() {
|
||||||
|
|
||||||
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_LIST ? '' : 'none' }}>
|
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_LIST ? '' : 'none' }}>
|
||||||
<EditorRSList
|
<EditorRSList
|
||||||
|
selected={selected}
|
||||||
|
setSelected={setSelected}
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
onOpenEdit={onOpenCst}
|
onOpenEdit={onOpenCst}
|
||||||
onCreateCst={promptCreateCst}
|
onClone={handleCloneCst}
|
||||||
onDeleteCst={promptDeleteCst}
|
onCreate={type => promptCreateCst(type, type !== undefined)}
|
||||||
|
onDelete={() => setShowDeleteCst(true)}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_EDIT ? '' : 'none' }}>
|
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_EDIT ? '' : 'none' }}>
|
||||||
<EditorConstituenta
|
<EditorConstituenta
|
||||||
|
schema={schema}
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
setIsModified={setIsModified}
|
setIsModified={setIsModified}
|
||||||
activeID={activeID}
|
|
||||||
activeCst={activeCst}
|
activeCst={activeCst}
|
||||||
onOpenEdit={onOpenCst}
|
onOpenEdit={onOpenCst}
|
||||||
onCreateCst={promptCreateCst}
|
onClone={handleCloneCst}
|
||||||
onDeleteCst={promptDeleteCst}
|
onCreate={type => promptCreateCst(type, false)}
|
||||||
onRenameCst={promptRenameCst}
|
onDelete={() => setShowDeleteCst(true)}
|
||||||
|
onRename={promptRenameCst}
|
||||||
onEditTerm={promptShowEditTerm}
|
onEditTerm={promptShowEditTerm}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel style={{ display: activeTab === RSTabID.TERM_GRAPH ? '' : 'none' }}>
|
<TabPanel style={{ display: activeTab === RSTabID.TERM_GRAPH ? '' : 'none' }}>
|
||||||
<EditorTermGraph
|
<EditorTermGraph
|
||||||
|
schema={schema}
|
||||||
|
selected={selected}
|
||||||
|
setSelected={setSelected}
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
onOpenEdit={onOpenCst}
|
onOpenEdit={onOpenCst}
|
||||||
onCreateCst={promptCreateCst}
|
onCreate={(type, definition) => promptCreateCst(type, false, definition)}
|
||||||
onDeleteCst={promptDeleteCst}
|
onDelete={() => setShowDeleteCst(true)}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</AnimateFade>
|
</AnimateFade>
|
||||||
|
|
|
@ -118,6 +118,7 @@ function RSTabsMenu({
|
||||||
dense
|
dense
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title='Меню'
|
title='Меню'
|
||||||
|
hideTitle={schemaMenu.isOpen}
|
||||||
icon={<BiMenu size='1.25rem' className='clr-text-controls' />}
|
icon={<BiMenu size='1.25rem' className='clr-text-controls' />}
|
||||||
className='h-full pl-2'
|
className='h-full pl-2'
|
||||||
style={{ outlineColor: 'transparent' }}
|
style={{ outlineColor: 'transparent' }}
|
||||||
|
@ -172,6 +173,7 @@ function RSTabsMenu({
|
||||||
noBorder
|
noBorder
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title={'Редактирование'}
|
title={'Редактирование'}
|
||||||
|
hideTitle={editMenu.isOpen}
|
||||||
className='h-full'
|
className='h-full'
|
||||||
style={{ outlineColor: 'transparent' }}
|
style={{ outlineColor: 'transparent' }}
|
||||||
icon={<FiEdit size='1.25rem' className={isMutable ? 'clr-text-success' : 'clr-text-warning'} />}
|
icon={<FiEdit size='1.25rem' className={isMutable ? 'clr-text-success' : 'clr-text-warning'} />}
|
||||||
|
@ -201,6 +203,7 @@ function RSTabsMenu({
|
||||||
noBorder
|
noBorder
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title={`Режим ${labelAccessMode(mode)}`}
|
title={`Режим ${labelAccessMode(mode)}`}
|
||||||
|
hideTitle={accessMenu.isOpen}
|
||||||
className='h-full pr-2'
|
className='h-full pr-2'
|
||||||
style={{ outlineColor: 'transparent' }}
|
style={{ outlineColor: 'transparent' }}
|
||||||
icon={
|
icon={
|
||||||
|
|
|
@ -82,6 +82,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
transparent
|
transparent
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title='Настройка атрибутов для фильтрации'
|
title='Настройка атрибутов для фильтрации'
|
||||||
|
hideTitle={matchModeMenu.isOpen}
|
||||||
className='h-full'
|
className='h-full'
|
||||||
icon={<BiFilterAlt size='1.25rem' />}
|
icon={<BiFilterAlt size='1.25rem' />}
|
||||||
text={labelCstMatchMode(filterMatch)}
|
text={labelCstMatchMode(filterMatch)}
|
||||||
|
@ -94,6 +95,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
const matchMode = value as CstMatchMode;
|
const matchMode = value as CstMatchMode;
|
||||||
return (
|
return (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
|
className='w-[22rem]'
|
||||||
key={`${prefixes.cst_match_mode_list}${index}`}
|
key={`${prefixes.cst_match_mode_list}${index}`}
|
||||||
onClick={() => handleMatchModeChange(matchMode)}
|
onClick={() => handleMatchModeChange(matchMode)}
|
||||||
>
|
>
|
||||||
|
@ -111,6 +113,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
transparent
|
transparent
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title='Настройка фильтрации по графу термов'
|
title='Настройка фильтрации по графу термов'
|
||||||
|
hideTitle={sourceMenu.isOpen}
|
||||||
className='h-full pr-2'
|
className='h-full pr-2'
|
||||||
icon={<BiCog size='1.25rem' />}
|
icon={<BiCog size='1.25rem' />}
|
||||||
text={labelCstSource(filterSource)}
|
text={labelCstSource(filterSource)}
|
||||||
|
@ -122,7 +125,11 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
.map((value, index) => {
|
.map((value, index) => {
|
||||||
const source = value as DependencyMode;
|
const source = value as DependencyMode;
|
||||||
return (
|
return (
|
||||||
<DropdownButton key={`${prefixes.cst_source_list}${index}`} onClick={() => handleSourceChange(source)}>
|
<DropdownButton
|
||||||
|
className='w-[23rem]'
|
||||||
|
key={`${prefixes.cst_source_list}${index}`}
|
||||||
|
onClick={() => handleSourceChange(source)}
|
||||||
|
>
|
||||||
<p>
|
<p>
|
||||||
<b>{labelCstSource(source)}:</b> {describeCstSource(source)}
|
<b>{labelCstSource(source)}:</b> {describeCstSource(source)}
|
||||||
</p>
|
</p>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user