mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
R: Refactor menu bars and fix QR dialog styling
Some checks failed
Frontend CI / build (22.x) (push) Has been cancelled
Some checks failed
Frontend CI / build (22.x) (push) Has been cancelled
This commit is contained in:
parent
fbdd561344
commit
9e50312d23
|
@ -9,6 +9,7 @@ import { useDialogsStore } from '@/stores/dialogs';
|
|||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip } from '@/utils/utils';
|
||||
|
||||
import { Overlay } from '../Container';
|
||||
import { Button, MiniButton, SubmitButton } from '../Control';
|
||||
import { IconClose } from '../Icons';
|
||||
import { type Styling } from '../props';
|
||||
|
@ -103,6 +104,7 @@ export function ModalForm({
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
<Overlay className='z-modalOverlay'>
|
||||
<MiniButton
|
||||
noPadding
|
||||
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
|
||||
|
@ -110,6 +112,7 @@ export function ModalForm({
|
|||
className='float-right mt-2 mr-2'
|
||||
onClick={handleCancel}
|
||||
/>
|
||||
</Overlay>
|
||||
|
||||
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { useDialogsStore } from '@/stores/dialogs';
|
|||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip } from '@/utils/utils';
|
||||
|
||||
import { Overlay } from '../Container';
|
||||
import { Button, MiniButton } from '../Control';
|
||||
import { IconClose } from '../Icons';
|
||||
|
||||
|
@ -48,6 +49,7 @@ export function ModalView({
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
<Overlay className='z-modalOverlay'>
|
||||
<MiniButton
|
||||
noPadding
|
||||
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
|
||||
|
@ -55,6 +57,7 @@ export function ModalView({
|
|||
className='float-right mt-2 mr-2'
|
||||
onClick={hideDialog}
|
||||
/>
|
||||
</Overlay>
|
||||
|
||||
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { UserRole } from '@/features/users';
|
||||
|
||||
import { IconAdmin, IconEditor, IconOwner, IconReader } from '@/components/Icons';
|
||||
|
||||
interface IconRoleProps {
|
||||
role: UserRole;
|
||||
size?: string;
|
||||
}
|
||||
|
||||
export function IconRole({ role, size = '1.25rem' }: IconRoleProps) {
|
||||
switch (role) {
|
||||
case UserRole.ADMIN:
|
||||
return <IconAdmin size={size} className='icon-primary' />;
|
||||
case UserRole.OWNER:
|
||||
return <IconOwner size={size} className='icon-primary' />;
|
||||
case UserRole.EDITOR:
|
||||
return <IconEditor size={size} className='icon-primary' />;
|
||||
case UserRole.READER:
|
||||
return <IconReader size={size} className='icon-primary' />;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
import { urls, useConceptNavigation } from '@/app';
|
||||
import { useAuthSuspense } from '@/features/auth';
|
||||
import { useRoleStore, UserRole } from '@/features/users';
|
||||
import { describeUserRole, labelUserRole } from '@/features/users/labels';
|
||||
|
||||
import { Button } from '@/components/Control';
|
||||
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
||||
import { IconAlert } from '@/components/Icons';
|
||||
|
||||
import { IconRole } from './IconRole';
|
||||
|
||||
interface MenuRoleProps {
|
||||
isOwned: boolean;
|
||||
isEditor: boolean;
|
||||
}
|
||||
|
||||
export function MenuRole({ isOwned, isEditor }: MenuRoleProps) {
|
||||
const { user, isAnonymous } = useAuthSuspense();
|
||||
const router = useConceptNavigation();
|
||||
const accessMenu = useDropdown();
|
||||
|
||||
const role = useRoleStore(state => state.role);
|
||||
const setRole = useRoleStore(state => state.setRole);
|
||||
|
||||
function handleChangeMode(newMode: UserRole) {
|
||||
accessMenu.hide();
|
||||
setRole(newMode);
|
||||
}
|
||||
|
||||
if (isAnonymous) {
|
||||
return (
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
titleHtml='<b>Анонимный режим</b><br />Войти в Портал'
|
||||
hideTitle={accessMenu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={<IconAlert size='1.25rem' className='icon-red' />}
|
||||
onClick={() => router.push(urls.login)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={accessMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title={`Режим ${labelUserRole(role)}`}
|
||||
hideTitle={accessMenu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={<IconRole role={role} size='1.25rem' />}
|
||||
onClick={accessMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={accessMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.READER)}
|
||||
title={describeUserRole(UserRole.READER)}
|
||||
icon={<IconRole role={UserRole.READER} size='1rem' />}
|
||||
onClick={() => handleChangeMode(UserRole.READER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.EDITOR)}
|
||||
title={describeUserRole(UserRole.EDITOR)}
|
||||
icon={<IconRole role={UserRole.EDITOR} size='1rem' />}
|
||||
disabled={!isOwned && !isEditor}
|
||||
onClick={() => handleChangeMode(UserRole.EDITOR)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.OWNER)}
|
||||
title={describeUserRole(UserRole.OWNER)}
|
||||
icon={<IconRole role={UserRole.OWNER} size='1rem' />}
|
||||
disabled={!isOwned}
|
||||
onClick={() => handleChangeMode(UserRole.OWNER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.ADMIN)}
|
||||
title={describeUserRole(UserRole.ADMIN)}
|
||||
icon={<IconRole role={UserRole.ADMIN} size='1rem' />}
|
||||
disabled={!user.is_staff}
|
||||
onClick={() => handleChangeMode(UserRole.ADMIN)}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -7,6 +7,7 @@ export { useUpdateItem } from './backend/useUpdateItem';
|
|||
export { useUpdateTimestamp } from './backend/useUpdateTimestamp';
|
||||
export { useVersionRestore } from './backend/useVersionRestore';
|
||||
export { EditorLibraryItem } from './components/EditorLibraryItem';
|
||||
export { MenuRole } from './components/MenuRole';
|
||||
export { MiniSelectorOSS } from './components/MiniSelectorOSS';
|
||||
export { PickSchema } from './components/PickSchema';
|
||||
export { SelectLibraryItem } from './components/SelectLibraryItem';
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import { useAuthSuspense } from '@/features/auth';
|
||||
|
||||
import { Button } from '@/components/Control';
|
||||
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
||||
import { IconChild, IconEdit2 } from '@/components/Icons';
|
||||
|
||||
import { useMutatingOss } from '../../backend/useMutatingOss';
|
||||
|
||||
import { useOssEdit } from './OssEditContext';
|
||||
|
||||
export function MenuEditOss() {
|
||||
const { isAnonymous } = useAuthSuspense();
|
||||
const editMenu = useDropdown();
|
||||
const { promptRelocateConstituents, isMutable } = useOssEdit();
|
||||
const isProcessing = useMutatingOss();
|
||||
|
||||
function handleRelocate() {
|
||||
editMenu.hide();
|
||||
promptRelocateConstituents(undefined, []);
|
||||
}
|
||||
|
||||
if (isAnonymous) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={editMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title='Редактирование'
|
||||
hideTitle={editMenu.isOpen}
|
||||
className='h-full px-2'
|
||||
icon={<IconEdit2 size='1.25rem' className={isMutable ? 'icon-green' : 'icon-red'} />}
|
||||
onClick={editMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={editMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text='Конституенты'
|
||||
titleHtml='Перенос конституент</br>между схемами'
|
||||
icon={<IconChild size='1rem' className='icon-green' />}
|
||||
disabled={isProcessing}
|
||||
onClick={handleRelocate}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
import { urls, useConceptNavigation } from '@/app';
|
||||
import { useAuthSuspense } from '@/features/auth';
|
||||
import { useRoleStore, UserRole } from '@/features/users';
|
||||
|
||||
import { Divider } from '@/components/Container';
|
||||
import { Button } from '@/components/Control';
|
||||
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
||||
import { IconDestroy, IconLibrary, IconMenu, IconNewItem, IconQR, IconShare } from '@/components/Icons';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { generatePageQR, sharePage } from '@/utils/utils';
|
||||
|
||||
import { useMutatingOss } from '../../backend/useMutatingOss';
|
||||
|
||||
import { useOssEdit } from './OssEditContext';
|
||||
|
||||
export function MenuMain() {
|
||||
const router = useConceptNavigation();
|
||||
const { isMutable, deleteSchema } = useOssEdit();
|
||||
const isProcessing = useMutatingOss();
|
||||
|
||||
const { isAnonymous } = useAuthSuspense();
|
||||
|
||||
const role = useRoleStore(state => state.role);
|
||||
|
||||
const showQR = useDialogsStore(state => state.showQR);
|
||||
|
||||
const schemaMenu = useDropdown();
|
||||
|
||||
function handleDelete() {
|
||||
schemaMenu.hide();
|
||||
deleteSchema();
|
||||
}
|
||||
|
||||
function handleShare() {
|
||||
schemaMenu.hide();
|
||||
sharePage();
|
||||
}
|
||||
|
||||
function handleCreateNew() {
|
||||
router.push(urls.create_schema);
|
||||
}
|
||||
|
||||
function handleShowQR() {
|
||||
schemaMenu.hide();
|
||||
showQR({ target: generatePageQR() });
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={schemaMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title='Меню'
|
||||
hideTitle={schemaMenu.isOpen}
|
||||
icon={<IconMenu size='1.25rem' className='clr-text-controls' />}
|
||||
className='h-full pl-2'
|
||||
onClick={schemaMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={schemaMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text='Поделиться'
|
||||
icon={<IconShare size='1rem' className='icon-primary' />}
|
||||
onClick={handleShare}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='QR-код'
|
||||
title='Показать QR-код схемы'
|
||||
icon={<IconQR size='1rem' className='icon-primary' />}
|
||||
onClick={handleShowQR}
|
||||
/>
|
||||
{isMutable ? (
|
||||
<DropdownButton
|
||||
text='Удалить схему'
|
||||
icon={<IconDestroy size='1rem' className='icon-red' />}
|
||||
disabled={isProcessing || role < UserRole.OWNER}
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Divider margins='mx-3 my-1' />
|
||||
|
||||
{!isAnonymous ? (
|
||||
<DropdownButton
|
||||
text='Создать новую схему'
|
||||
icon={<IconNewItem size='1rem' className='icon-primary' />}
|
||||
onClick={handleCreateNew}
|
||||
/>
|
||||
) : null}
|
||||
<DropdownButton
|
||||
text='Библиотека'
|
||||
icon={<IconLibrary size='1rem' className='icon-primary' />}
|
||||
onClick={() => router.push(urls.library)}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,213 +1,22 @@
|
|||
'use client';
|
||||
|
||||
import { urls, useConceptNavigation } from '@/app';
|
||||
import { useAuthSuspense } from '@/features/auth';
|
||||
import { useRoleStore, UserRole } from '@/features/users';
|
||||
import { describeUserRole, labelUserRole } from '@/features/users/labels';
|
||||
|
||||
import { Divider } from '@/components/Container';
|
||||
import { Button } from '@/components/Control';
|
||||
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
||||
import {
|
||||
IconAdmin,
|
||||
IconAlert,
|
||||
IconChild,
|
||||
IconDestroy,
|
||||
IconEdit2,
|
||||
IconEditor,
|
||||
IconLibrary,
|
||||
IconMenu,
|
||||
IconNewItem,
|
||||
IconOwner,
|
||||
IconReader,
|
||||
IconShare
|
||||
} from '@/components/Icons';
|
||||
import { sharePage } from '@/utils/utils';
|
||||
|
||||
import { useMutatingOss } from '../../backend/useMutatingOss';
|
||||
import { MenuRole } from '@/features/library';
|
||||
|
||||
import { MenuEditOss } from './MenuEditOss';
|
||||
import { MenuMain } from './MenuMain';
|
||||
import { useOssEdit } from './OssEditContext';
|
||||
|
||||
export function MenuOssTabs() {
|
||||
const { deleteSchema, promptRelocateConstituents, isMutable, isOwned, schema } = useOssEdit();
|
||||
const router = useConceptNavigation();
|
||||
const { user, isAnonymous } = useAuthSuspense();
|
||||
|
||||
const isProcessing = useMutatingOss();
|
||||
|
||||
const role = useRoleStore(state => state.role);
|
||||
const setRole = useRoleStore(state => state.setRole);
|
||||
|
||||
const schemaMenu = useDropdown();
|
||||
const editMenu = useDropdown();
|
||||
const accessMenu = useDropdown();
|
||||
|
||||
function handleDelete() {
|
||||
schemaMenu.hide();
|
||||
deleteSchema();
|
||||
}
|
||||
|
||||
function handleShare() {
|
||||
schemaMenu.hide();
|
||||
sharePage();
|
||||
}
|
||||
|
||||
function handleChangeRole(newMode: UserRole) {
|
||||
accessMenu.hide();
|
||||
setRole(newMode);
|
||||
}
|
||||
|
||||
function handleCreateNew() {
|
||||
router.push(urls.create_schema);
|
||||
}
|
||||
|
||||
function handleLogin() {
|
||||
router.push(urls.login);
|
||||
}
|
||||
|
||||
function handleRelocate() {
|
||||
editMenu.hide();
|
||||
promptRelocateConstituents(undefined, []);
|
||||
}
|
||||
|
||||
const { isOwned, schema } = useOssEdit();
|
||||
const { user } = useAuthSuspense();
|
||||
return (
|
||||
<div className='flex border-r-2'>
|
||||
<div ref={schemaMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title='Меню'
|
||||
hideTitle={schemaMenu.isOpen}
|
||||
icon={<IconMenu size='1.25rem' className='clr-text-controls' />}
|
||||
className='h-full pl-2'
|
||||
onClick={schemaMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={schemaMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text='Поделиться'
|
||||
icon={<IconShare size='1rem' className='icon-primary' />}
|
||||
onClick={handleShare}
|
||||
/>
|
||||
{isMutable ? (
|
||||
<DropdownButton
|
||||
text='Удалить схему'
|
||||
icon={<IconDestroy size='1rem' className='icon-red' />}
|
||||
disabled={isProcessing || role < UserRole.OWNER}
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
) : null}
|
||||
<MenuMain />
|
||||
|
||||
<Divider margins='mx-3 my-1' />
|
||||
<MenuEditOss />
|
||||
|
||||
{!isAnonymous ? (
|
||||
<DropdownButton
|
||||
text='Создать новую схему'
|
||||
icon={<IconNewItem size='1rem' className='icon-primary' />}
|
||||
onClick={handleCreateNew}
|
||||
/>
|
||||
) : null}
|
||||
<DropdownButton
|
||||
text='Библиотека'
|
||||
icon={<IconLibrary size='1rem' className='icon-primary' />}
|
||||
onClick={() => router.push(urls.library)}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
{!isAnonymous ? (
|
||||
<div ref={editMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title='Редактирование'
|
||||
hideTitle={editMenu.isOpen}
|
||||
className='h-full px-2'
|
||||
icon={<IconEdit2 size='1.25rem' className={isMutable ? 'icon-green' : 'icon-red'} />}
|
||||
onClick={editMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={editMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text='Конституенты'
|
||||
titleHtml='Перенос конституент</br>между схемами'
|
||||
icon={<IconChild size='1rem' className='icon-green' />}
|
||||
disabled={isProcessing}
|
||||
onClick={handleRelocate}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!isAnonymous ? (
|
||||
<div ref={accessMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title={`Режим ${labelUserRole(role)}`}
|
||||
hideTitle={accessMenu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={
|
||||
role === UserRole.ADMIN ? (
|
||||
<IconAdmin size='1.25rem' className='icon-primary' />
|
||||
) : role === UserRole.OWNER ? (
|
||||
<IconOwner size='1.25rem' className='icon-primary' />
|
||||
) : role === UserRole.EDITOR ? (
|
||||
<IconEditor size='1.25rem' className='icon-primary' />
|
||||
) : (
|
||||
<IconReader size='1.25rem' className='icon-primary' />
|
||||
)
|
||||
}
|
||||
onClick={accessMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={accessMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.READER)}
|
||||
title={describeUserRole(UserRole.READER)}
|
||||
icon={<IconReader size='1rem' className='icon-primary' />}
|
||||
onClick={() => handleChangeRole(UserRole.READER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.EDITOR)}
|
||||
title={describeUserRole(UserRole.EDITOR)}
|
||||
icon={<IconEditor size='1rem' className='icon-primary' />}
|
||||
disabled={!isOwned && (!user.id || !schema.editors.includes(user.id))}
|
||||
onClick={() => handleChangeRole(UserRole.EDITOR)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.OWNER)}
|
||||
title={describeUserRole(UserRole.OWNER)}
|
||||
icon={<IconOwner size='1rem' className='icon-primary' />}
|
||||
disabled={!isOwned}
|
||||
onClick={() => handleChangeRole(UserRole.OWNER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.ADMIN)}
|
||||
title={describeUserRole(UserRole.ADMIN)}
|
||||
icon={<IconAdmin size='1rem' className='icon-primary' />}
|
||||
disabled={!user.is_staff}
|
||||
onClick={() => handleChangeRole(UserRole.ADMIN)}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
) : null}
|
||||
{isAnonymous ? (
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
titleHtml='<b>Анонимный режим</b><br />Войти в Портал'
|
||||
hideTitle={accessMenu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={<IconAlert size='1.25rem' className='icon-red' />}
|
||||
onClick={handleLogin}
|
||||
/>
|
||||
) : null}
|
||||
<MenuRole isOwned={isOwned} isEditor={!!user.id && schema.editors.includes(user.id)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ export interface DlgShowQRProps {
|
|||
export function DlgShowQR() {
|
||||
const { target } = useDialogsStore(state => state.props as DlgShowQRProps);
|
||||
return (
|
||||
<ModalView className={clsx('w-[30rem]', 'py-12 pr-3 pl-6 flex gap-3 justify-center items-center')}>
|
||||
<ModalView className={clsx('w-[25rem]', 'pb-6 pt-12 flex justify-center items-center')}>
|
||||
<div className='bg-[#ffffff] p-4 border'>
|
||||
<QRCodeSVG value={target} size={256} />
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
import { urls, useConceptNavigation } from '@/app';
|
||||
import { useAuthSuspense } from '@/features/auth';
|
||||
|
||||
import { Divider } from '@/components/Container';
|
||||
import { Button } from '@/components/Control';
|
||||
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
||||
import {
|
||||
IconArchive,
|
||||
IconEdit2,
|
||||
IconGenerateNames,
|
||||
IconGenerateStructure,
|
||||
IconInlineSynthesis,
|
||||
IconReplace,
|
||||
IconSortList,
|
||||
IconTemplates
|
||||
} from '@/components/Icons';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { useModificationStore } from '@/stores/modification';
|
||||
import { promptUnsaved } from '@/utils/utils';
|
||||
|
||||
import { useMutatingRSForm } from '../../backend/useMutatingRSForm';
|
||||
import { useProduceStructure } from '../../backend/useProduceStructure';
|
||||
import { useResetAliases } from '../../backend/useResetAliases';
|
||||
import { useRestoreOrder } from '../../backend/useRestoreOrder';
|
||||
import { type IConstituenta } from '../../models/rsform';
|
||||
import { canProduceStructure } from '../../models/rsformAPI';
|
||||
|
||||
import { useRSEdit } from './RSEditContext';
|
||||
|
||||
export function MenuEditSchema() {
|
||||
const { isAnonymous } = useAuthSuspense();
|
||||
const { isModified } = useModificationStore();
|
||||
const router = useConceptNavigation();
|
||||
const editMenu = useDropdown();
|
||||
const { schema, activeCst, setSelected, isArchive, isContentEditable, promptTemplate, deselectAll } = useRSEdit();
|
||||
const isProcessing = useMutatingRSForm();
|
||||
|
||||
const { resetAliases } = useResetAliases();
|
||||
const { restoreOrder } = useRestoreOrder();
|
||||
const { produceStructure } = useProduceStructure();
|
||||
|
||||
const showInlineSynthesis = useDialogsStore(state => state.showInlineSynthesis);
|
||||
const showSubstituteCst = useDialogsStore(state => state.showSubstituteCst);
|
||||
|
||||
function handleReindex() {
|
||||
editMenu.hide();
|
||||
void resetAliases({ itemID: schema.id });
|
||||
}
|
||||
|
||||
function handleRestoreOrder() {
|
||||
editMenu.hide();
|
||||
void restoreOrder({ itemID: schema.id });
|
||||
}
|
||||
|
||||
function handleSubstituteCst() {
|
||||
editMenu.hide();
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
showSubstituteCst({
|
||||
schema: schema,
|
||||
onSubstitute: data => setSelected(prev => prev.filter(id => !data.substitutions.find(sub => sub.original === id)))
|
||||
});
|
||||
}
|
||||
|
||||
function handleTemplates() {
|
||||
editMenu.hide();
|
||||
promptTemplate();
|
||||
}
|
||||
|
||||
function handleProduceStructure(targetCst: IConstituenta | null) {
|
||||
editMenu.hide();
|
||||
if (!targetCst) {
|
||||
return;
|
||||
}
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
void produceStructure({
|
||||
itemID: schema.id,
|
||||
cstID: targetCst.id
|
||||
}).then(cstList => {
|
||||
if (cstList.length !== 0) {
|
||||
setSelected(cstList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleInlineSynthesis() {
|
||||
editMenu.hide();
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
showInlineSynthesis({
|
||||
receiver: schema,
|
||||
onSynthesis: () => deselectAll()
|
||||
});
|
||||
}
|
||||
|
||||
if (isAnonymous) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isArchive) {
|
||||
return (
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
titleHtml='<b>Архив</b>: Редактирование запрещено<br />Перейти к актуальной версии'
|
||||
hideTitle={editMenu.isOpen}
|
||||
className='h-full px-2'
|
||||
icon={<IconArchive size='1.25rem' className='icon-primary' />}
|
||||
onClick={event => router.push(urls.schema(schema.id), event.ctrlKey || event.metaKey)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={editMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title='Редактирование'
|
||||
hideTitle={editMenu.isOpen}
|
||||
className='h-full px-2'
|
||||
icon={<IconEdit2 size='1.25rem' className={isContentEditable ? 'icon-green' : 'icon-red'} />}
|
||||
onClick={editMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={editMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text='Шаблоны'
|
||||
title='Создать конституенту из шаблона'
|
||||
icon={<IconTemplates size='1rem' className='icon-green' />}
|
||||
disabled={!isContentEditable || isProcessing}
|
||||
onClick={handleTemplates}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='Встраивание'
|
||||
titleHtml='Импортировать совокупность <br/>конституент из другой схемы'
|
||||
icon={<IconInlineSynthesis size='1rem' className='icon-green' />}
|
||||
disabled={!isContentEditable || isProcessing}
|
||||
onClick={handleInlineSynthesis}
|
||||
/>
|
||||
|
||||
<Divider margins='mx-3 my-1' />
|
||||
|
||||
<DropdownButton
|
||||
text='Упорядочить список'
|
||||
titleHtml='Упорядочить список, исходя из <br/>логики типов и связей конституент'
|
||||
icon={<IconSortList size='1rem' className='icon-primary' />}
|
||||
disabled={!isContentEditable || isProcessing}
|
||||
onClick={handleRestoreOrder}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='Порядковые имена'
|
||||
titleHtml='Присвоить порядковые имена <br/>и обновить выражения'
|
||||
icon={<IconGenerateNames size='1rem' className='icon-primary' />}
|
||||
disabled={!isContentEditable || isProcessing}
|
||||
onClick={handleReindex}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='Порождение структуры'
|
||||
titleHtml='Раскрыть структуру типизации <br/>выделенной конституенты'
|
||||
icon={<IconGenerateStructure size='1rem' className='icon-primary' />}
|
||||
disabled={!isContentEditable || isProcessing || !activeCst || !canProduceStructure(activeCst)}
|
||||
onClick={() => handleProduceStructure(activeCst)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='Отождествление'
|
||||
titleHtml='Заменить вхождения <br/>одной конституенты на другую'
|
||||
icon={<IconReplace size='1rem' className='icon-red' />}
|
||||
onClick={handleSubstituteCst}
|
||||
disabled={!isContentEditable || isProcessing}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
import fileDownload from 'js-file-download';
|
||||
|
||||
import { urls, useConceptNavigation } from '@/app';
|
||||
import { useAuthSuspense } from '@/features/auth';
|
||||
import { AccessPolicy } from '@/features/library';
|
||||
import { LocationHead } from '@/features/library/models/library';
|
||||
import { useRoleStore, UserRole } from '@/features/users';
|
||||
|
||||
import { Divider } from '@/components/Container';
|
||||
import { Button } from '@/components/Control';
|
||||
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
||||
import {
|
||||
IconClone,
|
||||
IconDestroy,
|
||||
IconDownload,
|
||||
IconLibrary,
|
||||
IconMenu,
|
||||
IconNewItem,
|
||||
IconOSS,
|
||||
IconQR,
|
||||
IconShare,
|
||||
IconUpload
|
||||
} from '@/components/Icons';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { useModificationStore } from '@/stores/modification';
|
||||
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
||||
import { tooltipText } from '@/utils/labels';
|
||||
import { generatePageQR, promptUnsaved, sharePage } from '@/utils/utils';
|
||||
|
||||
import { useDownloadRSForm } from '../../backend/useDownloadRSForm';
|
||||
import { useMutatingRSForm } from '../../backend/useMutatingRSForm';
|
||||
|
||||
import { useRSEdit } from './RSEditContext';
|
||||
|
||||
export function MenuMain() {
|
||||
const router = useConceptNavigation();
|
||||
const { schema, selected, deleteSchema, isArchive, isMutable, isContentEditable } = useRSEdit();
|
||||
const isProcessing = useMutatingRSForm();
|
||||
|
||||
const { user, isAnonymous } = useAuthSuspense();
|
||||
|
||||
const role = useRoleStore(state => state.role);
|
||||
const { isModified } = useModificationStore();
|
||||
|
||||
const { download } = useDownloadRSForm();
|
||||
|
||||
const showQR = useDialogsStore(state => state.showQR);
|
||||
const showClone = useDialogsStore(state => state.showCloneLibraryItem);
|
||||
const showUpload = useDialogsStore(state => state.showUploadRSForm);
|
||||
|
||||
const schemaMenu = useDropdown();
|
||||
|
||||
function calculateCloneLocation() {
|
||||
const location = schema.location;
|
||||
const head = location.substring(0, 2) as LocationHead;
|
||||
if (head === LocationHead.LIBRARY) {
|
||||
return user.is_staff ? location : LocationHead.USER;
|
||||
}
|
||||
if (schema.owner === user.id) {
|
||||
return location;
|
||||
}
|
||||
return head === LocationHead.USER ? LocationHead.USER : location;
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
schemaMenu.hide();
|
||||
deleteSchema();
|
||||
}
|
||||
|
||||
function handleDownload() {
|
||||
schemaMenu.hide();
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
const fileName = (schema.alias ?? 'Schema') + EXTEOR_TRS_FILE;
|
||||
void download({
|
||||
itemID: schema.id,
|
||||
version: schema.version === 'latest' ? undefined : schema.version
|
||||
}).then((data: Blob) => {
|
||||
try {
|
||||
fileDownload(data, fileName);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleUpload() {
|
||||
schemaMenu.hide();
|
||||
showUpload({ itemID: schema.id });
|
||||
}
|
||||
|
||||
function handleClone() {
|
||||
schemaMenu.hide();
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
showClone({
|
||||
base: schema,
|
||||
initialLocation: calculateCloneLocation(),
|
||||
selected: selected,
|
||||
totalCount: schema.items.length
|
||||
});
|
||||
}
|
||||
|
||||
function handleShare() {
|
||||
schemaMenu.hide();
|
||||
sharePage();
|
||||
}
|
||||
|
||||
function handleShowQR() {
|
||||
schemaMenu.hide();
|
||||
showQR({ target: generatePageQR() });
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={schemaMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title='Меню'
|
||||
hideTitle={schemaMenu.isOpen}
|
||||
icon={<IconMenu size='1.25rem' className='clr-text-controls' />}
|
||||
className='h-full pl-2'
|
||||
onClick={schemaMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={schemaMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text='Поделиться'
|
||||
titleHtml={tooltipText.shareItem(schema.access_policy === AccessPolicy.PUBLIC)}
|
||||
icon={<IconShare size='1rem' className='icon-primary' />}
|
||||
onClick={handleShare}
|
||||
disabled={schema.access_policy !== AccessPolicy.PUBLIC}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='QR-код'
|
||||
title='Показать QR-код схемы'
|
||||
icon={<IconQR size='1rem' className='icon-primary' />}
|
||||
onClick={handleShowQR}
|
||||
/>
|
||||
{!isAnonymous ? (
|
||||
<DropdownButton
|
||||
text='Клонировать'
|
||||
icon={<IconClone size='1rem' className='icon-green' />}
|
||||
disabled={isArchive}
|
||||
onClick={handleClone}
|
||||
/>
|
||||
) : null}
|
||||
<DropdownButton
|
||||
text='Выгрузить в Экстеор'
|
||||
icon={<IconDownload size='1rem' className='icon-primary' />}
|
||||
onClick={handleDownload}
|
||||
/>
|
||||
{isContentEditable ? (
|
||||
<DropdownButton
|
||||
text='Загрузить из Экстеор'
|
||||
icon={<IconUpload size='1rem' className='icon-red' />}
|
||||
disabled={isProcessing || schema.oss.length !== 0}
|
||||
onClick={handleUpload}
|
||||
/>
|
||||
) : null}
|
||||
{isMutable ? (
|
||||
<DropdownButton
|
||||
text='Удалить схему'
|
||||
icon={<IconDestroy size='1rem' className='icon-red' />}
|
||||
disabled={isProcessing || role < UserRole.OWNER}
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Divider margins='mx-3 my-1' />
|
||||
|
||||
{!isAnonymous ? (
|
||||
<DropdownButton
|
||||
text='Создать новую схему'
|
||||
icon={<IconNewItem size='1rem' className='icon-primary' />}
|
||||
onClick={() => router.push(urls.create_schema)}
|
||||
/>
|
||||
) : null}
|
||||
{schema.oss.length > 0 ? (
|
||||
<DropdownButton
|
||||
text='Перейти к ОСС'
|
||||
icon={<IconOSS size='1rem' className='icon-primary' />}
|
||||
onClick={() => router.push(urls.oss(schema.oss[0].id))}
|
||||
/>
|
||||
) : null}
|
||||
<DropdownButton
|
||||
text='Библиотека'
|
||||
icon={<IconLibrary size='1rem' className='icon-primary' />}
|
||||
onClick={() => router.push(urls.library)}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,450 +1,21 @@
|
|||
'use client';
|
||||
|
||||
import fileDownload from 'js-file-download';
|
||||
|
||||
import { urls, useConceptNavigation } from '@/app';
|
||||
import { useAuthSuspense } from '@/features/auth';
|
||||
import { AccessPolicy } from '@/features/library';
|
||||
import { LocationHead } from '@/features/library/models/library';
|
||||
import { useRoleStore, UserRole } from '@/features/users';
|
||||
import { describeUserRole, labelUserRole } from '@/features/users/labels';
|
||||
|
||||
import { Divider } from '@/components/Container';
|
||||
import { Button } from '@/components/Control';
|
||||
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
||||
import {
|
||||
IconAdmin,
|
||||
IconAlert,
|
||||
IconArchive,
|
||||
IconClone,
|
||||
IconDestroy,
|
||||
IconDownload,
|
||||
IconEdit2,
|
||||
IconEditor,
|
||||
IconGenerateNames,
|
||||
IconGenerateStructure,
|
||||
IconInlineSynthesis,
|
||||
IconLibrary,
|
||||
IconMenu,
|
||||
IconNewItem,
|
||||
IconOSS,
|
||||
IconOwner,
|
||||
IconQR,
|
||||
IconReader,
|
||||
IconReplace,
|
||||
IconShare,
|
||||
IconSortList,
|
||||
IconTemplates,
|
||||
IconUpload
|
||||
} from '@/components/Icons';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { useModificationStore } from '@/stores/modification';
|
||||
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
||||
import { tooltipText } from '@/utils/labels';
|
||||
import { generatePageQR, promptUnsaved, sharePage } from '@/utils/utils';
|
||||
|
||||
import { useDownloadRSForm } from '../../backend/useDownloadRSForm';
|
||||
import { useMutatingRSForm } from '../../backend/useMutatingRSForm';
|
||||
import { useProduceStructure } from '../../backend/useProduceStructure';
|
||||
import { useResetAliases } from '../../backend/useResetAliases';
|
||||
import { useRestoreOrder } from '../../backend/useRestoreOrder';
|
||||
import { canProduceStructure } from '../../models/rsformAPI';
|
||||
import { MenuRole } from '@/features/library';
|
||||
|
||||
import { MenuEditSchema } from './MenuEditSchema';
|
||||
import { MenuMain } from './MenuMain';
|
||||
import { useRSEdit } from './RSEditContext';
|
||||
|
||||
export function MenuRSTabs() {
|
||||
const router = useConceptNavigation();
|
||||
const { user, isAnonymous } = useAuthSuspense();
|
||||
const {
|
||||
activeCst,
|
||||
schema,
|
||||
selected,
|
||||
setSelected,
|
||||
deleteSchema,
|
||||
promptTemplate,
|
||||
deselectAll,
|
||||
isArchive,
|
||||
isMutable,
|
||||
isContentEditable,
|
||||
isOwned
|
||||
} = useRSEdit();
|
||||
|
||||
const role = useRoleStore(state => state.role);
|
||||
const setRole = useRoleStore(state => state.setRole);
|
||||
const { isModified } = useModificationStore();
|
||||
const isProcessing = useMutatingRSForm();
|
||||
|
||||
const { resetAliases } = useResetAliases();
|
||||
const { restoreOrder } = useRestoreOrder();
|
||||
const { produceStructure } = useProduceStructure();
|
||||
const { download } = useDownloadRSForm();
|
||||
|
||||
const showInlineSynthesis = useDialogsStore(state => state.showInlineSynthesis);
|
||||
const showQR = useDialogsStore(state => state.showQR);
|
||||
const showSubstituteCst = useDialogsStore(state => state.showSubstituteCst);
|
||||
const showClone = useDialogsStore(state => state.showCloneLibraryItem);
|
||||
const showUpload = useDialogsStore(state => state.showUploadRSForm);
|
||||
|
||||
const schemaMenu = useDropdown();
|
||||
const editMenu = useDropdown();
|
||||
const accessMenu = useDropdown();
|
||||
|
||||
const structureEnabled = !!activeCst && canProduceStructure(activeCst);
|
||||
|
||||
function calculateCloneLocation() {
|
||||
const location = schema.location;
|
||||
const head = location.substring(0, 2) as LocationHead;
|
||||
if (head === LocationHead.LIBRARY) {
|
||||
return user.is_staff ? location : LocationHead.USER;
|
||||
}
|
||||
if (schema.owner === user.id) {
|
||||
return location;
|
||||
}
|
||||
return head === LocationHead.USER ? LocationHead.USER : location;
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
schemaMenu.hide();
|
||||
deleteSchema();
|
||||
}
|
||||
|
||||
function handleDownload() {
|
||||
schemaMenu.hide();
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
const fileName = (schema.alias ?? 'Schema') + EXTEOR_TRS_FILE;
|
||||
void download({
|
||||
itemID: schema.id,
|
||||
version: schema.version === 'latest' ? undefined : schema.version
|
||||
}).then((data: Blob) => {
|
||||
try {
|
||||
fileDownload(data, fileName);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleUpload() {
|
||||
schemaMenu.hide();
|
||||
showUpload({ itemID: schema.id });
|
||||
}
|
||||
|
||||
function handleClone() {
|
||||
schemaMenu.hide();
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
showClone({
|
||||
base: schema,
|
||||
initialLocation: calculateCloneLocation(),
|
||||
selected: selected,
|
||||
totalCount: schema.items.length
|
||||
});
|
||||
}
|
||||
|
||||
function handleShare() {
|
||||
schemaMenu.hide();
|
||||
sharePage();
|
||||
}
|
||||
|
||||
function handleShowQR() {
|
||||
schemaMenu.hide();
|
||||
showQR({ target: generatePageQR() });
|
||||
}
|
||||
|
||||
function handleReindex() {
|
||||
editMenu.hide();
|
||||
void resetAliases({ itemID: schema.id });
|
||||
}
|
||||
|
||||
function handleRestoreOrder() {
|
||||
editMenu.hide();
|
||||
void restoreOrder({ itemID: schema.id });
|
||||
}
|
||||
|
||||
function handleSubstituteCst() {
|
||||
editMenu.hide();
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
showSubstituteCst({
|
||||
schema: schema,
|
||||
onSubstitute: data => setSelected(prev => prev.filter(id => !data.substitutions.find(sub => sub.original === id)))
|
||||
});
|
||||
}
|
||||
|
||||
function handleTemplates() {
|
||||
editMenu.hide();
|
||||
promptTemplate();
|
||||
}
|
||||
|
||||
function handleProduceStructure() {
|
||||
editMenu.hide();
|
||||
if (!activeCst) {
|
||||
return;
|
||||
}
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
void produceStructure({
|
||||
itemID: schema.id,
|
||||
cstID: activeCst.id
|
||||
}).then(cstList => {
|
||||
if (cstList.length !== 0) {
|
||||
setSelected(cstList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleInlineSynthesis() {
|
||||
editMenu.hide();
|
||||
if (isModified && !promptUnsaved()) {
|
||||
return;
|
||||
}
|
||||
showInlineSynthesis({
|
||||
receiver: schema,
|
||||
onSynthesis: () => deselectAll()
|
||||
});
|
||||
}
|
||||
|
||||
function handleChangeMode(newMode: UserRole) {
|
||||
accessMenu.hide();
|
||||
setRole(newMode);
|
||||
}
|
||||
|
||||
function handleCreateNew() {
|
||||
router.push(urls.create_schema);
|
||||
}
|
||||
|
||||
function handleLogin() {
|
||||
router.push(urls.login);
|
||||
}
|
||||
const { user } = useAuthSuspense();
|
||||
const { schema, isOwned } = useRSEdit();
|
||||
|
||||
return (
|
||||
<div className='flex border-r-2'>
|
||||
<div ref={schemaMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title='Меню'
|
||||
hideTitle={schemaMenu.isOpen}
|
||||
icon={<IconMenu size='1.25rem' className='clr-text-controls' />}
|
||||
className='h-full pl-2'
|
||||
onClick={schemaMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={schemaMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text='Поделиться'
|
||||
titleHtml={tooltipText.shareItem(schema.access_policy === AccessPolicy.PUBLIC)}
|
||||
icon={<IconShare size='1rem' className='icon-primary' />}
|
||||
onClick={handleShare}
|
||||
disabled={schema.access_policy !== AccessPolicy.PUBLIC}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='QR-код'
|
||||
title='Показать QR-код схемы'
|
||||
icon={<IconQR size='1rem' className='icon-primary' />}
|
||||
onClick={handleShowQR}
|
||||
/>
|
||||
{!isAnonymous ? (
|
||||
<DropdownButton
|
||||
text='Клонировать'
|
||||
icon={<IconClone size='1rem' className='icon-green' />}
|
||||
disabled={isArchive}
|
||||
onClick={handleClone}
|
||||
/>
|
||||
) : null}
|
||||
<DropdownButton
|
||||
text='Выгрузить в Экстеор'
|
||||
icon={<IconDownload size='1rem' className='icon-primary' />}
|
||||
onClick={handleDownload}
|
||||
/>
|
||||
{isContentEditable ? (
|
||||
<DropdownButton
|
||||
text='Загрузить из Экстеор'
|
||||
icon={<IconUpload size='1rem' className='icon-red' />}
|
||||
disabled={isProcessing || schema.oss.length !== 0}
|
||||
onClick={handleUpload}
|
||||
/>
|
||||
) : null}
|
||||
{isMutable ? (
|
||||
<DropdownButton
|
||||
text='Удалить схему'
|
||||
icon={<IconDestroy size='1rem' className='icon-red' />}
|
||||
disabled={isProcessing || role < UserRole.OWNER}
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Divider margins='mx-3 my-1' />
|
||||
|
||||
{!isAnonymous ? (
|
||||
<DropdownButton
|
||||
text='Создать новую схему'
|
||||
icon={<IconNewItem size='1rem' className='icon-primary' />}
|
||||
onClick={handleCreateNew}
|
||||
/>
|
||||
) : null}
|
||||
{schema.oss.length > 0 ? (
|
||||
<DropdownButton
|
||||
text='Перейти к ОСС'
|
||||
icon={<IconOSS size='1rem' className='icon-primary' />}
|
||||
onClick={() => router.push(urls.oss(schema.oss[0].id))}
|
||||
/>
|
||||
) : null}
|
||||
<DropdownButton
|
||||
text='Библиотека'
|
||||
icon={<IconLibrary size='1rem' className='icon-primary' />}
|
||||
onClick={() => router.push(urls.library)}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
{!isArchive && !isAnonymous ? (
|
||||
<div ref={editMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title='Редактирование'
|
||||
hideTitle={editMenu.isOpen}
|
||||
className='h-full px-2'
|
||||
icon={<IconEdit2 size='1.25rem' className={isContentEditable ? 'icon-green' : 'icon-red'} />}
|
||||
onClick={editMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={editMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text='Шаблоны'
|
||||
title='Создать конституенту из шаблона'
|
||||
icon={<IconTemplates size='1rem' className='icon-green' />}
|
||||
disabled={!isContentEditable || isProcessing}
|
||||
onClick={handleTemplates}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='Встраивание'
|
||||
titleHtml='Импортировать совокупность <br/>конституент из другой схемы'
|
||||
icon={<IconInlineSynthesis size='1rem' className='icon-green' />}
|
||||
disabled={!isContentEditable || isProcessing}
|
||||
onClick={handleInlineSynthesis}
|
||||
/>
|
||||
|
||||
<Divider margins='mx-3 my-1' />
|
||||
|
||||
<DropdownButton
|
||||
text='Упорядочить список'
|
||||
titleHtml='Упорядочить список, исходя из <br/>логики типов и связей конституент'
|
||||
icon={<IconSortList size='1rem' className='icon-primary' />}
|
||||
disabled={!isContentEditable || isProcessing}
|
||||
onClick={handleRestoreOrder}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='Порядковые имена'
|
||||
titleHtml='Присвоить порядковые имена <br/>и обновить выражения'
|
||||
icon={<IconGenerateNames size='1rem' className='icon-primary' />}
|
||||
disabled={!isContentEditable || isProcessing}
|
||||
onClick={handleReindex}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='Порождение структуры'
|
||||
titleHtml='Раскрыть структуру типизации <br/>выделенной конституенты'
|
||||
icon={<IconGenerateStructure size='1rem' className='icon-primary' />}
|
||||
disabled={!isContentEditable || !structureEnabled || isProcessing}
|
||||
onClick={handleProduceStructure}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='Отождествление'
|
||||
titleHtml='Заменить вхождения <br/>одной конституенты на другую'
|
||||
icon={<IconReplace size='1rem' className='icon-red' />}
|
||||
onClick={handleSubstituteCst}
|
||||
disabled={!isContentEditable || isProcessing}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
) : null}
|
||||
{isArchive && !isAnonymous ? (
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
titleHtml='<b>Архив</b>: Редактирование запрещено<br />Перейти к актуальной версии'
|
||||
hideTitle={accessMenu.isOpen}
|
||||
className='h-full px-2'
|
||||
icon={<IconArchive size='1.25rem' className='icon-primary' />}
|
||||
onClick={event => router.push(urls.schema(schema.id), event.ctrlKey || event.metaKey)}
|
||||
/>
|
||||
) : null}
|
||||
{!isAnonymous ? (
|
||||
<div ref={accessMenu.ref}>
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title={`Режим ${labelUserRole(role)}`}
|
||||
hideTitle={accessMenu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={
|
||||
role === UserRole.ADMIN ? (
|
||||
<IconAdmin size='1.25rem' className='icon-primary' />
|
||||
) : role === UserRole.OWNER ? (
|
||||
<IconOwner size='1.25rem' className='icon-primary' />
|
||||
) : role === UserRole.EDITOR ? (
|
||||
<IconEditor size='1.25rem' className='icon-primary' />
|
||||
) : (
|
||||
<IconReader size='1.25rem' className='icon-primary' />
|
||||
)
|
||||
}
|
||||
onClick={accessMenu.toggle}
|
||||
/>
|
||||
<Dropdown isOpen={accessMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.READER)}
|
||||
title={describeUserRole(UserRole.READER)}
|
||||
icon={<IconReader size='1rem' className='icon-primary' />}
|
||||
onClick={() => handleChangeMode(UserRole.READER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.EDITOR)}
|
||||
title={describeUserRole(UserRole.EDITOR)}
|
||||
icon={<IconEditor size='1rem' className='icon-primary' />}
|
||||
disabled={!isOwned && (!user.id || !schema.editors.includes(user.id))}
|
||||
onClick={() => handleChangeMode(UserRole.EDITOR)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.OWNER)}
|
||||
title={describeUserRole(UserRole.OWNER)}
|
||||
icon={<IconOwner size='1rem' className='icon-primary' />}
|
||||
disabled={!isOwned}
|
||||
onClick={() => handleChangeMode(UserRole.OWNER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelUserRole(UserRole.ADMIN)}
|
||||
title={describeUserRole(UserRole.ADMIN)}
|
||||
icon={<IconAdmin size='1rem' className='icon-primary' />}
|
||||
disabled={!user.is_staff}
|
||||
onClick={() => handleChangeMode(UserRole.ADMIN)}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
) : null}
|
||||
{isAnonymous ? (
|
||||
<Button
|
||||
dense
|
||||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
titleHtml='<b>Анонимный режим</b><br />Войти в Портал'
|
||||
hideTitle={accessMenu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={<IconAlert size='1.25rem' className='icon-red' />}
|
||||
onClick={handleLogin}
|
||||
/>
|
||||
) : null}
|
||||
<MenuMain />
|
||||
<MenuEditSchema />
|
||||
<MenuRole isOwned={isOwned} isEditor={!!user.id && schema.editors.includes(user.id)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ const reactCompilerConfig = {
|
|||
};
|
||||
|
||||
// Packages to include in main app bundle
|
||||
const inlinePackages = ['react', 'react-router', 'react-dom'];
|
||||
const inlinePackages = ['react', 'react-router', 'react-dom', 'global', 'react-scan'];
|
||||
|
||||
// Rollup warnings that should not be displayed
|
||||
const warningsToIgnore = [
|
||||
|
|
Loading…
Reference in New Issue
Block a user