mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Minor UI fixes
This commit is contained in:
parent
03fb9e3fd0
commit
b84bc82c02
|
@ -18,7 +18,7 @@ function HelpLibrary() {
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<EducationIcon size={4}/>
|
<EducationIcon size={4}/>
|
||||||
<p>Аттрибут <b>библиотечная</b> выделяет неизменяемые стандартные схемы.</p>
|
<p>Аттрибут <b>неизменная</b> выделяет неизменяемые стандартные схемы.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@ function HelpRSFormMeta() {
|
||||||
<h1>Концептуальная схема</h1>
|
<h1>Концептуальная схема</h1>
|
||||||
<p><b>Владелец</b> - пользователь, обладающий правом редактирования</p>
|
<p><b>Владелец</b> - пользователь, обладающий правом редактирования</p>
|
||||||
<p>Для <b>общедоступных</b> схем владельцем может стать любой пользователь</p>
|
<p>Для <b>общедоступных</b> схем владельцем может стать любой пользователь</p>
|
||||||
<p>Для <b>библиотечных</b> схем правом редактирования обладают только администраторы</p>
|
<p>Для <b>не</b> схем правом редактирования обладают только администраторы</p>
|
||||||
<p><b>Клонировать</b> - создать копию схемы для дальнейшего редактирования</p>
|
<p><b>Клонировать</b> - создать копию схемы для дальнейшего редактирования</p>
|
||||||
<p><b>Отслеживание</b> - возможность видеть схему в Библиотеке и использовать фильтры</p>
|
<p><b>Отслеживание</b> - возможность видеть схему в Библиотеке и использовать фильтры</p>
|
||||||
<p><b>Загрузить/Выгрузить схему</b> - взаимодействие с Экстеор через файлы формата TRS</p>
|
<p><b>Загрузить/Выгрузить схему</b> - взаимодействие с Экстеор через файлы формата TRS</p>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import Label from '../Common/Label';
|
||||||
import { ccBracketMatching } from './bracketMatching';
|
import { ccBracketMatching } from './bracketMatching';
|
||||||
import { RSLanguage } from './rslang';
|
import { RSLanguage } from './rslang';
|
||||||
import { getSymbolSubstitute,TextWrapper } from './textEditing';
|
import { getSymbolSubstitute,TextWrapper } from './textEditing';
|
||||||
import { rshoverTooltip } from './tooltip';
|
import { rshoverTooltip as rsHoverTooltip } from './tooltip';
|
||||||
|
|
||||||
const editorSetup: BasicSetupOptions = {
|
const editorSetup: BasicSetupOptions = {
|
||||||
highlightSpecialChars: false,
|
highlightSpecialChars: false,
|
||||||
|
@ -111,7 +111,7 @@ function RSInput({
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
RSLanguage,
|
RSLanguage,
|
||||||
ccBracketMatching(darkMode),
|
ccBracketMatching(darkMode),
|
||||||
rshoverTooltip(schema?.items || []),
|
rsHoverTooltip(schema?.items || []),
|
||||||
], [darkMode, schema?.items]);
|
], [darkMode, schema?.items]);
|
||||||
|
|
||||||
const handleInput = useCallback(
|
const handleInput = useCallback(
|
||||||
|
|
|
@ -6,10 +6,9 @@ import { getCstTypificationLabel } from '../../utils/staticUI';
|
||||||
|
|
||||||
function createTooltipFor(cst: IConstituenta) {
|
function createTooltipFor(cst: IConstituenta) {
|
||||||
const dom = document.createElement('div');
|
const dom = document.createElement('div');
|
||||||
dom.className = 'overflow-y-auto border shadow-md max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-20 text-sm';
|
dom.className = 'overflow-y-auto border shadow-md max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-20 text-sm clr-border px-2 py-2';
|
||||||
const alias = document.createElement('h1');
|
const alias = document.createElement('p');
|
||||||
alias.className = 'text-sm text-left';
|
alias.innerHTML = `<b>${cst.alias}:</b> ${getCstTypificationLabel(cst)}`;
|
||||||
alias.textContent = `${cst.alias}: ${getCstTypificationLabel(cst)}`;
|
|
||||||
dom.appendChild(alias);
|
dom.appendChild(alias);
|
||||||
if (cst.term_resolved) {
|
if (cst.term_resolved) {
|
||||||
const term = document.createElement('p');
|
const term = document.createElement('p');
|
||||||
|
|
|
@ -54,9 +54,9 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
<DropdownButton onClick={() => handleChange(LibraryFilterStrategy.CANONICAL)}>
|
<DropdownButton onClick={() => handleChange(LibraryFilterStrategy.CANONICAL)}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={value === LibraryFilterStrategy.CANONICAL}
|
value={value === LibraryFilterStrategy.CANONICAL}
|
||||||
label='Библиотечные'
|
label='Неизменные'
|
||||||
widthClass='w-fit px-2'
|
widthClass='w-fit px-2'
|
||||||
tooltip='Отображать только библиотечные схемы'
|
tooltip='Отображать только неизменные схемы'
|
||||||
/>
|
/>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
<DropdownButton onClick={() => handleChange(LibraryFilterStrategy.PERSONAL)}>
|
<DropdownButton onClick={() => handleChange(LibraryFilterStrategy.PERSONAL)}>
|
||||||
|
|
|
@ -64,7 +64,7 @@ function SearchPanel({ total, filtered, setFilter }: SearchPanelProps) {
|
||||||
}, [strategy, navigate]);
|
}, [strategy, navigate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='sticky top-0 left-0 right-0 z-10 flex items-center justify-start w-full border-b clr-input'>
|
<div className='sticky top-0 left-0 right-0 z-30 flex items-center justify-start w-full border-b clr-input'>
|
||||||
<div className='px-2 py-1 select-none whitespace-nowrap min-w-[10rem]'>
|
<div className='px-2 py-1 select-none whitespace-nowrap min-w-[10rem]'>
|
||||||
Фильтр
|
Фильтр
|
||||||
<span className='ml-2'>
|
<span className='ml-2'>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useLayoutEffect, useMemo, useState } from 'react';
|
import { Dispatch, SetStateAction, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||||
|
@ -9,7 +9,6 @@ import TextArea from '../../components/Common/TextArea';
|
||||||
import HelpConstituenta from '../../components/Help/HelpConstituenta';
|
import HelpConstituenta from '../../components/Help/HelpConstituenta';
|
||||||
import { DumpBinIcon, HelpIcon, PenIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
import { DumpBinIcon, HelpIcon, PenIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import useModificationPrompt from '../../hooks/useModificationPrompt';
|
|
||||||
import { CstType, EditMode, ICstCreateData, ICstRenameData, ICstUpdateData, SyntaxTree } from '../../utils/models';
|
import { CstType, EditMode, ICstCreateData, ICstRenameData, ICstUpdateData, SyntaxTree } from '../../utils/models';
|
||||||
import { getCstTypificationLabel } from '../../utils/staticUI';
|
import { getCstTypificationLabel } from '../../utils/staticUI';
|
||||||
import EditorRSExpression from './EditorRSExpression';
|
import EditorRSExpression from './EditorRSExpression';
|
||||||
|
@ -25,17 +24,20 @@ interface EditorConstituentaProps {
|
||||||
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
|
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
|
||||||
onRenameCst: (initial: ICstRenameData) => void
|
onRenameCst: (initial: ICstRenameData) => void
|
||||||
onDeleteCst: (selected: number[], callback?: (items: number[]) => void) => void
|
onDeleteCst: (selected: number[], callback?: (items: number[]) => void) => void
|
||||||
|
isModified: boolean
|
||||||
|
setIsModified: Dispatch<SetStateAction<boolean>>
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onOpenEdit, onDeleteCst }: EditorConstituentaProps) {
|
function EditorConstituenta({
|
||||||
|
isModified, setIsModified, activeID,
|
||||||
|
onShowAST, onCreateCst, onRenameCst, onOpenEdit, onDeleteCst
|
||||||
|
}: EditorConstituentaProps) {
|
||||||
const { schema, processing, isEditable, cstUpdate } = useRSForm();
|
const { schema, processing, isEditable, cstUpdate } = useRSForm();
|
||||||
const activeCst = useMemo(
|
const activeCst = useMemo(
|
||||||
() => {
|
() => {
|
||||||
return schema?.items?.find((cst) => cst.id === activeID);
|
return schema?.items?.find((cst) => cst.id === activeID);
|
||||||
}, [schema?.items, activeID]);
|
}, [schema?.items, activeID]);
|
||||||
|
|
||||||
const { isModified, setIsModified } = useModificationPrompt();
|
|
||||||
|
|
||||||
const [editMode, setEditMode] = useState(EditMode.TEXT);
|
const [editMode, setEditMode] = useState(EditMode.TEXT);
|
||||||
|
|
||||||
const [alias, setAlias] = useState('');
|
const [alias, setAlias] = useState('');
|
||||||
|
@ -59,6 +61,7 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
||||||
activeCst.convention !== convention ||
|
activeCst.convention !== convention ||
|
||||||
activeCst.definition_formal !== expression
|
activeCst.definition_formal !== expression
|
||||||
);
|
);
|
||||||
|
return () => setIsModified(false);
|
||||||
}, [activeCst, activeCst?.term_raw, activeCst?.definition_formal,
|
}, [activeCst, activeCst?.term_raw, activeCst?.definition_formal,
|
||||||
activeCst?.definition_raw, activeCst?.convention,
|
activeCst?.definition_raw, activeCst?.convention,
|
||||||
term, textDefinition, expression, convention, setIsModified]);
|
term, textDefinition, expression, convention, setIsModified]);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { Dispatch, SetStateAction, useLayoutEffect, useState } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import { CrownIcon, DownloadIcon, DumpBinIcon, HelpIcon, SaveIcon, ShareIcon } f
|
||||||
import { useAuth } from '../../context/AuthContext';
|
import { useAuth } from '../../context/AuthContext';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { useUsers } from '../../context/UsersContext';
|
import { useUsers } from '../../context/UsersContext';
|
||||||
import useModificationPrompt from '../../hooks/useModificationPrompt';
|
|
||||||
import { IRSFormCreateData, LibraryItemType } from '../../utils/models';
|
import { IRSFormCreateData, LibraryItemType } from '../../utils/models';
|
||||||
|
|
||||||
interface EditorRSFormProps {
|
interface EditorRSFormProps {
|
||||||
|
@ -21,9 +20,11 @@ interface EditorRSFormProps {
|
||||||
onClaim: () => void
|
onClaim: () => void
|
||||||
onShare: () => void
|
onShare: () => void
|
||||||
onDownload: () => void
|
onDownload: () => void
|
||||||
|
isModified: boolean
|
||||||
|
setIsModified: Dispatch<SetStateAction<boolean>>
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorRSForm({ onDestroy, onClaim, onShare, onDownload }: EditorRSFormProps) {
|
function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified, onDownload }: EditorRSFormProps) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { getUserLabel } = useUsers();
|
const { getUserLabel } = useUsers();
|
||||||
const {
|
const {
|
||||||
|
@ -38,8 +39,6 @@ function EditorRSForm({ onDestroy, onClaim, onShare, onDownload }: EditorRSFormP
|
||||||
const [common, setCommon] = useState(false);
|
const [common, setCommon] = useState(false);
|
||||||
const [canonical, setCanonical] = useState(false);
|
const [canonical, setCanonical] = useState(false);
|
||||||
|
|
||||||
const { isModified, setIsModified } = useModificationPrompt();
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
|
@ -52,6 +51,7 @@ function EditorRSForm({ onDestroy, onClaim, onShare, onDownload }: EditorRSFormP
|
||||||
schema.is_common !== common ||
|
schema.is_common !== common ||
|
||||||
schema.is_canonical !== canonical
|
schema.is_canonical !== canonical
|
||||||
);
|
);
|
||||||
|
return () => setIsModified(false);
|
||||||
}, [schema, schema?.title, schema?.alias, schema?.comment,
|
}, [schema, schema?.title, schema?.alias, schema?.comment,
|
||||||
schema?.is_common, schema?.is_canonical,
|
schema?.is_common, schema?.is_canonical,
|
||||||
title, alias, comment, common, canonical, setIsModified]);
|
title, alias, comment, common, canonical, setIsModified]);
|
||||||
|
@ -138,10 +138,10 @@ function EditorRSForm({ onDestroy, onClaim, onShare, onDownload }: EditorRSFormP
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
onChange={event => setCommon(event.target.checked)}
|
onChange={event => setCommon(event.target.checked)}
|
||||||
/>
|
/>
|
||||||
<Checkbox id='canonical' label='Библиотечная схема'
|
<Checkbox id='canonical' label='Неизменная схема'
|
||||||
widthClass='w-fit'
|
widthClass='w-fit'
|
||||||
value={canonical}
|
value={canonical}
|
||||||
tooltip='Только администраторы могут присваивать схемам библиотечный статус'
|
tooltip='Только администраторы могут присваивать схемам неизменный статус'
|
||||||
disabled={!isEditable || !isForceAdmin}
|
disabled={!isEditable || !isForceAdmin}
|
||||||
onChange={event => setCanonical(event.target.checked)}
|
onChange={event => setCanonical(event.target.checked)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { Loader } from '../../components/Common/Loader';
|
||||||
import { useLibrary } from '../../context/LibraryContext';
|
import { useLibrary } from '../../context/LibraryContext';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
|
import useModificationPrompt from '../../hooks/useModificationPrompt';
|
||||||
import { prefixes, TIMEOUT_UI_REFRESH } from '../../utils/constants';
|
import { prefixes, TIMEOUT_UI_REFRESH } from '../../utils/constants';
|
||||||
import { ICstCreateData, ICstRenameData, LibraryFilterStrategy, SyntaxTree } from '../../utils/models';
|
import { ICstCreateData, ICstRenameData, LibraryFilterStrategy, SyntaxTree } from '../../utils/models';
|
||||||
import { createAliasFor } from '../../utils/staticUI';
|
import { createAliasFor } from '../../utils/staticUI';
|
||||||
|
@ -43,6 +44,8 @@ function RSTabs() {
|
||||||
const { destroySchema } = useLibrary();
|
const { destroySchema } = useLibrary();
|
||||||
const { setNoFooter } = useConceptTheme();
|
const { setNoFooter } = useConceptTheme();
|
||||||
|
|
||||||
|
const { isModified, setIsModified } = useModificationPrompt();
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<RSTabID>(RSTabID.CARD);
|
const [activeTab, setActiveTab] = useState<RSTabID>(RSTabID.CARD);
|
||||||
const [activeID, setActiveID] = useState<number | undefined>(undefined);
|
const [activeID, setActiveID] = useState<number | undefined>(undefined);
|
||||||
|
|
||||||
|
@ -226,6 +229,11 @@ function RSTabs() {
|
||||||
|
|
||||||
const onDownloadSchema = useCallback(
|
const onDownloadSchema = useCallback(
|
||||||
() => {
|
() => {
|
||||||
|
if (isModified) {
|
||||||
|
if (!window.confirm('Присутствуют несохраненные изменения. Продолжить без их учета?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
const fileName = (schema?.alias ?? 'Schema') + '.trs';
|
const fileName = (schema?.alias ?? 'Schema') + '.trs';
|
||||||
download(
|
download(
|
||||||
(data) => {
|
(data) => {
|
||||||
|
@ -235,7 +243,17 @@ function RSTabs() {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [schema?.alias, download]);
|
}, [schema?.alias, download, isModified]);
|
||||||
|
|
||||||
|
const handleShowClone = useCallback(
|
||||||
|
() => {
|
||||||
|
if (isModified) {
|
||||||
|
if (!window.confirm('Присутствуют несохраненные изменения. Продолжить без их учета?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setShowClone(true);
|
||||||
|
}, [isModified]);
|
||||||
|
|
||||||
const handleToggleSubscribe = useCallback(
|
const handleToggleSubscribe = useCallback(
|
||||||
() => {
|
() => {
|
||||||
|
@ -302,7 +320,7 @@ function RSTabs() {
|
||||||
onClaim={onClaimSchema}
|
onClaim={onClaimSchema}
|
||||||
onShare={onShareSchema}
|
onShare={onShareSchema}
|
||||||
onToggleSubscribe={handleToggleSubscribe}
|
onToggleSubscribe={handleToggleSubscribe}
|
||||||
showCloneDialog={() => setShowClone(true)}
|
showCloneDialog={handleShowClone}
|
||||||
showUploadDialog={() => setShowUpload(true)}
|
showUploadDialog={() => setShowUpload(true)}
|
||||||
/>
|
/>
|
||||||
<ConceptTab className='border-r-2 min-w-[7.8rem]'>Паспорт схемы</ConceptTab>
|
<ConceptTab className='border-r-2 min-w-[7.8rem]'>Паспорт схемы</ConceptTab>
|
||||||
|
@ -316,6 +334,8 @@ function RSTabs() {
|
||||||
|
|
||||||
<TabPanel className='flex w-full gap-4'>
|
<TabPanel className='flex w-full gap-4'>
|
||||||
<EditorRSForm
|
<EditorRSForm
|
||||||
|
isModified={isModified}
|
||||||
|
setIsModified={setIsModified}
|
||||||
onDownload={onDownloadSchema}
|
onDownload={onDownloadSchema}
|
||||||
onDestroy={onDestroySchema}
|
onDestroy={onDestroySchema}
|
||||||
onClaim={onClaimSchema}
|
onClaim={onClaimSchema}
|
||||||
|
@ -334,6 +354,8 @@ function RSTabs() {
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<EditorConstituenta
|
<EditorConstituenta
|
||||||
|
isModified={isModified}
|
||||||
|
setIsModified={setIsModified}
|
||||||
activeID={activeID}
|
activeID={activeID}
|
||||||
onOpenEdit={onOpenCst}
|
onOpenEdit={onOpenCst}
|
||||||
onShowAST={onShowAST}
|
onShowAST={onShowAST}
|
||||||
|
|
|
@ -131,7 +131,7 @@ function RSTabsMenu({
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!user || !isClaimable}
|
disabled={!user || !isClaimable}
|
||||||
onClick={!isOwned ? handleClaimOwner : undefined}
|
onClick={!isOwned ? handleClaimOwner : undefined}
|
||||||
description={!user || !isClaimable ? 'Стать владельцем можно только для общей небиблиотечной схемы' : ''}
|
description={!user || !isClaimable ? 'Стать владельцем можно только для общей изменяемой схемы' : ''}
|
||||||
>
|
>
|
||||||
<div className='inline-flex items-center gap-1 justify-normal'>
|
<div className='inline-flex items-center gap-1 justify-normal'>
|
||||||
<span className={isOwned ? 'text-green' : ''}><CrownIcon size={4} /></span>
|
<span className={isOwned ? 'text-green' : ''}><CrownIcon size={4} /></span>
|
||||||
|
|
|
@ -624,6 +624,7 @@ export function getNodeLabel(node: ISyntaxTreeNode): string {
|
||||||
case TokenID.NT_ENUM_DECL: return 'ENUM_DECLARATION'
|
case TokenID.NT_ENUM_DECL: return 'ENUM_DECLARATION'
|
||||||
case TokenID.NT_TUPLE_DECL: return 'TUPLE_DECLARATION'
|
case TokenID.NT_TUPLE_DECL: return 'TUPLE_DECLARATION'
|
||||||
case TokenID.PUNC_DEFINE: return 'DEFINITION'
|
case TokenID.PUNC_DEFINE: return 'DEFINITION'
|
||||||
|
case TokenID.PUNC_STRUCT: return 'STRUCTURE_DEFITION'
|
||||||
|
|
||||||
case TokenID.NT_ARG_DECL: return 'ARG'
|
case TokenID.NT_ARG_DECL: return 'ARG'
|
||||||
case TokenID.NT_FUNC_CALL: return 'CALL'
|
case TokenID.NT_FUNC_CALL: return 'CALL'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user