Minor UI fixes

This commit is contained in:
IRBorisov 2023-08-27 16:35:17 +03:00
parent e8ec015633
commit 35754a59c7
22 changed files with 82 additions and 65 deletions

View File

@ -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>

View File

@ -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'>

View File

@ -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}>

View File

@ -30,7 +30,7 @@ function UserMenu() {
</div>
{ user && menu.isActive &&
<UserDropdown
hideDropdown={() => { menu.hide(); }}
hideDropdown={() => menu.hide()}
/>}
</div>
);

View File

@ -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]);

View File

@ -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);

View File

@ -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)
};
}

View File

@ -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();

View File

@ -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);

View File

@ -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>

View File

@ -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'>

View File

@ -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>
);

View File

@ -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='Договоренность об интерпретации неопределяемого понятия&#x000D;&#x000A;Комментарий к производному понятию'
rows={2}
value={convention}
spellCheck
onChange={event => { setConvention(event.target.value); }}
onChange={event => setConvention(event.target.value)}
/>
</div>
</Modal>

View File

@ -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='Имя'

View File

@ -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>

View File

@ -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='Договоренность об интерпретации неопределяемого понятия&#x000D;&#x000A;Комментарий к производному понятию'
@ -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

View File

@ -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;

View File

@ -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='Скрыть текст'

View File

@ -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, () => {

View File

@ -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'

View File

@ -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`}

View File

@ -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'>