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
e8ec015633
commit
35754a59c7
|
@ -6,8 +6,8 @@ interface DropdownProps {
|
|||
|
||||
function Dropdown({ children, widthClass = 'w-fit', stretchLeft }: DropdownProps) {
|
||||
return (
|
||||
<div className='relative'>
|
||||
<div className={`absolute ${stretchLeft ? 'right-0' : 'left-0'} py-2 z-40 flex flex-col items-stretch justify-start px-2 mt-2 text-sm origin-top-right bg-white border border-gray-100 divide-y rounded-md shadow-lg dark:border-gray-500 dark:bg-gray-900 ${widthClass}`}>
|
||||
<div className='relative text-sm'>
|
||||
<div className={`absolute ${stretchLeft ? 'right-0' : 'left-0'} mt-2 z-40 flex flex-col items-stretch justify-start origin-top-right border divide-y rounded-md shadow-lg clr-input clr-border ${widthClass}`}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,8 +10,8 @@ function Navigation () {
|
|||
const navigate = useNavigate();
|
||||
const { noNavigation, toggleNoNavigation } = useConceptTheme();
|
||||
|
||||
const navigateLibrary = () => { navigate('/library') };
|
||||
const navigateHelp = () => { navigate('/manuals') };
|
||||
const navigateLibrary = () => navigate('/library');
|
||||
const navigateHelp = () => navigate('/manuals');
|
||||
|
||||
return (
|
||||
<nav className='sticky top-0 left-0 right-0 z-50 select-none h-fit'>
|
||||
|
|
|
@ -16,14 +16,14 @@ function UserDropdown({ hideDropdown }: UserDropdownProps) {
|
|||
const { user, logout } = useAuth();
|
||||
|
||||
const navigateProfile = () => {
|
||||
hideDropdown()
|
||||
hideDropdown();
|
||||
navigate('/profile');
|
||||
};
|
||||
|
||||
const logoutAndRedirect =
|
||||
() => {
|
||||
hideDropdown();
|
||||
logout(() => { navigate('/login/'); })
|
||||
logout(() => navigate('/login/'));
|
||||
};
|
||||
|
||||
const navigateMyWork = () => {
|
||||
|
@ -33,10 +33,16 @@ function UserDropdown({ hideDropdown }: UserDropdownProps) {
|
|||
|
||||
return (
|
||||
<Dropdown widthClass='w-36' stretchLeft>
|
||||
<DropdownButton description='Профиль пользователя' onClick={navigateProfile}>
|
||||
<DropdownButton
|
||||
description='Профиль пользователя'
|
||||
onClick={navigateProfile}
|
||||
>
|
||||
{user?.username}
|
||||
</DropdownButton>
|
||||
<DropdownButton description='Переключение темы оформления' onClick={toggleDarkMode}>
|
||||
<DropdownButton
|
||||
description='Переключение темы оформления'
|
||||
onClick={toggleDarkMode}
|
||||
>
|
||||
{darkMode ? 'Светлая тема' : 'Темная тема'}
|
||||
</DropdownButton>
|
||||
<DropdownButton onClick={navigateMyWork}>
|
||||
|
|
|
@ -30,7 +30,7 @@ function UserMenu() {
|
|||
</div>
|
||||
{ user && menu.isActive &&
|
||||
<UserDropdown
|
||||
hideDropdown={() => { menu.hide(); }}
|
||||
hideDropdown={() => menu.hide()}
|
||||
/>}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -283,9 +283,9 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => setError(error),
|
||||
onSuccess: newData => {
|
||||
reload(setProcessing, () => { if (callback) callback(newData); })
|
||||
}
|
||||
onSuccess: newData => reload(setProcessing, () => {
|
||||
if (callback) callback(newData);
|
||||
})
|
||||
});
|
||||
}, [setError, reload]);
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ function useCheckExpression({ schema }: { schema?: IRSForm }) {
|
|||
const [error, setError] = useState<ErrorInfo>(undefined);
|
||||
const [parseData, setParseData] = useState<IExpressionParse | undefined>(undefined);
|
||||
|
||||
const resetParse = useCallback(() => { setParseData(undefined); }, []);
|
||||
const resetParse = useCallback(() => setParseData(undefined), []);
|
||||
|
||||
function checkExpression(expression: string, activeCst?: IConstituenta, onSuccess?: DataCallback<IExpressionParse>) {
|
||||
setError(undefined);
|
||||
|
|
|
@ -6,14 +6,14 @@ function useDropdown() {
|
|||
const [isActive, setIsActive] = useState(false);
|
||||
const ref = useRef(null);
|
||||
|
||||
useClickedOutside({ ref, callback: () => { setIsActive(false); } })
|
||||
useClickedOutside({ ref, callback: () => setIsActive(false) })
|
||||
|
||||
return {
|
||||
ref,
|
||||
isActive,
|
||||
setIsActive,
|
||||
toggle: () => { setIsActive(!isActive); },
|
||||
hide: () => { setIsActive(false); }
|
||||
toggle: () => setIsActive(!isActive),
|
||||
hide: () => setIsActive(false)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,10 @@ export function useRSFormDetails({ target }: { target?: string }) {
|
|||
getRSFormDetails(target, {
|
||||
showError: true,
|
||||
setLoading: setCustomLoading ?? setLoading,
|
||||
onError: error => { setInnerSchema(undefined); setError(error); },
|
||||
onError: error => {
|
||||
setInnerSchema(undefined);
|
||||
setError(error);
|
||||
},
|
||||
onSuccess: schema => {
|
||||
setSchema(schema);
|
||||
if (callback) callback();
|
||||
|
|
|
@ -9,7 +9,7 @@ function useResolveText({ schema }: { schema?: IRSForm }) {
|
|||
const [error, setError] = useState<ErrorInfo>(undefined);
|
||||
const [refsData, setRefsData] = useState<IReferenceData | undefined>(undefined);
|
||||
|
||||
const resetData = useCallback(() => { setRefsData(undefined); }, []);
|
||||
const resetData = useCallback(() => setRefsData(undefined), []);
|
||||
|
||||
function resolveText(text: string, onSuccess?: DataCallback<IReferenceData>) {
|
||||
setError(undefined);
|
||||
|
@ -17,7 +17,7 @@ function useResolveText({ schema }: { schema?: IRSForm }) {
|
|||
data: { text: text },
|
||||
showError: true,
|
||||
setLoading,
|
||||
onError: error => { setError(error); },
|
||||
onError: error => setError(error),
|
||||
onSuccess: data => {
|
||||
setRefsData(data);
|
||||
if (onSuccess) onSuccess(data);
|
||||
|
|
|
@ -38,12 +38,14 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
|||
<Checkbox
|
||||
value={value === LibraryFilterStrategy.MANUAL}
|
||||
label='Отображать все'
|
||||
widthClass='w-fit px-2'
|
||||
/>
|
||||
</DropdownButton>
|
||||
<DropdownButton onClick={() => handleChange(LibraryFilterStrategy.COMMON)}>
|
||||
<Checkbox
|
||||
value={value === LibraryFilterStrategy.COMMON}
|
||||
label='Общедоступные'
|
||||
widthClass='w-fit px-2'
|
||||
tooltip='Отображать только общедоступные схемы'
|
||||
/>
|
||||
</DropdownButton>
|
||||
|
@ -51,6 +53,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
|||
<Checkbox
|
||||
value={value === LibraryFilterStrategy.CANONICAL}
|
||||
label='Библиотечные'
|
||||
widthClass='w-fit px-2'
|
||||
tooltip='Отображать только библиотечные схемы'
|
||||
/>
|
||||
</DropdownButton>
|
||||
|
@ -58,6 +61,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
|||
<Checkbox
|
||||
value={value === LibraryFilterStrategy.PERSONAL}
|
||||
label='Личные'
|
||||
widthClass='w-fit px-2'
|
||||
tooltip='Отображать только подписки и владеемые схемы'
|
||||
/>
|
||||
</DropdownButton>
|
||||
|
@ -65,6 +69,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
|||
<Checkbox
|
||||
value={value === LibraryFilterStrategy.SUBSCRIBE}
|
||||
label='Подписки'
|
||||
widthClass='w-fit px-2'
|
||||
tooltip='Отображать только подписки'
|
||||
/>
|
||||
</DropdownButton>
|
||||
|
@ -72,6 +77,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
|||
<Checkbox
|
||||
value={value === LibraryFilterStrategy.OWNED}
|
||||
label='Я - Владелец!'
|
||||
widthClass='w-fit px-2'
|
||||
tooltip='Отображать только владеемые схемы'
|
||||
/>
|
||||
</DropdownButton>
|
||||
|
|
|
@ -34,7 +34,7 @@ function LoginPage() {
|
|||
username: username,
|
||||
password: password
|
||||
};
|
||||
login(data, () => { navigate('/library?filter=personal'); });
|
||||
login(data, () => navigate('/library'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,14 +54,14 @@ function LoginPage() {
|
|||
type='text'
|
||||
value={username}
|
||||
autoFocus
|
||||
onChange={event => { setUsername(event.target.value); }}
|
||||
onChange={event => setUsername(event.target.value)}
|
||||
/>
|
||||
<TextInput id='password'
|
||||
label='Пароль'
|
||||
required
|
||||
type='password'
|
||||
value={password}
|
||||
onChange={event => { setPassword(event.target.value); }}
|
||||
onChange={event => setPassword(event.target.value)}
|
||||
/>
|
||||
|
||||
<div className='flex justify-center w-full gap-2 mt-4'>
|
||||
|
|
|
@ -65,21 +65,21 @@ function DlgCloneRSForm({ hideWindow }: DlgCloneRSFormProps) {
|
|||
<TextInput id='title' label='Полное название' type='text'
|
||||
required
|
||||
value={title}
|
||||
onChange={event => { setTitle(event.target.value); }}
|
||||
onChange={event => setTitle(event.target.value)}
|
||||
/>
|
||||
<TextInput id='alias' label='Сокращение' type='text'
|
||||
required
|
||||
value={alias}
|
||||
widthClass='max-w-sm'
|
||||
onChange={event => { setAlias(event.target.value); }}
|
||||
onChange={event => setAlias(event.target.value)}
|
||||
/>
|
||||
<TextArea id='comment' label='Комментарий'
|
||||
value={comment}
|
||||
onChange={event => { setComment(event.target.value); }}
|
||||
onChange={event => setComment(event.target.value)}
|
||||
/>
|
||||
<Checkbox id='common' label='Общедоступная схема'
|
||||
value={common}
|
||||
onChange={event => { setCommon(event.target.checked); }}
|
||||
onChange={event => setCommon(event.target.checked)}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -34,9 +34,7 @@ function DlgCreateCst({ hideWindow, initial, onCreate }: DlgCreateCstProps) {
|
|||
}
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
onCreate(getData());
|
||||
};
|
||||
const handleSubmit = () => onCreate(getData());
|
||||
|
||||
useEffect(() => {
|
||||
if (initial) {
|
||||
|
@ -48,10 +46,7 @@ function DlgCreateCst({ hideWindow, initial, onCreate }: DlgCreateCstProps) {
|
|||
}
|
||||
}, [initial]);
|
||||
|
||||
useEffect(() => {
|
||||
setValidated(selectedType !== undefined);
|
||||
}, [selectedType]
|
||||
);
|
||||
useEffect(() => setValidated(selectedType !== undefined), [selectedType]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
@ -67,7 +62,7 @@ function DlgCreateCst({ hideWindow, initial, onCreate }: DlgCreateCstProps) {
|
|||
options={CstTypeSelector}
|
||||
placeholder='Выберите тип'
|
||||
values={selectedType ? [{ value: selectedType, label: getCstTypeLabel(selectedType) }] : []}
|
||||
onChange={data => { setSelectedType(data.length > 0 ? data[0].value : CstType.BASE); }}
|
||||
onChange={data => setSelectedType(data.length > 0 ? data[0].value : CstType.BASE)}
|
||||
/>
|
||||
</div>
|
||||
<TextArea id='term' label='Термин'
|
||||
|
@ -89,14 +84,14 @@ function DlgCreateCst({ hideWindow, initial, onCreate }: DlgCreateCstProps) {
|
|||
rows={2}
|
||||
value={textDefinition}
|
||||
spellCheck
|
||||
onChange={event => { setTextDefinition(event.target.value); }}
|
||||
onChange={event => setTextDefinition(event.target.value)}
|
||||
/>
|
||||
<TextArea id='convention' label='Конвенция / Комментарий'
|
||||
placeholder='Договоренность об интерпретации неопределяемого понятия
Комментарий к производному понятию'
|
||||
rows={2}
|
||||
value={convention}
|
||||
spellCheck
|
||||
onChange={event => { setConvention(event.target.value); }}
|
||||
onChange={event => setConvention(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
|
@ -72,7 +72,7 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
|||
options={CstTypeSelector}
|
||||
placeholder='Выберите тип'
|
||||
values={cstType ? [{ value: cstType, label: getCstTypeLabel(cstType) }] : []}
|
||||
onChange={data => { setCstType(data.length > 0 ? data[0].value : CstType.BASE); }}
|
||||
onChange={data => setCstType(data.length > 0 ? data[0].value : CstType.BASE)}
|
||||
/>
|
||||
<div>
|
||||
<TextInput id='alias' label='Имя'
|
||||
|
|
|
@ -32,7 +32,7 @@ function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
|
|||
if (event.target.files && event.target.files.length > 0) {
|
||||
setFile(event.target.files[0]);
|
||||
} else {
|
||||
setFile(undefined)
|
||||
setFile(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
|
|||
<Checkbox
|
||||
label='Загружать название и комментарий'
|
||||
value={loadMetadata}
|
||||
onChange={event => { setLoadMetadata(event.target.checked); }}
|
||||
onChange={event => setLoadMetadata(event.target.checked)}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
|
@ -45,7 +45,8 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
|||
|
||||
const isEnabled = useMemo(() => activeCst && isEditable, [activeCst, isEditable]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useLayoutEffect(
|
||||
() => {
|
||||
if (!activeCst) {
|
||||
setIsModified(false);
|
||||
return;
|
||||
|
@ -60,7 +61,8 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
|||
activeCst?.definition.text.raw, activeCst?.convention,
|
||||
term, textDefinition, expression, convention]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useLayoutEffect(
|
||||
() => {
|
||||
if (activeCst) {
|
||||
setAlias(activeCst.alias);
|
||||
setConvention(activeCst.convention ?? '');
|
||||
|
@ -86,7 +88,7 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
|||
definition_raw: textDefinition,
|
||||
term_raw: term
|
||||
};
|
||||
cstUpdate(data, () => { toast.success('Изменения сохранены'); });
|
||||
cstUpdate(data, () => toast.success('Изменения сохранены'));
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
|
@ -208,8 +210,8 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
|||
resolved={activeCst?.definition.text.resolved ?? ''}
|
||||
disabled={!isEnabled}
|
||||
spellCheck
|
||||
onChange={event => { setTextDefinition(event.target.value); }}
|
||||
onFocus={() => { setEditMode(EditMode.TEXT); }}
|
||||
onChange={event => setTextDefinition(event.target.value)}
|
||||
onFocus={() => setEditMode(EditMode.TEXT)}
|
||||
/>
|
||||
<TextArea id='convention' label='Конвенция / Комментарий'
|
||||
placeholder='Договоренность об интерпретации неопределяемого понятия
Комментарий к производному понятию'
|
||||
|
@ -217,8 +219,8 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onO
|
|||
value={convention}
|
||||
disabled={!isEnabled}
|
||||
spellCheck
|
||||
onChange={event => { setConvention(event.target.value); }}
|
||||
onFocus={() => { setEditMode(EditMode.TEXT); }}
|
||||
onChange={event => setConvention(event.target.value)}
|
||||
onFocus={() => setEditMode(EditMode.TEXT)}
|
||||
/>
|
||||
<div className='flex justify-center w-full mt-4 mb-2'>
|
||||
<SubmitButton
|
||||
|
|
|
@ -53,7 +53,9 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
|||
}, -1);
|
||||
const target = Math.max(0, currentIndex - 1) + 1
|
||||
const data = {
|
||||
items: selected.map(id => { return { id: id }; }),
|
||||
items: selected.map(id => {
|
||||
return { id: id };
|
||||
}),
|
||||
move_to: target
|
||||
}
|
||||
cstMoveTo(data);
|
||||
|
@ -78,7 +80,9 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
|||
}, -1);
|
||||
const target = Math.min(schema.items.length - 1, currentIndex - count + 2) + 1
|
||||
const data: ICstMovetoData = {
|
||||
items: selected.map(id => { return { id: id }; }),
|
||||
items: selected.map(id => {
|
||||
return { id: id };
|
||||
}),
|
||||
move_to: target
|
||||
}
|
||||
cstMoveTo(data);
|
||||
|
@ -89,7 +93,6 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
|||
resetAliases(() => toast.success('Переиндексация конституент успешна'));
|
||||
}
|
||||
|
||||
// Add new constituenta
|
||||
function handleCreateCst(type?: CstType) {
|
||||
if (!schema) {
|
||||
return;
|
||||
|
|
|
@ -400,7 +400,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
searchable={false}
|
||||
placeholder='Выберите цвет'
|
||||
values={coloringScheme ? [{ value: coloringScheme, label: mapColoringLabels.get(coloringScheme) }] : []}
|
||||
onChange={data => { setColoringScheme(data.length > 0 ? data[0].value : GraphColoringSelector[0].value); }}
|
||||
onChange={data => setColoringScheme(data.length > 0 ? data[0].value : GraphColoringSelector[0].value)}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
@ -410,7 +410,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
searchable={false}
|
||||
placeholder='Способ расположения'
|
||||
values={layout ? [{ value: layout, label: mapLayoutLabels.get(layout) }] : []}
|
||||
onChange={data => { setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value); }}
|
||||
onChange={data => setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value)}
|
||||
/>
|
||||
<Checkbox
|
||||
label='Скрыть текст'
|
||||
|
|
|
@ -148,7 +148,9 @@ function RSTabs() {
|
|||
return;
|
||||
}
|
||||
const data = {
|
||||
items: deleted.map(id => { return { id: id }; })
|
||||
items: deleted.map(id => {
|
||||
return { id: id };
|
||||
})
|
||||
};
|
||||
let activeIndex = schema.items.findIndex(cst => cst.id === activeID);
|
||||
cstDelete(data, () => {
|
||||
|
|
|
@ -12,7 +12,7 @@ function RSLocalButton({ text, tooltip, disabled, onInsert }: RSLocalButtonProps
|
|||
<button
|
||||
type='button'
|
||||
disabled={disabled}
|
||||
onClick={() => { onInsert(TokenID.ID_LOCAL, text); }}
|
||||
onClick={() => onInsert(TokenID.ID_LOCAL, text)}
|
||||
title={tooltip}
|
||||
tabIndex={-1}
|
||||
className='w-[1.5rem] h-7 cursor-pointer border rounded-none clr-btn-clear'
|
||||
|
|
|
@ -14,7 +14,7 @@ function RSTokenButton({ id, disabled, onInsert }: RSTokenButtonProps) {
|
|||
<button
|
||||
type='button'
|
||||
disabled={disabled}
|
||||
onClick={() => { onInsert(id); }}
|
||||
onClick={() => onInsert(id)}
|
||||
title={data.tooltip}
|
||||
tabIndex={-1}
|
||||
className={`px-1 cursor-pointer border rounded-none h-7 ${width} clr-btn-clear`}
|
||||
|
|
|
@ -55,17 +55,17 @@ function RegisterPage() {
|
|||
<TextInput id='username' label='Имя пользователя' type='text'
|
||||
required
|
||||
value={username}
|
||||
onChange={event => { setUsername(event.target.value); }}
|
||||
onChange={event => setUsername(event.target.value)}
|
||||
/>
|
||||
<TextInput id='password' label='Пароль' type='password'
|
||||
required
|
||||
value={password}
|
||||
onChange={event => { setPassword(event.target.value); }}
|
||||
onChange={event => setPassword(event.target.value)}
|
||||
/>
|
||||
<TextInput id='password2' label='Повторите пароль' type='password'
|
||||
required
|
||||
value={password2}
|
||||
onChange={event => { setPassword2(event.target.value); }}
|
||||
onChange={event => setPassword2(event.target.value)}
|
||||
/>
|
||||
<div className='text-sm'>
|
||||
<p>- используйте уникальный пароль</p>
|
||||
|
@ -78,15 +78,15 @@ function RegisterPage() {
|
|||
<TextInput id='email' label='email' type='text'
|
||||
required
|
||||
value={email}
|
||||
onChange={event => { setEmail(event.target.value); }}
|
||||
onChange={event => setEmail(event.target.value)}
|
||||
/>
|
||||
<TextInput id='first_name' label='Имя' type='text'
|
||||
value={firstName}
|
||||
onChange={event => { setFirstName(event.target.value); }}
|
||||
onChange={event => setFirstName(event.target.value)}
|
||||
/>
|
||||
<TextInput id='last_name' label='Фамилия' type='text'
|
||||
value={lastName}
|
||||
onChange={event => { setLastName(event.target.value); }}
|
||||
onChange={event => setLastName(event.target.value)}
|
||||
/>
|
||||
|
||||
<div className='flex items-center justify-center w-full my-4'>
|
||||
|
|
Loading…
Reference in New Issue
Block a user