mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Refactor UI state flags
This commit is contained in:
parent
102f8c2baf
commit
40696fa553
|
@ -1,6 +1,3 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { BiDownvote, BiDuplicate, BiPlusCircle, BiReset, BiTrash, BiUpvote } from 'react-icons/bi';
|
import { BiDownvote, BiDuplicate, BiPlusCircle, BiReset, BiTrash, BiUpvote } from 'react-icons/bi';
|
||||||
import { FiSave } from 'react-icons/fi';
|
import { FiSave } from 'react-icons/fi';
|
||||||
|
|
||||||
|
@ -9,8 +6,8 @@ import Overlay from '@/components/ui/Overlay';
|
||||||
import { messages, prepareTooltip } from '@/utils/labels';
|
import { messages, prepareTooltip } from '@/utils/labels';
|
||||||
|
|
||||||
interface ConstituentaToolbarProps {
|
interface ConstituentaToolbarProps {
|
||||||
isMutable: boolean;
|
disabled: boolean;
|
||||||
isModified: boolean;
|
modified: boolean;
|
||||||
|
|
||||||
onSubmit: () => void;
|
onSubmit: () => void;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
|
@ -23,8 +20,8 @@ interface ConstituentaToolbarProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConstituentaToolbar({
|
function ConstituentaToolbar({
|
||||||
isMutable,
|
disabled,
|
||||||
isModified,
|
modified,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onReset,
|
onReset,
|
||||||
onMoveUp,
|
onMoveUp,
|
||||||
|
@ -33,49 +30,48 @@ function ConstituentaToolbar({
|
||||||
onClone,
|
onClone,
|
||||||
onCreate
|
onCreate
|
||||||
}: ConstituentaToolbarProps) {
|
}: ConstituentaToolbarProps) {
|
||||||
const canSave = useMemo(() => isModified && isMutable, [isModified, isMutable]);
|
|
||||||
return (
|
return (
|
||||||
<Overlay position='top-1 right-4 sm:right-1/2 sm:translate-x-1/2' className='flex'>
|
<Overlay position='top-1 right-4 sm:right-1/2 sm:translate-x-1/2' className='flex'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
|
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
|
||||||
disabled={!canSave}
|
|
||||||
icon={<FiSave size='1.25rem' className='icon-primary' />}
|
icon={<FiSave size='1.25rem' className='icon-primary' />}
|
||||||
|
disabled={disabled || !modified}
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Сбросить несохраненные изменения'
|
title='Сбросить несохраненные изменения'
|
||||||
disabled={!canSave}
|
|
||||||
onClick={onReset}
|
|
||||||
icon={<BiReset size='1.25rem' className='icon-primary' />}
|
icon={<BiReset size='1.25rem' className='icon-primary' />}
|
||||||
|
disabled={disabled || !modified}
|
||||||
|
onClick={onReset}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Создать конституенту после данной'
|
title='Создать конституенту после данной'
|
||||||
disabled={!isMutable}
|
|
||||||
onClick={onCreate}
|
|
||||||
icon={<BiPlusCircle size={'1.25rem'} className='icon-green' />}
|
icon={<BiPlusCircle size={'1.25rem'} className='icon-green' />}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={onCreate}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={isModified ? messages.unsaved : prepareTooltip('Клонировать конституенту', 'Alt + V')}
|
titleHtml={modified ? messages.unsaved : prepareTooltip('Клонировать конституенту', 'Alt + V')}
|
||||||
disabled={!isMutable || isModified}
|
|
||||||
onClick={onClone}
|
|
||||||
icon={<BiDuplicate size='1.25rem' className='icon-green' />}
|
icon={<BiDuplicate size='1.25rem' className='icon-green' />}
|
||||||
|
disabled={disabled || modified}
|
||||||
|
onClick={onClone}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Удалить редактируемую конституенту'
|
title='Удалить редактируемую конституенту'
|
||||||
disabled={!isMutable}
|
disabled={disabled}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
icon={<BiTrash size='1.25rem' className='icon-red' />}
|
icon={<BiTrash size='1.25rem' className='icon-red' />}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
|
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
|
||||||
icon={<BiUpvote size='1.25rem' className='icon-primary' />}
|
icon={<BiUpvote size='1.25rem' className='icon-primary' />}
|
||||||
disabled={!isMutable}
|
disabled={disabled || modified}
|
||||||
onClick={onMoveUp}
|
onClick={onMoveUp}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
|
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
|
||||||
icon={<BiDownvote size='1.25rem' className='icon-primary' />}
|
icon={<BiDownvote size='1.25rem' className='icon-primary' />}
|
||||||
disabled={!isMutable}
|
disabled={disabled || modified}
|
||||||
onClick={onMoveDown}
|
onClick={onMoveDown}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
|
@ -8,33 +8,26 @@ import { messages } from '@/utils/labels';
|
||||||
|
|
||||||
interface ControlsOverlayProps {
|
interface ControlsOverlayProps {
|
||||||
constituenta?: IConstituenta;
|
constituenta?: IConstituenta;
|
||||||
isMutable: boolean;
|
disabled: boolean;
|
||||||
isModified: boolean;
|
modified: boolean;
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
|
|
||||||
onRename: () => void;
|
onRename: () => void;
|
||||||
onEditTerm: () => void;
|
onEditTerm: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ControlsOverlay({
|
function ControlsOverlay({ constituenta, disabled, modified, processing, onRename, onEditTerm }: ControlsOverlayProps) {
|
||||||
constituenta,
|
|
||||||
isMutable,
|
|
||||||
isModified,
|
|
||||||
processing,
|
|
||||||
onRename,
|
|
||||||
onEditTerm
|
|
||||||
}: ControlsOverlayProps) {
|
|
||||||
return (
|
return (
|
||||||
<Overlay position='top-1 left-[4.1rem]' className='flex select-none'>
|
<Overlay position='top-1 left-[4.1rem]' className='flex select-none'>
|
||||||
{isMutable || processing ? (
|
{!disabled || processing ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title={
|
title={
|
||||||
isModified ? messages.unsaved : `Редактировать словоформы термина: ${constituenta?.term_forms.length ?? 0}`
|
modified ? messages.unsaved : `Редактировать словоформы термина: ${constituenta?.term_forms.length ?? 0}`
|
||||||
}
|
}
|
||||||
noHover
|
noHover
|
||||||
onClick={onEditTerm}
|
onClick={onEditTerm}
|
||||||
icon={<LiaEdit size='1rem' className='icon-primary' />}
|
icon={<LiaEdit size='1rem' className='icon-primary' />}
|
||||||
disabled={isModified}
|
disabled={modified}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<div
|
<div
|
||||||
|
@ -42,19 +35,19 @@ function ControlsOverlay({
|
||||||
'pt-1 pl-[1.375rem]', // prettier: split lines
|
'pt-1 pl-[1.375rem]', // prettier: split lines
|
||||||
'text-sm font-medium whitespace-nowrap',
|
'text-sm font-medium whitespace-nowrap',
|
||||||
'select-text cursor-default',
|
'select-text cursor-default',
|
||||||
!isMutable && !processing && 'pl-[2.8rem]'
|
disabled && !processing && 'pl-[2.8rem]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span>Имя </span>
|
<span>Имя </span>
|
||||||
<span className='ml-1'>{constituenta?.alias ?? ''}</span>
|
<span className='ml-1'>{constituenta?.alias ?? ''}</span>
|
||||||
</div>
|
</div>
|
||||||
{isMutable || processing ? (
|
{!disabled || processing ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
noHover
|
noHover
|
||||||
title={isModified ? messages.unsaved : 'Переименовать конституенту'}
|
title={modified ? messages.unsaved : 'Переименовать конституенту'}
|
||||||
onClick={onRename}
|
onClick={onRename}
|
||||||
icon={<LiaEdit size='1rem' className='icon-primary' />}
|
icon={<LiaEdit size='1rem' className='icon-primary' />}
|
||||||
disabled={isModified}
|
disabled={modified}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
|
@ -35,8 +35,8 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
|
||||||
const [toggleReset, setToggleReset] = useState(false);
|
const [toggleReset, setToggleReset] = useState(false);
|
||||||
|
|
||||||
const disabled = useMemo(
|
const disabled = useMemo(
|
||||||
() => !activeCst || !controller.isContentEditable,
|
() => !activeCst || !controller.isContentEditable || controller.isProcessing,
|
||||||
[activeCst, controller.isContentEditable]
|
[activeCst, controller.isContentEditable, controller.isProcessing]
|
||||||
);
|
);
|
||||||
|
|
||||||
const isNarrow = useMemo(() => !!windowSize.width && windowSize.width <= SIDELIST_LAYOUT_THRESHOLD, [windowSize]);
|
const isNarrow = useMemo(() => !!windowSize.width && windowSize.width <= SIDELIST_LAYOUT_THRESHOLD, [windowSize]);
|
||||||
|
@ -79,10 +79,10 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{controller.isContentEditable || controller.isProcessing ? (
|
{controller.isContentEditable ? (
|
||||||
<ConstituentaToolbar
|
<ConstituentaToolbar
|
||||||
isMutable={!disabled}
|
disabled={disabled}
|
||||||
isModified={isModified}
|
modified={isModified}
|
||||||
onMoveUp={controller.moveUp}
|
onMoveUp={controller.moveUp}
|
||||||
onMoveDown={controller.moveDown}
|
onMoveDown={controller.moveDown}
|
||||||
onSubmit={initiateSubmit}
|
onSubmit={initiateSubmit}
|
||||||
|
@ -102,7 +102,7 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
>
|
>
|
||||||
<FormConstituenta
|
<FormConstituenta
|
||||||
isMutable={!disabled}
|
disabled={disabled}
|
||||||
showList={showList}
|
showList={showList}
|
||||||
id={globals.constituenta_editor}
|
id={globals.constituenta_editor}
|
||||||
constituenta={activeCst}
|
constituenta={activeCst}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import ControlsOverlay from './ControlsOverlay';
|
||||||
export const ROW_SIZE_IN_CHARACTERS = 70;
|
export const ROW_SIZE_IN_CHARACTERS = 70;
|
||||||
|
|
||||||
interface FormConstituentaProps {
|
interface FormConstituentaProps {
|
||||||
isMutable: boolean;
|
disabled: boolean;
|
||||||
showList: boolean;
|
showList: boolean;
|
||||||
|
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -37,7 +37,7 @@ interface FormConstituentaProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormConstituenta({
|
function FormConstituenta({
|
||||||
isMutable,
|
disabled,
|
||||||
showList,
|
showList,
|
||||||
id,
|
id,
|
||||||
isModified,
|
isModified,
|
||||||
|
@ -114,8 +114,8 @@ function FormConstituenta({
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ControlsOverlay
|
<ControlsOverlay
|
||||||
isMutable={isMutable}
|
disabled={disabled}
|
||||||
isModified={isModified}
|
modified={isModified}
|
||||||
processing={processing}
|
processing={processing}
|
||||||
constituenta={constituenta}
|
constituenta={constituenta}
|
||||||
onEditTerm={onEditTerm}
|
onEditTerm={onEditTerm}
|
||||||
|
@ -134,14 +134,14 @@ function FormConstituenta({
|
||||||
value={term}
|
value={term}
|
||||||
initialValue={constituenta?.term_raw ?? ''}
|
initialValue={constituenta?.term_raw ?? ''}
|
||||||
resolved={constituenta?.term_resolved ?? ''}
|
resolved={constituenta?.term_resolved ?? ''}
|
||||||
disabled={!isMutable}
|
disabled={disabled}
|
||||||
onChange={newValue => setTerm(newValue)}
|
onChange={newValue => setTerm(newValue)}
|
||||||
/>
|
/>
|
||||||
<TextArea
|
<TextArea
|
||||||
id='cst_typification'
|
id='cst_typification'
|
||||||
dense
|
dense
|
||||||
noBorder
|
noBorder
|
||||||
disabled
|
disabled={true}
|
||||||
label='Типизация'
|
label='Типизация'
|
||||||
rows={typification.length > ROW_SIZE_IN_CHARACTERS ? 2 : 1}
|
rows={typification.length > ROW_SIZE_IN_CHARACTERS ? 2 : 1}
|
||||||
value={typification}
|
value={typification}
|
||||||
|
@ -157,7 +157,7 @@ function FormConstituenta({
|
||||||
value={expression}
|
value={expression}
|
||||||
activeCst={constituenta}
|
activeCst={constituenta}
|
||||||
showList={showList}
|
showList={showList}
|
||||||
disabled={!isMutable}
|
disabled={disabled}
|
||||||
toggleReset={toggleReset}
|
toggleReset={toggleReset}
|
||||||
onToggleList={onToggleList}
|
onToggleList={onToggleList}
|
||||||
onChange={newValue => setExpression(newValue)}
|
onChange={newValue => setExpression(newValue)}
|
||||||
|
@ -172,7 +172,7 @@ function FormConstituenta({
|
||||||
value={textDefinition}
|
value={textDefinition}
|
||||||
initialValue={constituenta?.definition_raw ?? ''}
|
initialValue={constituenta?.definition_raw ?? ''}
|
||||||
resolved={constituenta?.definition_resolved ?? ''}
|
resolved={constituenta?.definition_resolved ?? ''}
|
||||||
disabled={!isMutable}
|
disabled={disabled}
|
||||||
onChange={newValue => setTextDefinition(newValue)}
|
onChange={newValue => setTextDefinition(newValue)}
|
||||||
/>
|
/>
|
||||||
<TextArea
|
<TextArea
|
||||||
|
@ -181,15 +181,15 @@ function FormConstituenta({
|
||||||
label='Конвенция / Комментарий'
|
label='Конвенция / Комментарий'
|
||||||
placeholder='Договоренность об интерпретации или пояснение'
|
placeholder='Договоренность об интерпретации или пояснение'
|
||||||
value={convention}
|
value={convention}
|
||||||
disabled={!isMutable}
|
disabled={disabled}
|
||||||
rows={convention.length > 2 * ROW_SIZE_IN_CHARACTERS ? 3 : 2}
|
rows={convention.length > 2 * ROW_SIZE_IN_CHARACTERS ? 3 : 2}
|
||||||
onChange={event => setConvention(event.target.value)}
|
onChange={event => setConvention(event.target.value)}
|
||||||
/>
|
/>
|
||||||
{isMutable || processing ? (
|
{!disabled || processing ? (
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Сохранить изменения'
|
text='Сохранить изменения'
|
||||||
className='self-center'
|
className='self-center'
|
||||||
disabled={!isModified || !isMutable}
|
disabled={disabled || !isModified}
|
||||||
icon={<FiSave size='1.25rem' />}
|
icon={<FiSave size='1.25rem' />}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -118,7 +118,7 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
|
||||||
/>
|
/>
|
||||||
<div className='flex flex-col'>
|
<div className='flex flex-col'>
|
||||||
<Overlay position='top-[-0.25rem] right-[-0.25rem] flex'>
|
<Overlay position='top-[-0.25rem] right-[-0.25rem] flex'>
|
||||||
{controller.isMutable || (controller.isMutable && controller.isProcessing) ? (
|
{controller.isMutable ? (
|
||||||
<>
|
<>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
noHover
|
noHover
|
||||||
|
@ -152,7 +152,7 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
|
||||||
label='Комментарий'
|
label='Комментарий'
|
||||||
rows={3}
|
rows={3}
|
||||||
value={comment}
|
value={comment}
|
||||||
disabled={!controller.isContentEditable}
|
disabled={!controller.isContentEditable || controller.isProcessing}
|
||||||
onChange={event => setComment(event.target.value)}
|
onChange={event => setComment(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className='flex justify-between whitespace-nowrap'>
|
<div className='flex justify-between whitespace-nowrap'>
|
||||||
|
@ -160,7 +160,7 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
|
||||||
id='schema_common'
|
id='schema_common'
|
||||||
label='Общедоступная схема'
|
label='Общедоступная схема'
|
||||||
title='Общедоступные схемы видны всем пользователям и могут быть изменены'
|
title='Общедоступные схемы видны всем пользователям и могут быть изменены'
|
||||||
disabled={!controller.isContentEditable}
|
disabled={!controller.isContentEditable || controller.isProcessing}
|
||||||
value={common}
|
value={common}
|
||||||
setValue={value => setCommon(value)}
|
setValue={value => setCommon(value)}
|
||||||
/>
|
/>
|
||||||
|
@ -168,17 +168,17 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
|
||||||
id='schema_immutable'
|
id='schema_immutable'
|
||||||
label='Неизменная схема'
|
label='Неизменная схема'
|
||||||
title='Только администраторы могут присваивать схемам неизменный статус'
|
title='Только администраторы могут присваивать схемам неизменный статус'
|
||||||
disabled={!controller.isContentEditable || !user?.is_staff}
|
disabled={!controller.isContentEditable || !user?.is_staff || controller.isProcessing}
|
||||||
value={canonical}
|
value={canonical}
|
||||||
setValue={value => setCanonical(value)}
|
setValue={value => setCanonical(value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{controller.isContentEditable || (controller.isMutable && controller.isProcessing) ? (
|
{controller.isContentEditable ? (
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Сохранить изменения'
|
text='Сохранить изменения'
|
||||||
className='self-center'
|
className='self-center'
|
||||||
loading={processing}
|
loading={processing}
|
||||||
disabled={!isModified || controller.isProcessing}
|
disabled={!isModified}
|
||||||
icon={<FiSave size='1.25rem' />}
|
icon={<FiSave size='1.25rem' />}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -24,10 +24,10 @@ interface RSFormToolbarProps {
|
||||||
|
|
||||||
function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, onDestroy }: RSFormToolbarProps) {
|
function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, onDestroy }: RSFormToolbarProps) {
|
||||||
const controller = useRSEdit();
|
const controller = useRSEdit();
|
||||||
const canSave = useMemo(() => modified && controller.isMutable, [modified, controller.isMutable]);
|
const canSave = useMemo(() => modified && !controller.isProcessing, [modified, controller.isProcessing]);
|
||||||
return (
|
return (
|
||||||
<Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'>
|
<Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'>
|
||||||
{controller.isContentEditable || (controller.isMutable && controller.isProcessing) ? (
|
{controller.isContentEditable ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
|
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
|
||||||
disabled={!canSave}
|
disabled={!canSave}
|
||||||
|
@ -48,7 +48,6 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
|
||||||
{!anonymous ? (
|
{!anonymous ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={`Отслеживание <b>${subscribed ? 'включено' : 'выключено'}</b>`}
|
titleHtml={`Отслеживание <b>${subscribed ? 'включено' : 'выключено'}</b>`}
|
||||||
disabled={controller.isProcessing}
|
|
||||||
icon={
|
icon={
|
||||||
subscribed ? (
|
subscribed ? (
|
||||||
<FiBell size='1.25rem' className='icon-primary' />
|
<FiBell size='1.25rem' className='icon-primary' />
|
||||||
|
@ -56,6 +55,7 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
|
||||||
<FiBellOff size='1.25rem' className='clr-text-controls' />
|
<FiBellOff size='1.25rem' className='clr-text-controls' />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
disabled={controller.isProcessing}
|
||||||
onClick={controller.toggleSubscribe}
|
onClick={controller.toggleSubscribe}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -67,12 +67,12 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
|
||||||
onClick={controller.claim}
|
onClick={controller.claim}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{controller.isContentEditable || (controller.isMutable && controller.isProcessing) ? (
|
{controller.isMutable ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Удалить схему'
|
title='Удалить схему'
|
||||||
disabled={!controller.isMutable}
|
|
||||||
onClick={onDestroy}
|
|
||||||
icon={<BiTrash size='1.25rem' className='icon-red' />}
|
icon={<BiTrash size='1.25rem' className='icon-red' />}
|
||||||
|
disabled={!controller.isContentEditable || controller.isProcessing}
|
||||||
|
onClick={onDestroy}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<HelpButton topic={HelpTopic.RSFORM} offset={4} />
|
<HelpButton topic={HelpTopic.RSFORM} offset={4} />
|
||||||
|
|
|
@ -51,7 +51,7 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTableKey(event: React.KeyboardEvent<HTMLDivElement>) {
|
function handleTableKey(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||||
if (!controller.isMutable) {
|
if (!controller.isContentEditable || controller.isProcessing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.key === 'Delete' && selected.length > 0) {
|
if (event.key === 'Delete' && selected.length > 0) {
|
||||||
|
@ -97,7 +97,7 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div tabIndex={-1} className='outline-none' onKeyDown={handleTableKey}>
|
<div tabIndex={-1} className='outline-none' onKeyDown={handleTableKey}>
|
||||||
{controller.isContentEditable || (controller.isMutable && controller.isProcessing) ? (
|
{controller.isContentEditable ? (
|
||||||
<SelectedCounter
|
<SelectedCounter
|
||||||
totalCount={controller.schema?.stats?.count_all ?? 0}
|
totalCount={controller.schema?.stats?.count_all ?? 0}
|
||||||
selectedCount={selected.length}
|
selectedCount={selected.length}
|
||||||
|
@ -105,20 +105,18 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps)
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{controller.isContentEditable || (controller.isMutable && controller.isProcessing) ? (
|
{controller.isContentEditable ? <RSListToolbar selectedCount={selected.length} /> : null}
|
||||||
<RSListToolbar selectedCount={selected.length} />
|
|
||||||
) : null}
|
|
||||||
<div
|
<div
|
||||||
className={clsx('border-b', {
|
className={clsx('border-b', {
|
||||||
'pt-[2.3rem]': controller.isContentEditable || controller.isProcessing,
|
'pt-[2.3rem]': controller.isContentEditable,
|
||||||
'relative top-[-1px]': !controller.isContentEditable && !controller.isProcessing
|
'relative top-[-1px]': !controller.isContentEditable
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<RSTable
|
<RSTable
|
||||||
items={controller.schema?.items}
|
items={controller.schema?.items}
|
||||||
maxHeight={tableHeight}
|
maxHeight={tableHeight}
|
||||||
enableSelection={controller.isContentEditable || (controller.isMutable && controller.isProcessing)}
|
enableSelection={controller.isContentEditable}
|
||||||
selected={rowSelection}
|
selected={rowSelection}
|
||||||
setSelected={handleRowSelection}
|
setSelected={handleRowSelection}
|
||||||
onEdit={onOpenEdit}
|
onEdit={onOpenEdit}
|
||||||
|
|
|
@ -31,25 +31,25 @@ function RSListToolbar({ selectedCount }: RSListToolbarProps) {
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
|
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
|
||||||
icon={<BiUpvote size='1.25rem' className='icon-primary' />}
|
icon={<BiUpvote size='1.25rem' className='icon-primary' />}
|
||||||
disabled={!controller.isMutable || nothingSelected}
|
disabled={controller.isProcessing || nothingSelected}
|
||||||
onClick={controller.moveUp}
|
onClick={controller.moveUp}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
|
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
|
||||||
icon={<BiDownvote size='1.25rem' className='icon-primary' />}
|
icon={<BiDownvote size='1.25rem' className='icon-primary' />}
|
||||||
disabled={!controller.isMutable || nothingSelected}
|
disabled={controller.isProcessing || nothingSelected}
|
||||||
onClick={controller.moveDown}
|
onClick={controller.moveDown}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Клонировать конституенту', 'Alt + V')}
|
titleHtml={prepareTooltip('Клонировать конституенту', 'Alt + V')}
|
||||||
icon={<BiDuplicate size='1.25rem' className='icon-green' />}
|
icon={<BiDuplicate size='1.25rem' className='icon-green' />}
|
||||||
disabled={!controller.isMutable || selectedCount !== 1}
|
disabled={controller.isProcessing || selectedCount !== 1}
|
||||||
onClick={controller.cloneCst}
|
onClick={controller.cloneCst}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Добавить новую конституенту...', 'Alt + `')}
|
titleHtml={prepareTooltip('Добавить новую конституенту...', 'Alt + `')}
|
||||||
icon={<BiPlusCircle size='1.25rem' className='icon-green' />}
|
icon={<BiPlusCircle size='1.25rem' className='icon-green' />}
|
||||||
disabled={!controller.isMutable}
|
disabled={controller.isProcessing}
|
||||||
onClick={() => controller.createCst(undefined, false)}
|
onClick={() => controller.createCst(undefined, false)}
|
||||||
/>
|
/>
|
||||||
<div ref={insertMenu.ref}>
|
<div ref={insertMenu.ref}>
|
||||||
|
@ -57,7 +57,7 @@ function RSListToolbar({ selectedCount }: RSListToolbarProps) {
|
||||||
title='Добавить пустую конституенту'
|
title='Добавить пустую конституенту'
|
||||||
hideTitle={insertMenu.isOpen}
|
hideTitle={insertMenu.isOpen}
|
||||||
icon={<BiDownArrowCircle size='1.25rem' className='icon-green' />}
|
icon={<BiDownArrowCircle size='1.25rem' className='icon-green' />}
|
||||||
disabled={!controller.isMutable}
|
disabled={controller.isProcessing}
|
||||||
onClick={insertMenu.toggle}
|
onClick={insertMenu.toggle}
|
||||||
/>
|
/>
|
||||||
<Dropdown isOpen={insertMenu.isOpen}>
|
<Dropdown isOpen={insertMenu.isOpen}>
|
||||||
|
@ -74,7 +74,7 @@ function RSListToolbar({ selectedCount }: RSListToolbarProps) {
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Удалить выбранные', 'Delete')}
|
titleHtml={prepareTooltip('Удалить выбранные', 'Delete')}
|
||||||
icon={<BiTrash size='1.25rem' className='icon-red' />}
|
icon={<BiTrash size='1.25rem' className='icon-red' />}
|
||||||
disabled={!controller.isMutable || nothingSelected}
|
disabled={controller.isProcessing || nothingSelected}
|
||||||
onClick={controller.deleteCst}
|
onClick={controller.deleteCst}
|
||||||
/>
|
/>
|
||||||
<HelpButton topic={HelpTopic.CSTLIST} offset={5} />
|
<HelpButton topic={HelpTopic.CSTLIST} offset={5} />
|
||||||
|
|
|
@ -169,7 +169,7 @@ function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphP
|
||||||
|
|
||||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||||
// Hotkeys implementation
|
// Hotkeys implementation
|
||||||
if (!controller.isMutable) {
|
if (!controller.isContentEditable || controller.isProcessing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.key === 'Delete') {
|
if (event.key === 'Delete') {
|
||||||
|
|
|
@ -68,19 +68,19 @@ function GraphToolbar({
|
||||||
disabled={!is3D}
|
disabled={!is3D}
|
||||||
onClick={toggleOrbit}
|
onClick={toggleOrbit}
|
||||||
/>
|
/>
|
||||||
{controller.isMutable || controller.isProcessing ? (
|
{controller.isContentEditable ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Новая конституента'
|
title='Новая конституента'
|
||||||
icon={<BiPlusCircle size='1.25rem' className='icon-green' />}
|
icon={<BiPlusCircle size='1.25rem' className='icon-green' />}
|
||||||
disabled={!controller.isMutable}
|
disabled={controller.isProcessing}
|
||||||
onClick={onCreate}
|
onClick={onCreate}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{controller.isMutable || controller.isProcessing ? (
|
{controller.isContentEditable ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Удалить выбранные'
|
title='Удалить выбранные'
|
||||||
icon={<BiTrash size='1.25rem' className='icon-red' />}
|
icon={<BiTrash size='1.25rem' className='icon-red' />}
|
||||||
disabled={nothingSelected || !controller.isMutable}
|
disabled={nothingSelected || controller.isProcessing}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -114,13 +114,9 @@ export const RSEditState = ({
|
||||||
|
|
||||||
const isMutable = useMemo(() => {
|
const isMutable = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
!model.loading &&
|
mode !== UserAccessMode.READER && ((model.isOwned || (mode === UserAccessMode.ADMIN && user?.is_staff)) ?? false)
|
||||||
!model.processing &&
|
|
||||||
mode !== UserAccessMode.READER &&
|
|
||||||
((model.isOwned || (mode === UserAccessMode.ADMIN && user?.is_staff)) ?? false)
|
|
||||||
);
|
);
|
||||||
}, [user?.is_staff, mode, model.isOwned, model.loading, model.processing]);
|
}, [user?.is_staff, mode, model.isOwned]);
|
||||||
|
|
||||||
const isContentEditable = useMemo(() => isMutable && !model.isArchive, [isMutable, model.isArchive]);
|
const isContentEditable = useMemo(() => isMutable && !model.isArchive, [isMutable, model.isArchive]);
|
||||||
|
|
||||||
const [showUpload, setShowUpload] = useState(false);
|
const [showUpload, setShowUpload] = useState(false);
|
||||||
|
|
|
@ -138,9 +138,9 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
|
||||||
<Dropdown isOpen={schemaMenu.isOpen}>
|
<Dropdown isOpen={schemaMenu.isOpen}>
|
||||||
{user ? (
|
{user ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!model.isClaimable && !model.isOwned}
|
|
||||||
text={model.isOwned ? 'Вы — владелец' : 'Стать владельцем'}
|
text={model.isOwned ? 'Вы — владелец' : 'Стать владельцем'}
|
||||||
icon={<LuCrown size='1rem' className='icon-green' />}
|
icon={<LuCrown size='1rem' className='icon-green' />}
|
||||||
|
disabled={!model.isClaimable && !model.isOwned}
|
||||||
onClick={!model.isOwned && model.isClaimable ? handleClaimOwner : undefined}
|
onClick={!model.isOwned && model.isClaimable ? handleClaimOwner : undefined}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -151,9 +151,9 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
|
||||||
/>
|
/>
|
||||||
{user ? (
|
{user ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={model.isArchive}
|
|
||||||
text='Клонировать'
|
text='Клонировать'
|
||||||
icon={<BiDuplicate size='1rem' className='icon-primary' />}
|
icon={<BiDuplicate size='1rem' className='icon-primary' />}
|
||||||
|
disabled={model.isArchive}
|
||||||
onClick={handleClone}
|
onClick={handleClone}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -162,11 +162,11 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
|
||||||
icon={<BiDownload size='1rem' className='icon-primary' />}
|
icon={<BiDownload size='1rem' className='icon-primary' />}
|
||||||
onClick={handleDownload}
|
onClick={handleDownload}
|
||||||
/>
|
/>
|
||||||
{user ? (
|
{controller.isContentEditable ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!controller.isContentEditable}
|
|
||||||
text='Загрузить из Экстеора'
|
text='Загрузить из Экстеора'
|
||||||
icon={<BiUpload size='1rem' className='icon-red' />}
|
icon={<BiUpload size='1rem' className='icon-red' />}
|
||||||
|
disabled={controller.isProcessing}
|
||||||
onClick={handleUpload}
|
onClick={handleUpload}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -174,6 +174,7 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Удалить схему'
|
text='Удалить схему'
|
||||||
icon={<BiTrash size='1rem' className='icon-red' />}
|
icon={<BiTrash size='1rem' className='icon-red' />}
|
||||||
|
disabled={controller.isProcessing}
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -208,40 +209,40 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
|
||||||
/>
|
/>
|
||||||
<Dropdown isOpen={editMenu.isOpen}>
|
<Dropdown isOpen={editMenu.isOpen}>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!controller.isContentEditable}
|
|
||||||
text='Шаблоны'
|
text='Шаблоны'
|
||||||
title='Создать конституенту из шаблона'
|
title='Создать конституенту из шаблона'
|
||||||
icon={<BiDiamond size='1rem' className='icon-green' />}
|
icon={<BiDiamond size='1rem' className='icon-green' />}
|
||||||
|
disabled={!controller.isContentEditable || controller.isProcessing}
|
||||||
onClick={handleTemplates}
|
onClick={handleTemplates}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!controller.isContentEditable}
|
|
||||||
text='Встраивание'
|
text='Встраивание'
|
||||||
title='Импортировать совокупность конституент из другой схемы'
|
title='Импортировать совокупность конституент из другой схемы'
|
||||||
icon={<LuBookCopy size='1rem' className='icon-green' />}
|
icon={<LuBookCopy size='1rem' className='icon-green' />}
|
||||||
|
disabled={!controller.isContentEditable || controller.isProcessing}
|
||||||
onClick={handleInlineSynthesis}
|
onClick={handleInlineSynthesis}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!controller.isContentEditable}
|
|
||||||
className='border-t-2'
|
className='border-t-2'
|
||||||
text='Порядковые имена'
|
text='Порядковые имена'
|
||||||
title='Присвоить порядковые имена и обновить выражения'
|
title='Присвоить порядковые имена и обновить выражения'
|
||||||
icon={<LuWand2 size='1rem' className='icon-primary' />}
|
icon={<LuWand2 size='1rem' className='icon-primary' />}
|
||||||
|
disabled={!controller.isContentEditable || controller.isProcessing}
|
||||||
onClick={handleReindex}
|
onClick={handleReindex}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!controller.isContentEditable || !controller.canProduceStructure}
|
|
||||||
text='Порождение структуры'
|
text='Порождение структуры'
|
||||||
title='Раскрыть структуру типизации выделенной конституенты'
|
title='Раскрыть структуру типизации выделенной конституенты'
|
||||||
icon={<LuNetwork size='1rem' className='icon-primary' />}
|
icon={<LuNetwork size='1rem' className='icon-primary' />}
|
||||||
|
disabled={!controller.isContentEditable || !controller.canProduceStructure}
|
||||||
onClick={handleProduceStructure}
|
onClick={handleProduceStructure}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!controller.isContentEditable}
|
|
||||||
text='Отождествление'
|
text='Отождествление'
|
||||||
title='Заменить вхождения одной конституенты на другую'
|
title='Заменить вхождения одной конституенты на другую'
|
||||||
icon={<LuReplace size='1rem' className='icon-red' />}
|
icon={<LuReplace size='1rem' className='icon-red' />}
|
||||||
onClick={handleSubstituteCst}
|
onClick={handleSubstituteCst}
|
||||||
|
disabled={!controller.isContentEditable || controller.isProcessing}
|
||||||
/>
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
@ -289,17 +290,17 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
|
||||||
onClick={() => handleChangeMode(UserAccessMode.READER)}
|
onClick={() => handleChangeMode(UserAccessMode.READER)}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!model.isOwned}
|
|
||||||
text={labelAccessMode(UserAccessMode.OWNER)}
|
text={labelAccessMode(UserAccessMode.OWNER)}
|
||||||
title={describeAccessMode(UserAccessMode.OWNER)}
|
title={describeAccessMode(UserAccessMode.OWNER)}
|
||||||
icon={<LuCrown size='1rem' className='icon-primary' />}
|
icon={<LuCrown size='1rem' className='icon-primary' />}
|
||||||
|
disabled={!model.isOwned}
|
||||||
onClick={() => handleChangeMode(UserAccessMode.OWNER)}
|
onClick={() => handleChangeMode(UserAccessMode.OWNER)}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!user?.is_staff}
|
|
||||||
text={labelAccessMode(UserAccessMode.ADMIN)}
|
text={labelAccessMode(UserAccessMode.ADMIN)}
|
||||||
title={describeAccessMode(UserAccessMode.ADMIN)}
|
title={describeAccessMode(UserAccessMode.ADMIN)}
|
||||||
icon={<BiMeteor size='1rem' className='icon-primary' />}
|
icon={<BiMeteor size='1rem' className='icon-primary' />}
|
||||||
|
disabled={!user?.is_staff}
|
||||||
onClick={() => handleChangeMode(UserAccessMode.ADMIN)}
|
onClick={() => handleChangeMode(UserAccessMode.ADMIN)}
|
||||||
/>
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user