mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
UI improvements
This commit is contained in:
parent
f6c51a3c5f
commit
0fe2360886
|
@ -6,14 +6,15 @@ import Button from './Button';
|
|||
interface ModalProps {
|
||||
title?: string
|
||||
submitText?: string
|
||||
readonly?: boolean
|
||||
canSubmit?: boolean
|
||||
hideWindow: () => void
|
||||
onSubmit: () => void
|
||||
onSubmit?: () => void
|
||||
onCancel?: () => void
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function Modal({ title, hideWindow, onSubmit, onCancel, canSubmit, children, submitText = 'Продолжить' }: ModalProps) {
|
||||
function Modal({ title, hideWindow, onSubmit, readonly, onCancel, canSubmit, children, submitText = 'Продолжить' }: ModalProps) {
|
||||
const ref = useRef(null);
|
||||
useEscapeKey(hideWindow);
|
||||
|
||||
|
@ -24,29 +25,32 @@ function Modal({ title, hideWindow, onSubmit, onCancel, canSubmit, children, sub
|
|||
|
||||
const handleSubmit = () => {
|
||||
hideWindow();
|
||||
onSubmit();
|
||||
if (onSubmit) onSubmit();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='fixed top-0 left-0 z-50 w-full h-full opacity-50 clr-modal'>
|
||||
</div>
|
||||
<div ref={ref} className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 py-4 flex flex-col w-fit h-fit z-[60] clr-card border shadow-md mb-[5rem]'>
|
||||
<div
|
||||
ref={ref}
|
||||
className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 py-4 flex flex-col w-fit max-w-[95vw] h-fit z-[60] clr-card border shadow-md mb-[5rem]'
|
||||
>
|
||||
{ title && <h1 className='mb-2 text-xl font-bold text-center'>{title}</h1> }
|
||||
<div>
|
||||
<div className='max-h-[calc(95vh-15rem)] overflow-auto'>
|
||||
{children}
|
||||
</div>
|
||||
<div className='flex justify-between w-full pt-4 mt-2 border-t-4'>
|
||||
<Button
|
||||
<div className='flex justify-center w-full gap-4 pt-4 mt-2 border-t-4'>
|
||||
{!readonly && <Button
|
||||
text={submitText}
|
||||
widthClass='min-w-[6rem] min-h-[2.6rem] w-fit h-fit'
|
||||
colorClass='clr-btn-primary'
|
||||
disabled={!canSubmit}
|
||||
onClick={handleSubmit}
|
||||
autoFocus
|
||||
/>
|
||||
/>}
|
||||
<Button
|
||||
text='Отмена'
|
||||
text={readonly ? 'Закрыть' : 'Отмена'}
|
||||
widthClass='min-w-[6rem] min-h-[2.6rem] w-fit h-fit'
|
||||
onClick={handleCancel}
|
||||
/>
|
||||
|
|
|
@ -8,7 +8,7 @@ interface SubmitButtonProps {
|
|||
function SubmitButton({ text = 'ОК', icon, disabled, loading = false }: SubmitButtonProps) {
|
||||
return (
|
||||
<button type='submit'
|
||||
className={`px-4 py-2 inline-flex items-center gap-2 align-middle justify-center font-bold disabled:cursor-not-allowed rounded clr-btn-primary ${loading ? ' cursor-progress' : ''}`}
|
||||
className={`px-4 py-2 inline-flex items-center gap-2 align-middle justify-center font-bold disabled:cursor-not-allowed border rounded clr-btn-primary ${loading ? ' cursor-progress' : ''}`}
|
||||
disabled={disabled ?? loading}
|
||||
>
|
||||
{icon && <span>{icon}</span>}
|
||||
|
|
|
@ -28,7 +28,7 @@ function TextArea({
|
|||
htmlFor={id}
|
||||
/>
|
||||
<textarea id={id}
|
||||
className={'px-3 py-2 mt-2 leading-tight border shadow dark:bg-gray-800 ' + widthClass}
|
||||
className={`px-3 py-2 mt-2 leading-tight border shadow clr-input ${widthClass}`}
|
||||
rows={rows}
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
|
|
|
@ -21,7 +21,7 @@ function TextInput({
|
|||
htmlFor={id}
|
||||
/>
|
||||
<input id={id}
|
||||
className={'px-3 py-2 mt-2 leading-tight border shadow dark:bg-gray-800 truncate hover:text-clip ' + widthClass}
|
||||
className={`px-3 py-2 mt-2 leading-tight border shadow truncate hover:text-clip clr-input ${widthClass}`}
|
||||
required={required}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { useNavigate } from 'react-router-dom';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import { BellIcon, PlusIcon, SquaresIcon } from '../Icons';
|
||||
import NavigationButton from './NavigationButton';
|
||||
|
||||
function UserTools() {
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAuth();
|
||||
|
||||
const navigateCreateRSForm = () => { navigate('/rsform-create'); };
|
||||
const navigateMyWork = () => { navigate('/library?filter=personal'); };
|
||||
|
@ -24,7 +26,7 @@ function UserTools() {
|
|||
/>
|
||||
</span>
|
||||
<NavigationButton icon={<SquaresIcon />} description='Мои схемы' onClick={navigateMyWork} />
|
||||
<NavigationButton icon={<BellIcon />} description='Уведомления' onClick={handleNotifications} />
|
||||
{ user && user.is_staff && <NavigationButton icon={<BellIcon />} description='Уведомления' onClick={handleNotifications} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
|
||||
const toggleTracking = useCallback(
|
||||
() => {
|
||||
toast('not implemented yet')
|
||||
toast.info('Отслеживание в разработке...')
|
||||
}, [])
|
||||
|
||||
const update = useCallback(
|
||||
|
|
|
@ -51,6 +51,10 @@
|
|||
@apply border-gray-400 dark:border-gray-300 bg-white dark:bg-gray-700
|
||||
}
|
||||
|
||||
.clr-input {
|
||||
@apply dark:bg-black bg-white disabled:bg-[#f0f2f7] dark:disabled:bg-gray-700
|
||||
}
|
||||
|
||||
.clr-footer {
|
||||
@apply text-gray-600 bg-white border-gray-400 dark:bg-gray-700 dark:border-gray-300 dark:text-gray-300
|
||||
}
|
||||
|
@ -68,11 +72,11 @@
|
|||
}
|
||||
|
||||
.clr-btn-primary {
|
||||
@apply text-white bg-blue-400 hover:bg-blue-600 dark:bg-orange-600 dark:hover:bg-orange-400 disabled:bg-gray-400 dark:disabled:bg-gray-400
|
||||
@apply text-white bg-blue-400 hover:bg-blue-600 dark:bg-orange-600 dark:hover:bg-orange-400 disabled:bg-gray-400 dark:disabled:bg-gray-600
|
||||
}
|
||||
|
||||
.clr-btn-default {
|
||||
@apply text-gray-500 dark:text-zinc-200 dark:disabled:text-zinc-400 disabled:text-gray-400 bg-gray-100 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-400
|
||||
@apply text-gray-600 dark:text-zinc-200 dark:disabled:text-zinc-400 disabled:text-gray-400 bg-[#f0f2f7] hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-400
|
||||
}
|
||||
|
||||
/* Transparent button */
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useLayoutEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
|
@ -5,11 +6,14 @@ import { useAuth } from '../context/AuthContext';
|
|||
function HomePage() {
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAuth();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!user) {
|
||||
navigate('/library?filter=common');
|
||||
} else if(!user.is_staff) {
|
||||
navigate('/library?filter=personal');
|
||||
}
|
||||
}, [navigate, user])
|
||||
|
||||
return (
|
||||
<div className='flex flex-col items-center justify-center w-full py-2'>
|
||||
|
|
|
@ -16,10 +16,6 @@ interface DlgShowASTProps {
|
|||
function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||
const { darkMode } = useConceptTheme();
|
||||
|
||||
function handleSubmit() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
const nodes: GraphNode[] = useMemo(
|
||||
() => syntaxTree.map(node => {
|
||||
return {
|
||||
|
@ -46,14 +42,12 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
|||
return (
|
||||
<Modal
|
||||
hideWindow={hideWindow}
|
||||
onSubmit={handleSubmit}
|
||||
submitText='Закрыть'
|
||||
canSubmit={true}
|
||||
readonly
|
||||
>
|
||||
<div className='flex flex-col items-start gap-2'>
|
||||
<div className='w-full text-lg text-center'>{expression}</div>
|
||||
<div className='flex-wrap w-full h-full overflow-auto'>
|
||||
<div className='relative w-[1040px] h-[600px] 2xl:w-[1680px] 2xl:h-[600px]'>
|
||||
<div className='relative w-[1040px] h-[600px] 2xl:w-[1680px] 2xl:h-[600px] max-h-full max-w-full'>
|
||||
<GraphCanvas
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
|
|
|
@ -114,7 +114,7 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDe
|
|||
<div className='flex items-start justify-between'>
|
||||
<button type='submit'
|
||||
title='Сохранить изменения'
|
||||
className='px-1 py-1 font-bold rounded whitespace-nowrap disabled:cursor-not-allowed clr-btn-primary'
|
||||
className='px-1 py-1 font-bold border rounded whitespace-nowrap disabled:cursor-not-allowed clr-btn-primary'
|
||||
disabled={!isModified || !isEnabled}
|
||||
>
|
||||
<SaveIcon size={5} />
|
||||
|
@ -166,7 +166,7 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDe
|
|||
<p>- слева от ввода текста настраивается набор атрибутов конституенты</p>
|
||||
<p>- справа от ввода текста настраивается список конституент, которые фильтруются</p>
|
||||
<p>- текущая конституента выделена цветом строки</p>
|
||||
<p>- двойнок клин / Alt + клик - выбор редактируемой конституенты</p>
|
||||
<p>- двойной клик / Alt + клик - выбор редактируемой конституенты</p>
|
||||
<p>- при наведении на ID конституенты отображаются ее атрибуты</p>
|
||||
<p>- столбец "Описание" содержит один из непустых текстовых атрибутов</p>
|
||||
<Divider margins='mt-2' />
|
||||
|
|
|
@ -210,13 +210,22 @@ function EditorRSExpression({
|
|||
|
||||
return (
|
||||
<div className='flex flex-col items-start [&:not(:first-child)]:mt-3 w-full'>
|
||||
<div className='relative w-full'>
|
||||
<div className='absolute top-[-0.3rem] right-0'>
|
||||
<StatusBar
|
||||
isModified={isModified}
|
||||
constituenta={activeCst}
|
||||
parseData={parseData}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Label
|
||||
text={label}
|
||||
required={false}
|
||||
htmlFor={id}
|
||||
/>
|
||||
<textarea id={id} ref={expressionCtrl}
|
||||
className='w-full px-3 py-2 mt-2 leading-tight border shadow dark:bg-gray-800'
|
||||
className='w-full px-3 py-2 mt-2 leading-tight border shadow clr-input'
|
||||
rows={6}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
|
@ -228,23 +237,15 @@ function EditorRSExpression({
|
|||
/>
|
||||
<div className='flex w-full gap-4 py-1 mt-1 justify-stretch'>
|
||||
<div className='flex flex-col gap-2'>
|
||||
{isActive && <StatusBar
|
||||
isModified={isModified}
|
||||
constituenta={activeCst}
|
||||
parseData={parseData}
|
||||
/>}
|
||||
<Button
|
||||
tooltip='Проверить формальное выражение'
|
||||
text='Проверить'
|
||||
widthClass='h-full w-fit'
|
||||
colorClass='clr-btn-default'
|
||||
onClick={handleCheckExpression}
|
||||
/>
|
||||
</div>
|
||||
{isActive && EditButtons}
|
||||
{!isActive && <StatusBar
|
||||
isModified={isModified}
|
||||
constituenta={activeCst}
|
||||
parseData={parseData}
|
||||
/>}
|
||||
</div>
|
||||
{ (loading || parseData) &&
|
||||
<div className='w-full overflow-y-auto border mt-2 max-h-[14rem] min-h-[7rem]'>
|
||||
|
|
|
@ -35,7 +35,7 @@ function MatchModePicker({ value, onChange }: MatchModePickerProps) {
|
|||
<p><b>везде:</b> искать во всех атрибутах</p>
|
||||
</DropdownButton>
|
||||
<DropdownButton onClick={() => handleChange(CstMatchMode.EXPR)}>
|
||||
<p><b>ФВ:</b> искать в формальных выражениях</p>
|
||||
<p><b>выраж:</b> искать в формальных выражениях</p>
|
||||
</DropdownButton>
|
||||
<DropdownButton onClick={() => handleChange(CstMatchMode.TERM)}>
|
||||
<p><b>термин:</b> искать в терминах</p>
|
||||
|
|
|
@ -24,8 +24,8 @@ function StatusBar({ isModified, constituenta, parseData }: StatusBarProps) {
|
|||
const data = mapStatusInfo.get(status)!;
|
||||
return (
|
||||
<div title={data.tooltip}
|
||||
className={'min-h-[2rem] min-w-[6rem] font-semibold inline-flex border rounded-lg items-center justify-center align-middle ' + data.color}>
|
||||
{data.text}
|
||||
className={`text-sm h-[1.6rem] w-[10rem] font-semibold inline-flex border items-center justify-center align-middle ${data.color}`}>
|
||||
Статус: [ {data.text} ]
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,10 @@ import { defineConfig } from 'vite'
|
|||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 3000
|
||||
port: 3000,
|
||||
// https: {
|
||||
// key: fs.readFileSync('cert/portal-key.pem'),
|
||||
// cert: fs.readFileSync('cert/portal-cert.pem'),
|
||||
// }
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue
Block a user