F: Improve buttons accessibility
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run

This commit is contained in:
Ivan 2025-03-20 11:33:42 +03:00
parent 575b7a29f2
commit e144d20f2d
57 changed files with 217 additions and 136 deletions

View File

@ -23,6 +23,7 @@ export function ToggleNavigation() {
onClick={toggleNoNavigation} onClick={toggleNoNavigation}
data-tooltip-id={globalIDs.tooltip} data-tooltip-id={globalIDs.tooltip}
data-tooltip-content={noNavigationAnimation ? 'Показать навигацию' : 'Скрыть навигацию'} data-tooltip-content={noNavigationAnimation ? 'Показать навигацию' : 'Скрыть навигацию'}
aria-label={noNavigationAnimation ? 'Показать навигацию' : 'Скрыть навигацию'}
> >
{!noNavigationAnimation ? <IconPin size='0.75rem' /> : null} {!noNavigationAnimation ? <IconPin size='0.75rem' /> : null}
{noNavigationAnimation ? <IconUnpin size='0.75rem' /> : null} {noNavigationAnimation ? <IconUnpin size='0.75rem' /> : null}
@ -35,6 +36,7 @@ export function ToggleNavigation() {
onClick={toggleDarkMode} onClick={toggleDarkMode}
data-tooltip-id={globalIDs.tooltip} data-tooltip-id={globalIDs.tooltip}
data-tooltip-content={darkMode ? 'Тема: Темная' : 'Тема: Светлая'} data-tooltip-content={darkMode ? 'Тема: Темная' : 'Тема: Светлая'}
aria-label={darkMode ? 'Тема: Темная' : 'Тема: Светлая'}
> >
{darkMode ? <IconDarkTheme size='0.75rem' /> : null} {darkMode ? <IconDarkTheme size='0.75rem' /> : null}
{!darkMode ? <IconLightTheme size='0.75rem' /> : null} {!darkMode ? <IconLightTheme size='0.75rem' /> : null}

View File

@ -85,27 +85,28 @@ export function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
/> />
<DropdownButton <DropdownButton
text={darkMode ? 'Тема: Темная' : 'Тема: Светлая'} text={darkMode ? 'Тема: Темная' : 'Тема: Светлая'}
icon={darkMode ? <IconDarkTheme size='1rem' /> : <IconLightTheme size='1rem' />}
title='Переключение темы оформления' title='Переключение темы оформления'
icon={darkMode ? <IconDarkTheme size='1rem' /> : <IconLightTheme size='1rem' />}
onClick={handleToggleDarkMode} onClick={handleToggleDarkMode}
/> />
<DropdownButton <DropdownButton
text={showHelp ? 'Помощь: Вкл' : 'Помощь: Выкл'} text={showHelp ? 'Помощь: Вкл' : 'Помощь: Выкл'}
icon={showHelp ? <IconHelp size='1rem' /> : <IconHelpOff size='1rem' />}
title='Отображение иконок подсказок' title='Отображение иконок подсказок'
icon={showHelp ? <IconHelp size='1rem' /> : <IconHelpOff size='1rem' />}
onClick={toggleShowHelp} onClick={toggleShowHelp}
/> />
{user.is_staff ? ( {user.is_staff ? (
<DropdownButton <DropdownButton
text={adminMode ? 'Админ: Вкл' : 'Админ: Выкл'} text={adminMode ? 'Админ: Вкл' : 'Админ: Выкл'}
icon={adminMode ? <IconAdmin size='1rem' /> : <IconAdminOff size='1rem' />}
title='Работа в режиме администратора' title='Работа в режиме администратора'
icon={adminMode ? <IconAdmin size='1rem' /> : <IconAdminOff size='1rem' />}
onClick={toggleAdminMode} onClick={toggleAdminMode}
/> />
) : null} ) : null}
{user.is_staff ? ( {user.is_staff ? (
<DropdownButton <DropdownButton
text='REST API' // text='REST API' //
title='Переход к backend API'
icon={<IconRESTapi size='1rem' />} icon={<IconRESTapi size='1rem' />}
className='border-t' className='border-t'
onClick={gotoRestApi} onClick={gotoRestApi}
@ -114,6 +115,7 @@ export function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
{user.is_staff ? ( {user.is_staff ? (
<DropdownButton <DropdownButton
text='База данных' // text='База данных' //
title='Переход к администрированию базы данных'
icon={<IconDatabase size='1rem' />} icon={<IconDatabase size='1rem' />}
onClick={gotoAdmin} onClick={gotoAdmin}
/> />
@ -121,6 +123,7 @@ export function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
{user?.is_staff ? ( {user?.is_staff ? (
<DropdownButton <DropdownButton
text='Иконки' // text='Иконки' //
title='Переход к странице иконок'
icon={<IconImage size='1rem' />} icon={<IconImage size='1rem' />}
onClick={gotoIcons} onClick={gotoIcons}
/> />
@ -128,6 +131,7 @@ export function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
{user.is_staff ? ( {user.is_staff ? (
<DropdownButton <DropdownButton
text='Структура БД' // text='Структура БД' //
title='Переход к странице структуры БД'
icon={<IconDBStructure size='1rem' />} icon={<IconDBStructure size='1rem' />}
onClick={gotoDatabaseSchema} onClick={gotoDatabaseSchema}
className='border-b' className='border-b'
@ -135,6 +139,7 @@ export function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
) : null} ) : null}
<DropdownButton <DropdownButton
text='Выйти...' text='Выйти...'
title='Выход из приложения'
className='font-semibold' className='font-semibold'
icon={<IconLogout size='1rem' />} icon={<IconLogout size='1rem' />}
onClick={logoutAndRedirect} onClick={logoutAndRedirect}

View File

@ -38,7 +38,6 @@ export function Button({
return ( return (
<button <button
type='button' type='button'
disabled={disabled ?? loading}
className={clsx( className={clsx(
'inline-flex gap-2 items-center justify-center', 'inline-flex gap-2 items-center justify-center',
'font-medium select-none disabled:cursor-auto', 'font-medium select-none disabled:cursor-auto',
@ -53,6 +52,8 @@ export function Button({
data-tooltip-html={titleHtml} data-tooltip-html={titleHtml}
data-tooltip-content={title} data-tooltip-content={title}
data-tooltip-hidden={hideTitle} data-tooltip-hidden={hideTitle}
disabled={disabled ?? loading}
aria-label={!text ? title : undefined}
{...restProps} {...restProps}
> >
{icon ? icon : null} {icon ? icon : null}

View File

@ -49,6 +49,7 @@ export function MiniButton({
data-tooltip-html={titleHtml} data-tooltip-html={titleHtml}
data-tooltip-content={title} data-tooltip-content={title}
data-tooltip-hidden={hideTitle} data-tooltip-hidden={hideTitle}
aria-label={title}
{...restProps} {...restProps}
> >
{icon} {icon}

View File

@ -46,6 +46,7 @@ export function DropdownButton({
data-tooltip-html={titleHtml} data-tooltip-html={titleHtml}
data-tooltip-content={title} data-tooltip-content={title}
data-tooltip-hidden={hideTitle} data-tooltip-hidden={hideTitle}
aria-label={title}
{...restProps} {...restProps}
> >
{icon ? icon : null} {icon ? icon : null}

View File

@ -55,12 +55,12 @@ export function CheckboxTristate({
cursor, cursor,
className className
)} )}
disabled={disabled}
onClick={handleClick} onClick={handleClick}
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined} data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
data-tooltip-html={titleHtml} data-tooltip-html={titleHtml}
data-tooltip-content={title} data-tooltip-content={title}
data-tooltip-hidden={hideTitle} data-tooltip-hidden={hideTitle}
disabled={disabled}
{...restProps} {...restProps}
> >
<div <div

View File

@ -54,12 +54,12 @@ export function Checkbox({
cursor, cursor,
className className
)} )}
disabled={disabled}
onClick={handleClick} onClick={handleClick}
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined} data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
data-tooltip-html={titleHtml} data-tooltip-html={titleHtml}
data-tooltip-content={title} data-tooltip-content={title}
data-tooltip-hidden={hideTitle} data-tooltip-hidden={hideTitle}
disabled={disabled}
{...restProps} {...restProps}
> >
<div <div

View File

@ -99,6 +99,7 @@ export function SelectTree<ItemType>({
> >
{foldable.has(item) ? ( {foldable.has(item) ? (
<MiniButton <MiniButton
aria-label={!folded.includes(item) ? 'Свернуть' : 'Развернуть'}
className={clsx('absolute left-1', !folded.includes(item) ? 'top-1.5' : 'top-1')} className={clsx('absolute left-1', !folded.includes(item) ? 'top-1.5' : 'top-1')}
noPadding noPadding
noHover noHover

View File

@ -105,9 +105,9 @@ export function ModalForm({
) : null} ) : null}
<MiniButton <MiniButton
noPadding
aria-label='Закрыть'
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')} titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
aria-label='Закрыть'
noPadding
icon={<IconClose size='1.25rem' />} icon={<IconClose size='1.25rem' />}
className='absolute z-pop top-2 right-2' className='absolute z-pop top-2 right-2'
onClick={hideDialog} onClick={hideDialog}

View File

@ -49,9 +49,9 @@ export function ModalView({
) : null} ) : null}
<MiniButton <MiniButton
noPadding
aria-label='Закрыть'
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')} titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
aria-label='Закрыть'
noPadding
icon={<IconClose size='1.25rem' />} icon={<IconClose size='1.25rem' />}
className='absolute z-pop top-2 right-2' className='absolute z-pop top-2 right-2'
onClick={hideDialog} onClick={hideDialog}

View File

@ -14,7 +14,15 @@ interface TabLabelProps extends Omit<TabPropsImpl, 'children'>, Titled {
/** /**
* Displays a tab header with a label. * Displays a tab header with a label.
*/ */
export function TabLabel({ label, title, titleHtml, hideTitle, className, ...otherProps }: TabLabelProps) { export function TabLabel({
label,
title,
titleHtml,
hideTitle,
className,
role = 'tab',
...otherProps
}: TabLabelProps) {
return ( return (
<TabImpl <TabImpl
className={clsx( className={clsx(
@ -31,6 +39,7 @@ export function TabLabel({ label, title, titleHtml, hideTitle, className, ...oth
data-tooltip-html={titleHtml} data-tooltip-html={titleHtml}
data-tooltip-content={title} data-tooltip-content={title}
data-tooltip-hidden={hideTitle} data-tooltip-hidden={hideTitle}
role={role}
{...otherProps} {...otherProps}
> >
{label} {label}

View File

@ -50,6 +50,7 @@ export function ValueIcon({
...restProps ...restProps
}: ValueIconProps) { }: ValueIconProps) {
// TODO: use CSS instead of threshold // TODO: use CSS instead of threshold
// TODO: do not add button if onClick is disabled
const isSmall = !smallThreshold || String(value).length < smallThreshold; const isSmall = !smallThreshold || String(value).length < smallThreshold;
return ( return (
<div <div
@ -66,7 +67,7 @@ export function ValueIcon({
data-tooltip-content={title} data-tooltip-content={title}
data-tooltip-hidden={hideTitle} data-tooltip-hidden={hideTitle}
> >
<MiniButton noHover noPadding icon={icon} disabled={disabled} onClick={onClick} /> <MiniButton noHover noPadding icon={icon} onClick={onClick} disabled={disabled} />
<span id={id} className={clsx({ 'text-xs': !isSmall }, textClassName)}> <span id={id} className={clsx({ 'text-xs': !isSmall }, textClassName)}>
{value} {value}
</span> </span>

View File

@ -64,20 +64,20 @@ export function LoginPage() {
id='username' id='username'
autoComplete='username' autoComplete='username'
label='Логин или email' label='Логин или email'
{...register('username')}
autoFocus autoFocus
allowEnter allowEnter
spellCheck={false} spellCheck={false}
defaultValue={initialName} defaultValue={initialName}
{...register('username')}
error={errors.username} error={errors.username}
/> />
<TextInput <TextInput
id='password' id='password'
{...register('password')}
type='password' type='password'
autoComplete='current-password' autoComplete='current-password'
label='Пароль' label='Пароль'
allowEnter allowEnter
{...register('password')}
error={errors.password} error={errors.password}
/> />

View File

@ -85,9 +85,9 @@ export function EditorLibraryItem({ schema, isAttachedToOSS }: EditorLibraryItem
<div className='flex flex-col'> <div className='flex flex-col'>
<div className='relative flex justify-stretch sm:mb-1 max-w-120 gap-3'> <div className='relative flex justify-stretch sm:mb-1 max-w-120 gap-3'>
<MiniButton <MiniButton
title='Открыть в библиотеке'
noHover noHover
noPadding noPadding
title='Открыть в библиотеке'
icon={<IconFolderOpened size='1.25rem' className='icon-primary' />} icon={<IconFolderOpened size='1.25rem' className='icon-primary' />}
onClick={handleOpenLibrary} onClick={handleOpenLibrary}
/> />

View File

@ -67,22 +67,22 @@ export function MenuRole({ isOwned, isEditor }: MenuRoleProps) {
text={labelUserRole(UserRole.EDITOR)} text={labelUserRole(UserRole.EDITOR)}
title={describeUserRole(UserRole.EDITOR)} title={describeUserRole(UserRole.EDITOR)}
icon={<IconRole role={UserRole.EDITOR} size='1rem' />} icon={<IconRole role={UserRole.EDITOR} size='1rem' />}
disabled={!isOwned && !isEditor}
onClick={() => handleChangeMode(UserRole.EDITOR)} onClick={() => handleChangeMode(UserRole.EDITOR)}
disabled={!isOwned && !isEditor}
/> />
<DropdownButton <DropdownButton
text={labelUserRole(UserRole.OWNER)} text={labelUserRole(UserRole.OWNER)}
title={describeUserRole(UserRole.OWNER)} title={describeUserRole(UserRole.OWNER)}
icon={<IconRole role={UserRole.OWNER} size='1rem' />} icon={<IconRole role={UserRole.OWNER} size='1rem' />}
disabled={!isOwned}
onClick={() => handleChangeMode(UserRole.OWNER)} onClick={() => handleChangeMode(UserRole.OWNER)}
disabled={!isOwned}
/> />
<DropdownButton <DropdownButton
text={labelUserRole(UserRole.ADMIN)} text={labelUserRole(UserRole.ADMIN)}
title={describeUserRole(UserRole.ADMIN)} title={describeUserRole(UserRole.ADMIN)}
icon={<IconRole role={UserRole.ADMIN} size='1rem' />} icon={<IconRole role={UserRole.ADMIN} size='1rem' />}
disabled={!user.is_staff}
onClick={() => handleChangeMode(UserRole.ADMIN)} onClick={() => handleChangeMode(UserRole.ADMIN)}
disabled={!user.is_staff}
/> />
</Dropdown> </Dropdown>
</div> </div>

View File

@ -30,8 +30,8 @@ export function MiniSelectorOSS({ items, onSelect, className, ...restProps }: Mi
return ( return (
<div ref={ossMenu.ref} className={clsx('relative flex items-center', className)} {...restProps}> <div ref={ossMenu.ref} className={clsx('relative flex items-center', className)} {...restProps}>
<MiniButton <MiniButton
icon={<IconOSS size='1.25rem' className='icon-primary' />}
title='Операционные схемы' title='Операционные схемы'
icon={<IconOSS size='1.25rem' className='icon-primary' />}
hideTitle={ossMenu.isOpen} hideTitle={ossMenu.isOpen}
onClick={onToggle} onClick={onToggle}
/> />
@ -40,9 +40,9 @@ export function MiniSelectorOSS({ items, onSelect, className, ...restProps }: Mi
<Label text='Список ОСС' className='border-b px-3 py-1' /> <Label text='Список ОСС' className='border-b px-3 py-1' />
{items.map((reference, index) => ( {items.map((reference, index) => (
<DropdownButton <DropdownButton
className='min-w-20'
key={`${prefixes.oss_list}${index}`} key={`${prefixes.oss_list}${index}`}
text={reference.alias} text={reference.alias}
className='min-w-20'
onClick={event => onSelect(event, reference)} onClick={event => onSelect(event, reference)}
/> />
))} ))}

View File

@ -114,8 +114,8 @@ export function PickSchema({
/> />
<div className='relative' ref={locationMenu.ref}> <div className='relative' ref={locationMenu.ref}>
<MiniButton <MiniButton
icon={<IconFolderTree size='1.25rem' className={!!filterLocation ? 'icon-green' : 'icon-primary'} />}
title='Фильтр по расположению' title='Фильтр по расположению'
icon={<IconFolderTree size='1.25rem' className={!!filterLocation ? 'icon-green' : 'icon-primary'} />}
className='mt-1' className='mt-1'
onClick={() => locationMenu.toggle()} onClick={() => locationMenu.toggle()}
/> />

View File

@ -52,10 +52,10 @@ export function SelectLocationHead({
return ( return (
<DropdownButton <DropdownButton
key={`${prefixes.location_head_list}${index}`} key={`${prefixes.location_head_list}${index}`}
onClick={() => handleChange(head)}
title={describeLocationHead(head)}
icon={<IconLocationHead value={head} size='1rem' />}
text={labelLocationHead(head)} text={labelLocationHead(head)}
title={describeLocationHead(head)}
onClick={() => handleChange(head)}
icon={<IconLocationHead value={head} size='1rem' />}
/> />
); );
})} })}

View File

@ -48,13 +48,14 @@ export function ToolbarItemAccess({
<Label text='Доступ' className='self-center select-none' /> <Label text='Доступ' className='self-center select-none' />
<div className='ml-auto cc-icons'> <div className='ml-auto cc-icons'>
<SelectAccessPolicy <SelectAccessPolicy
disabled={role <= UserRole.EDITOR || isProcessing || isAttachedToOSS}
value={policy} value={policy}
onChange={handleSetAccessPolicy} onChange={handleSetAccessPolicy}
disabled={role <= UserRole.EDITOR || isProcessing || isAttachedToOSS}
/> />
<MiniButton <MiniButton
title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'} title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
aria-label='Переключатель отображения библиотеки'
icon={<IconItemVisibility value={visible} />} icon={<IconItemVisibility value={visible} />}
onClick={toggleVisible} onClick={toggleVisible}
disabled={role === UserRole.READER || isProcessing} disabled={role === UserRole.READER || isProcessing}
@ -62,6 +63,7 @@ export function ToolbarItemAccess({
<MiniButton <MiniButton
title={readOnly ? 'Изменение: запрещено' : 'Изменение: разрешено'} title={readOnly ? 'Изменение: запрещено' : 'Изменение: разрешено'}
aria-label='Переключатель режима изменения'
icon={ icon={
readOnly ? ( readOnly ? (
<IconImmutable size='1.25rem' className='text-sec-600' /> <IconImmutable size='1.25rem' className='text-sec-600' />

View File

@ -56,13 +56,15 @@ export function ToolbarItemCard({ className, schema, onSubmit, isMutable, delete
{isMutable || isModified ? ( {isMutable || isModified ? (
<MiniButton <MiniButton
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')} titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
disabled={!canSave} aria-label='Сохранить изменения'
icon={<IconSave size='1.25rem' className='icon-primary' />} icon={<IconSave size='1.25rem' className='icon-primary' />}
onClick={onSubmit} onClick={onSubmit}
disabled={!canSave}
/> />
) : null} ) : null}
<MiniButton <MiniButton
titleHtml={tooltipText.shareItem(schema.access_policy === AccessPolicy.PUBLIC)} titleHtml={tooltipText.shareItem(schema.access_policy === AccessPolicy.PUBLIC)}
aria-label='Поделиться схемой'
icon={<IconShare size='1.25rem' className='icon-primary' />} icon={<IconShare size='1.25rem' className='icon-primary' />}
onClick={sharePage} onClick={sharePage}
disabled={schema.access_policy !== AccessPolicy.PUBLIC} disabled={schema.access_policy !== AccessPolicy.PUBLIC}
@ -71,8 +73,8 @@ export function ToolbarItemCard({ className, schema, onSubmit, isMutable, delete
<MiniButton <MiniButton
title='Удалить схему' title='Удалить схему'
icon={<IconDestroy size='1.25rem' className='icon-red' />} icon={<IconDestroy size='1.25rem' className='icon-red' />}
disabled={!isMutable || isProcessing || role < UserRole.OWNER}
onClick={deleteSchema} onClick={deleteSchema}
disabled={!isMutable || isProcessing || role < UserRole.OWNER}
/> />
) : null} ) : null}
<BadgeHelp topic={HelpTopic.UI_RS_CARD} offset={4} /> <BadgeHelp topic={HelpTopic.UI_RS_CARD} offset={4} />

View File

@ -95,6 +95,7 @@ export function DlgCloneLibraryItem() {
render={({ field }) => ( render={({ field }) => (
<MiniButton <MiniButton
title={field.value ? 'Библиотека: отображать' : 'Библиотека: скрывать'} title={field.value ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
aria-label='Переключатель отображения библиотеки'
icon={<IconItemVisibility value={field.value} />} icon={<IconItemVisibility value={field.value} />}
onClick={() => field.onChange(!field.value)} onClick={() => field.onChange(!field.value)}
/> />

View File

@ -47,12 +47,12 @@ export function DlgEditEditors() {
<div className='self-center text-sm font-semibold'> <div className='self-center text-sm font-semibold'>
<span>Всего редакторов [{selected.length}]</span> <span>Всего редакторов [{selected.length}]</span>
<MiniButton <MiniButton
noHover
title='Очистить список' title='Очистить список'
noHover
className='py-0 align-middle' className='py-0 align-middle'
icon={<IconRemove size='1.5rem' className='icon-red' />} icon={<IconRemove size='1.5rem' className='icon-red' />}
disabled={selected.length === 0}
onClick={() => setSelected([])} onClick={() => setSelected([])}
disabled={selected.length === 0}
/> />
</div> </div>

View File

@ -107,14 +107,15 @@ export function DlgEditVersions() {
<MiniButton <MiniButton
type='submit' type='submit'
title={isValid ? 'Сохранить изменения' : errorMsg.versionTaken} title={isValid ? 'Сохранить изменения' : errorMsg.versionTaken}
disabled={!isDirty || !isValid || isProcessing} aria-label='Сохранить изменения'
icon={<IconSave size='1.25rem' className='icon-primary' />} icon={<IconSave size='1.25rem' className='icon-primary' />}
disabled={!isDirty || !isValid || isProcessing}
/> />
<MiniButton <MiniButton
title='Сбросить несохраненные изменения' title='Сбросить несохраненные изменения'
disabled={!isDirty}
onClick={() => reset()} onClick={() => reset()}
icon={<IconReset size='1.25rem' className='icon-primary' />} icon={<IconReset size='1.25rem' className='icon-primary' />}
disabled={!isDirty}
/> />
</div> </div>
</form> </form>

View File

@ -66,9 +66,9 @@ export function TableVersions({ processing, items, onDelete, selected, onSelect
className='align-middle' className='align-middle'
noHover noHover
noPadding noPadding
disabled={processing}
icon={<IconRemove size='1.25rem' className='icon-red' />} icon={<IconRemove size='1.25rem' className='icon-red' />}
onClick={event => handleDeleteVersion(event, props.row.original.id)} onClick={event => handleDeleteVersion(event, props.row.original.id)}
disabled={processing}
/> />
) )
}) })

View File

@ -185,6 +185,7 @@ export function FormCreateItem() {
render={({ field }) => ( render={({ field }) => (
<MiniButton <MiniButton
title={field.value ? 'Библиотека: отображать' : 'Библиотека: скрывать'} title={field.value ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
aria-label='Переключатель отображения библиотеки'
icon={<IconItemVisibility value={field.value} />} icon={<IconItemVisibility value={field.value} />}
onClick={() => field.onChange(!field.value)} onClick={() => field.onChange(!field.value)}
/> />

View File

@ -56,8 +56,8 @@ export function LibraryPage() {
<ToolbarSearch className='top-0 h-9' total={libraryItems.length} filtered={filtered.length} /> <ToolbarSearch className='top-0 h-9' total={libraryItems.length} filtered={filtered.length} />
<div className='relative cc-fade-in flex'> <div className='relative cc-fade-in flex'>
<MiniButton <MiniButton
className='absolute z-tooltip -top-8 right-6'
title='Выгрузить в формате CSV' title='Выгрузить в формате CSV'
className='absolute z-tooltip -top-8 right-6'
icon={<IconCSV size='1.25rem' className='icon-green' />} icon={<IconCSV size='1.25rem' className='icon-green' />}
onClick={handleDownloadCSV} onClick={handleDownloadCSV}
/> />

View File

@ -98,17 +98,20 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
<Dropdown isOpen={userMenu.isOpen}> <Dropdown isOpen={userMenu.isOpen}>
<DropdownButton <DropdownButton
text='Я - Владелец' text='Я - Владелец'
title='Фильтровать схемы, в которых текущий пользователь является владельцем'
icon={<IconOwner size='1.25rem' className={tripleToggleColor(isOwned)} />} icon={<IconOwner size='1.25rem' className={tripleToggleColor(isOwned)} />}
onClick={toggleOwned} onClick={toggleOwned}
/> />
<DropdownButton <DropdownButton
text='Я - Редактор' text='Я - Редактор'
title='Фильтровать схемы, в которых текущий пользователя является редактором'
icon={<IconEditor size='1.25rem' className={tripleToggleColor(isEditor)} />} icon={<IconEditor size='1.25rem' className={tripleToggleColor(isEditor)} />}
onClick={toggleEditor} onClick={toggleEditor}
/> />
<SelectUser <SelectUser
noBorder aria-label='Выбор пользователя для фильтра по владельцу'
placeholder='Выберите владельца' placeholder='Выберите владельца'
noBorder
className='min-w-60 text-sm mx-1 mb-1' className='min-w-60 text-sm mx-1 mb-1'
value={filterUser} value={filterUser}
onChange={setFilterUser} onChange={setFilterUser}
@ -155,13 +158,14 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
<Dropdown isOpen={headMenu.isOpen} stretchLeft> <Dropdown isOpen={headMenu.isOpen} stretchLeft>
<DropdownButton <DropdownButton
title='Переключение в режим Проводник'
text='проводник...' text='проводник...'
title='Переключение в режим Проводник'
icon={<IconFolderTree size='1rem' className='clr-text-controls' />} icon={<IconFolderTree size='1rem' className='clr-text-controls' />}
onClick={handleToggleFolder} onClick={handleToggleFolder}
/> />
<DropdownButton <DropdownButton
text='отображать все' text='отображать все'
title='Очистить фильтр по расположению'
icon={<IconFolder size='1rem' className='clr-text-controls' />} icon={<IconFolder size='1rem' className='clr-text-controls' />}
onClick={() => handleChange(null)} onClick={() => handleChange(null)}
/> />
@ -169,9 +173,9 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
return ( return (
<DropdownButton <DropdownButton
key={`${prefixes.location_head_list}${index}`} key={`${prefixes.location_head_list}${index}`}
onClick={() => handleChange(head)}
title={describeLocationHead(head)}
text={labelLocationHead(head)} text={labelLocationHead(head)}
title={describeLocationHead(head)}
onClick={() => handleChange(head)}
icon={<IconLocationHead value={head} size='1rem' />} icon={<IconLocationHead value={head} size='1rem' />}
/> />
); );

View File

@ -35,11 +35,12 @@ export function useLibraryColumns() {
id: 'location', id: 'location',
header: () => ( header: () => (
<MiniButton <MiniButton
titleHtml='Переключение в режим Проводник'
aria-label='Переключатель режима Проводник'
noPadding noPadding
noHover noHover
className='pl-2 max-h-4 -translate-y-0.5' className='pl-2 max-h-4 -translate-y-0.5'
onClick={handleToggleFolder} onClick={handleToggleFolder}
titleHtml='Переключение в режим Проводник'
icon={<IconFolderTree size='1.25rem' className='clr-text-controls' />} icon={<IconFolderTree size='1.25rem' className='clr-text-controls' />}
/> />
), ),

View File

@ -71,21 +71,23 @@ export function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocati
<div className='cc-icons'> <div className='cc-icons'>
{canRename ? ( {canRename ? (
<MiniButton <MiniButton
icon={<IconFolderEdit size='1.25rem' className='icon-primary' />}
titleHtml='<b>Редактирование пути</b><br/>Перемещаются только Ваши схемы<br/>в указанной папке (и подпапках)' titleHtml='<b>Редактирование пути</b><br/>Перемещаются только Ваши схемы<br/>в указанной папке (и подпапках)'
aria-label='Редактирование расположения'
icon={<IconFolderEdit size='1.25rem' className='icon-primary' />}
onClick={onRenameLocation} onClick={onRenameLocation}
/> />
) : null} ) : null}
{!!location ? ( {!!location ? (
<MiniButton <MiniButton
title={subfolders ? 'Вложенные папки: Вкл' : 'Вложенные папки: Выкл'} title={subfolders ? 'Вложенные папки: Вкл' : 'Вложенные папки: Выкл'}
aria-label='Переключатель отображения вложенных папок'
icon={<IconShowSubfolders value={subfolders} />} icon={<IconShowSubfolders value={subfolders} />}
onClick={toggleSubfolders} onClick={toggleSubfolders}
/> />
) : null} ) : null}
<MiniButton <MiniButton
icon={<IconFolderTree size='1.25rem' className='icon-green' />}
title='Переключение в режим Поиск' title='Переключение в режим Поиск'
icon={<IconFolderTree size='1.25rem' className='icon-green' />}
onClick={toggleFolderMode} onClick={toggleFolderMode}
/> />
</div> </div>

View File

@ -81,22 +81,22 @@ export function PickMultiOperation({ rows, items, value, onChange, className, ..
cell: props => ( cell: props => (
<div className='flex gap-1 w-fit'> <div className='flex gap-1 w-fit'>
<MiniButton <MiniButton
title='Удалить'
noHover noHover
className='px-0' className='px-0'
title='Удалить'
icon={<IconRemove size='1rem' className='icon-red' />} icon={<IconRemove size='1rem' className='icon-red' />}
onClick={() => handleDelete(props.row.original.id)} onClick={() => handleDelete(props.row.original.id)}
/> />
<MiniButton <MiniButton
title='Переместить выше'
noHover noHover
className='px-0' className='px-0'
title='Выше'
icon={<IconMoveUp size='1rem' className='icon-primary' />} icon={<IconMoveUp size='1rem' className='icon-primary' />}
onClick={() => handleMoveUp(props.row.original.id)} onClick={() => handleMoveUp(props.row.original.id)}
/> />
<MiniButton <MiniButton
title='Переместить ниже'
noHover noHover
title='Ниже'
className='px-0' className='px-0'
icon={<IconMoveDown size='1rem' className='icon-primary' />} icon={<IconMoveDown size='1rem' className='icon-primary' />}
onClick={() => handleMoveDown(props.row.original.id)} onClick={() => handleMoveDown(props.row.original.id)}

View File

@ -65,8 +65,8 @@ export function FormOSS() {
{...register('title')} {...register('title')}
label='Полное название' label='Полное название'
className='mb-3' className='mb-3'
disabled={!isMutable}
error={errors.title} error={errors.title}
disabled={!isMutable}
/> />
<div className='relative flex justify-between gap-3 mb-3'> <div className='relative flex justify-between gap-3 mb-3'>
<TextInput <TextInput
@ -74,8 +74,8 @@ export function FormOSS() {
{...register('alias')} {...register('alias')}
label='Сокращение' label='Сокращение'
className='w-64' className='w-64'
disabled={!isMutable}
error={errors.alias} error={errors.alias}
disabled={!isMutable}
/> />
<ToolbarItemAccess <ToolbarItemAccess
className='absolute top-18 right-2' className='absolute top-18 right-2'
@ -93,16 +93,16 @@ export function FormOSS() {
{...register('comment')} {...register('comment')}
label='Описание' label='Описание'
rows={3} rows={3}
disabled={!isMutable || isProcessing}
error={errors.comment} error={errors.comment}
disabled={!isMutable || isProcessing}
/> />
{isMutable || isModified ? ( {isMutable || isModified ? (
<SubmitButton <SubmitButton
text='Сохранить изменения' text='Сохранить изменения'
className='self-center mt-4' className='self-center mt-4'
loading={isProcessing} loading={isProcessing}
disabled={!isModified}
icon={<IconSave size='1.25rem' />} icon={<IconSave size='1.25rem' />}
disabled={!isModified}
/> />
) : null} ) : null}
</form> </form>

View File

@ -178,17 +178,18 @@ export function NodeContextMenu({ isOpen, operation, cursorX, cursorY, onHide }:
text='Редактировать' text='Редактировать'
title='Редактировать операцию' title='Редактировать операцию'
icon={<IconEdit2 size='1rem' className='icon-primary' />} icon={<IconEdit2 size='1rem' className='icon-primary' />}
disabled={!isMutable || isProcessing}
onClick={handleEditOperation} onClick={handleEditOperation}
disabled={!isMutable || isProcessing}
/> />
{operation?.result ? ( {operation?.result ? (
<DropdownButton <DropdownButton
text='Открыть схему' text='Открыть схему'
titleHtml={prepareTooltip('Открыть привязанную КС', 'Двойной клик')} titleHtml={prepareTooltip('Открыть привязанную КС', 'Двойной клик')}
aria-label='Открыть привязанную КС'
icon={<IconRSForm size='1rem' className='icon-green' />} icon={<IconRSForm size='1rem' className='icon-green' />}
disabled={isProcessing}
onClick={handleOpenSchema} onClick={handleOpenSchema}
disabled={isProcessing}
/> />
) : null} ) : null}
{isMutable && !operation?.result && operation?.arguments.length === 0 ? ( {isMutable && !operation?.result && operation?.arguments.length === 0 ? (
@ -196,8 +197,8 @@ export function NodeContextMenu({ isOpen, operation, cursorX, cursorY, onHide }:
text='Создать схему' text='Создать схему'
title='Создать пустую схему' title='Создать пустую схему'
icon={<IconNewRSForm size='1rem' className='icon-green' />} icon={<IconNewRSForm size='1rem' className='icon-green' />}
disabled={isProcessing}
onClick={handleInputCreate} onClick={handleInputCreate}
disabled={isProcessing}
/> />
) : null} ) : null}
{isMutable && operation?.operation_type === OperationType.INPUT ? ( {isMutable && operation?.operation_type === OperationType.INPUT ? (
@ -205,8 +206,8 @@ export function NodeContextMenu({ isOpen, operation, cursorX, cursorY, onHide }:
text={!operation?.result ? 'Загрузить схему' : 'Изменить схему'} text={!operation?.result ? 'Загрузить схему' : 'Изменить схему'}
title='Выбрать схему для загрузки' title='Выбрать схему для загрузки'
icon={<IconConnect size='1rem' className='icon-primary' />} icon={<IconConnect size='1rem' className='icon-primary' />}
disabled={isProcessing}
onClick={handleEditSchema} onClick={handleEditSchema}
disabled={isProcessing}
/> />
) : null} ) : null}
{isMutable && !operation?.result && operation?.operation_type === OperationType.SYNTHESIS ? ( {isMutable && !operation?.result && operation?.operation_type === OperationType.SYNTHESIS ? (
@ -217,9 +218,10 @@ export function NodeContextMenu({ isOpen, operation, cursorX, cursorY, onHide }:
? 'Активировать операцию<br/>и получить синтезированную КС' ? 'Активировать операцию<br/>и получить синтезированную КС'
: 'Необходимо предоставить все аргументы' : 'Необходимо предоставить все аргументы'
} }
aria-label='Активировать операцию и получить синтезированную КС'
icon={<IconExecute size='1rem' className='icon-green' />} icon={<IconExecute size='1rem' className='icon-green' />}
disabled={isProcessing || !readyForSynthesis}
onClick={handleOperationExecute} onClick={handleOperationExecute}
disabled={isProcessing || !readyForSynthesis}
/> />
) : null} ) : null}
@ -227,17 +229,18 @@ export function NodeContextMenu({ isOpen, operation, cursorX, cursorY, onHide }:
<DropdownButton <DropdownButton
text='Конституенты' text='Конституенты'
titleHtml='Перенос конституент</br>между схемами' titleHtml='Перенос конституент</br>между схемами'
aria-label='Перенос конституент между схемами'
icon={<IconChild size='1rem' className='icon-green' />} icon={<IconChild size='1rem' className='icon-green' />}
disabled={isProcessing}
onClick={handleRelocateConstituents} onClick={handleRelocateConstituents}
disabled={isProcessing}
/> />
) : null} ) : null}
<DropdownButton <DropdownButton
text='Удалить операцию' text='Удалить операцию'
icon={<IconDestroy size='1rem' className='icon-red' />} icon={<IconDestroy size='1rem' className='icon-red' />}
disabled={!isMutable || isProcessing || !operation || !canDelete(operation)}
onClick={handleDeleteOperation} onClick={handleDeleteOperation}
disabled={!isMutable || isProcessing || !operation || !canDelete(operation)}
/> />
</Dropdown> </Dropdown>
</div> </div>

View File

@ -143,12 +143,13 @@ export function ToolbarOssGraph({
onClick={onResetPositions} onClick={onResetPositions}
/> />
<MiniButton <MiniButton
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
title='Сбросить вид' title='Сбросить вид'
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
onClick={handleFitView} onClick={handleFitView}
/> />
<MiniButton <MiniButton
title={showGrid ? 'Скрыть сетку' : 'Отобразить сетку'} title={showGrid ? 'Скрыть сетку' : 'Отобразить сетку'}
aria-label='Переключатель отображения сетки'
icon={ icon={
showGrid ? ( showGrid ? (
<IconGrid size='1.25rem' className='icon-green' /> <IconGrid size='1.25rem' className='icon-green' />
@ -160,6 +161,7 @@ export function ToolbarOssGraph({
/> />
<MiniButton <MiniButton
title={edgeStraight ? 'Связи: прямые' : 'Связи: безье'} title={edgeStraight ? 'Связи: прямые' : 'Связи: безье'}
aria-label='Переключатель формы связей'
icon={ icon={
edgeStraight ? ( edgeStraight ? (
<IconLineStraight size='1.25rem' className='icon-primary' /> <IconLineStraight size='1.25rem' className='icon-primary' />
@ -171,6 +173,7 @@ export function ToolbarOssGraph({
/> />
<MiniButton <MiniButton
title={edgeAnimate ? 'Анимация: вкл' : 'Анимация: выкл'} title={edgeAnimate ? 'Анимация: вкл' : 'Анимация: выкл'}
aria-label='Переключатель анимации связей'
icon={ icon={
edgeAnimate ? ( edgeAnimate ? (
<IconAnimation size='1.25rem' className='icon-primary' /> <IconAnimation size='1.25rem' className='icon-primary' />
@ -186,33 +189,37 @@ export function ToolbarOssGraph({
<div className='cc-icons'> <div className='cc-icons'>
<MiniButton <MiniButton
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')} titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
aria-label='Сохранить изменения'
icon={<IconSave size='1.25rem' className='icon-primary' />} icon={<IconSave size='1.25rem' className='icon-primary' />}
disabled={isProcessing}
onClick={handleSavePositions} onClick={handleSavePositions}
disabled={isProcessing}
/> />
<MiniButton <MiniButton
titleHtml={prepareTooltip('Новая операция', 'Ctrl + Q')} titleHtml={prepareTooltip('Новая операция', 'Ctrl + Q')}
aria-label='Новая операция'
icon={<IconNewItem size='1.25rem' className='icon-green' />} icon={<IconNewItem size='1.25rem' className='icon-green' />}
disabled={isProcessing}
onClick={onCreate} onClick={onCreate}
disabled={isProcessing}
/> />
<MiniButton <MiniButton
title='Активировать операцию' title='Активировать операцию'
icon={<IconExecute size='1.25rem' className='icon-green' />} icon={<IconExecute size='1.25rem' className='icon-green' />}
disabled={isProcessing || selected.length !== 1 || !readyForSynthesis}
onClick={handleOperationExecute} onClick={handleOperationExecute}
disabled={isProcessing || selected.length !== 1 || !readyForSynthesis}
/> />
<MiniButton <MiniButton
titleHtml={prepareTooltip('Редактировать выбранную', 'Двойной клик')} titleHtml={prepareTooltip('Редактировать выбранную', 'Двойной клик')}
aria-label='Редактировать выбранную'
icon={<IconEdit2 size='1.25rem' className='icon-primary' />} icon={<IconEdit2 size='1.25rem' className='icon-primary' />}
disabled={selected.length !== 1 || isProcessing}
onClick={handleEditOperation} onClick={handleEditOperation}
disabled={selected.length !== 1 || isProcessing}
/> />
<MiniButton <MiniButton
titleHtml={prepareTooltip('Удалить выбранную', 'Delete')} titleHtml={prepareTooltip('Удалить выбранную', 'Delete')}
aria-label='Удалить выбранную'
icon={<IconDestroy size='1.25rem' className='icon-red' />} icon={<IconDestroy size='1.25rem' className='icon-red' />}
disabled={selected.length !== 1 || isProcessing || !selectedOperation || !canDelete(selectedOperation)}
onClick={onDelete} onClick={onDelete}
disabled={selected.length !== 1 || isProcessing || !selectedOperation || !canDelete(selectedOperation)}
/> />
</div> </div>
) : null} ) : null}

View File

@ -47,6 +47,7 @@ export function MenuEditOss() {
<DropdownButton <DropdownButton
text='Конституенты' text='Конституенты'
titleHtml='Перенос конституент</br>между схемами' titleHtml='Перенос конституент</br>между схемами'
aria-label='Перенос конституент между схемами'
icon={<IconChild size='1rem' className='icon-green' />} icon={<IconChild size='1rem' className='icon-green' />}
disabled={isProcessing} disabled={isProcessing}
onClick={handleRelocate} onClick={handleRelocate}

View File

@ -61,6 +61,7 @@ export function MenuMain() {
<Dropdown isOpen={schemaMenu.isOpen} margin='mt-3'> <Dropdown isOpen={schemaMenu.isOpen} margin='mt-3'>
<DropdownButton <DropdownButton
text='Поделиться' text='Поделиться'
title='Скопировать ссылку в буфер обмена'
icon={<IconShare size='1rem' className='icon-primary' />} icon={<IconShare size='1rem' className='icon-primary' />}
onClick={handleShare} onClick={handleShare}
/> />
@ -74,8 +75,8 @@ export function MenuMain() {
<DropdownButton <DropdownButton
text='Удалить схему' text='Удалить схему'
icon={<IconDestroy size='1rem' className='icon-red' />} icon={<IconDestroy size='1rem' className='icon-red' />}
disabled={isProcessing || role < UserRole.OWNER}
onClick={handleDelete} onClick={handleDelete}
disabled={isProcessing || role < UserRole.OWNER}
/> />
) : null} ) : null}

View File

@ -196,14 +196,14 @@ export function PickSubstitutions({
props.row.original.is_suggestion ? ( props.row.original.is_suggestion ? (
<div className='max-w-fit'> <div className='max-w-fit'>
<MiniButton <MiniButton
noHover
title='Принять предложение' title='Принять предложение'
noHover
icon={<IconAccept size='1rem' className='icon-green' />} icon={<IconAccept size='1rem' className='icon-green' />}
onClick={() => handleAcceptSuggestion(props.row.original)} onClick={() => handleAcceptSuggestion(props.row.original)}
/> />
<MiniButton <MiniButton
noHover
title='Игнорировать предложение' title='Игнорировать предложение'
noHover
icon={<IconRemove size='1rem' className='icon-red' />} icon={<IconRemove size='1rem' className='icon-red' />}
onClick={() => handleDeclineSuggestion(props.row.original)} onClick={() => handleDeclineSuggestion(props.row.original)}
/> />
@ -211,8 +211,8 @@ export function PickSubstitutions({
) : ( ) : (
<div className='max-w-fit'> <div className='max-w-fit'>
<MiniButton <MiniButton
noHover
title='Удалить' title='Удалить'
noHover
icon={<IconRemove size='1rem' className='icon-red' />} icon={<IconRemove size='1rem' className='icon-red' />}
onClick={() => handleDeleteSubstitution(props.row.original)} onClick={() => handleDeleteSubstitution(props.row.original)}
/> />
@ -257,8 +257,8 @@ export function PickSubstitutions({
title='Добавить в таблицу отождествлений' title='Добавить в таблицу отождествлений'
className='grow-0' className='grow-0'
icon={<IconReplace size='1.5rem' className='icon-primary' />} icon={<IconReplace size='1.5rem' className='icon-primary' />}
disabled={!leftCst || !rightCst || (leftCst === rightCst && !allowSelfSubstitution)}
onClick={addSubstitution} onClick={addSubstitution}
disabled={!leftCst || !rightCst || (leftCst === rightCst && !allowSelfSubstitution)}
/> />
</div> </div>

View File

@ -46,54 +46,55 @@ export function ToolbarGraphSelection({
return ( return (
<div className={clsx('cc-icons', className)} {...restProps}> <div className={clsx('cc-icons', className)} {...restProps}>
<MiniButton <MiniButton
titleHtml='Сбросить выделение' title='Сбросить выделение'
icon={<IconReset size='1.25rem' className='icon-primary' />} icon={<IconReset size='1.25rem' className='icon-primary' />}
onClick={() => onChange([])} onClick={() => onChange([])}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='Выделить все влияющие' title='Выделить все влияющие'
icon={<IconGraphCollapse size='1.25rem' className='icon-primary' />} icon={<IconGraphCollapse size='1.25rem' className='icon-primary' />}
onClick={() => onChange([...selected, ...graph.expandAllInputs(selected)])} onClick={() => onChange([...selected, ...graph.expandAllInputs(selected)])}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='Выделить все зависимые' title='Выделить все зависимые'
icon={<IconGraphExpand size='1.25rem' className='icon-primary' />} icon={<IconGraphExpand size='1.25rem' className='icon-primary' />}
onClick={() => onChange([...selected, ...graph.expandAllOutputs(selected)])} onClick={() => onChange([...selected, ...graph.expandAllOutputs(selected)])}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='<b>Максимизация</b> <br/>дополнение выделения конституентами, <br/>зависимыми только от выделенных' titleHtml='<b>Максимизация</b> <br/>дополнение выделения конституентами, <br/>зависимыми только от выделенных'
aria-label='Максимизация - дополнение выделения конституентами, зависимыми только от выделенных'
icon={<IconGraphMaximize size='1.25rem' className='icon-primary' />} icon={<IconGraphMaximize size='1.25rem' className='icon-primary' />}
onClick={() => onChange(graph.maximizePart(selected))} onClick={() => onChange(graph.maximizePart(selected))}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='Выделить поставщиков' title='Выделить поставщиков'
icon={<IconGraphInputs size='1.25rem' className='icon-primary' />} icon={<IconGraphInputs size='1.25rem' className='icon-primary' />}
onClick={() => onChange([...selected, ...graph.expandInputs(selected)])} onClick={() => onChange([...selected, ...graph.expandInputs(selected)])}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='Выделить потребителей' title='Выделить потребителей'
icon={<IconGraphOutputs size='1.25rem' className='icon-primary' />} icon={<IconGraphOutputs size='1.25rem' className='icon-primary' />}
onClick={() => onChange([...selected, ...graph.expandOutputs(selected)])} onClick={() => onChange([...selected, ...graph.expandOutputs(selected)])}
disabled={emptySelection} disabled={emptySelection}
/> />
<MiniButton <MiniButton
titleHtml='Инвертировать' title='Инвертировать'
icon={<IconGraphInverse size='1.25rem' className='icon-primary' />} icon={<IconGraphInverse size='1.25rem' className='icon-primary' />}
onClick={() => onChange([...graph.nodes.keys()].filter(item => !selected.includes(item)))} onClick={() => onChange([...graph.nodes.keys()].filter(item => !selected.includes(item)))}
/> />
<MiniButton <MiniButton
titleHtml='Выделить ядро' title='Выделить ядро'
icon={<IconGraphCore size='1.25rem' className='icon-primary' />} icon={<IconGraphCore size='1.25rem' className='icon-primary' />}
onClick={handleSelectCore} onClick={handleSelectCore}
/> />
{isOwned ? ( {isOwned ? (
<MiniButton <MiniButton
titleHtml='Выделить собственные' title='Выделить собственные'
icon={<IconPredecessor size='1.25rem' className='icon-primary' />} icon={<IconPredecessor size='1.25rem' className='icon-primary' />}
onClick={handleSelectOwned} onClick={handleSelectOwned}
/> />

View File

@ -43,8 +43,20 @@ export function FormCreateCst({ schema }: FormCreateCstProps) {
return ( return (
<> <>
<div className='flex items-center self-center gap-3'> <div className='flex items-center self-center gap-3'>
<SelectCstType id='dlg_cst_type' className='w-64' value={cst_type} onChange={handleTypeChange} /> <SelectCstType
<TextInput id='dlg_cst_alias' dense label='Имя' className='w-28' {...register('alias')} error={errors.alias} /> id='dlg_cst_type' //
className='w-64'
value={cst_type}
onChange={handleTypeChange}
/>
<TextInput
id='dlg_cst_alias' //
dense
label='Имя'
className='w-28'
{...register('alias')}
error={errors.alias}
/>
<BadgeHelp topic={HelpTopic.CC_CONSTITUENTA} offset={16} contentClass='sm:max-w-160' /> <BadgeHelp topic={HelpTopic.CC_CONSTITUENTA} offset={16} contentClass='sm:max-w-160' />
</div> </div>

View File

@ -133,8 +133,8 @@ export function TabArguments() {
noHover noHover
className='py-0' className='py-0'
icon={<IconAccept size='1.5rem' className='icon-green' />} icon={<IconAccept size='1.5rem' className='icon-green' />}
disabled={!argumentValue || !selectedArgument}
onClick={() => handleAssignArgument(selectedArgument!, argumentValue)} onClick={() => handleAssignArgument(selectedArgument!, argumentValue)}
disabled={!argumentValue || !selectedArgument}
/> />
<MiniButton <MiniButton
title='Очистить поле' title='Очистить поле'

View File

@ -143,18 +143,18 @@ export function DlgEditWordForms() {
/> />
<div className='flex flex-col self-center gap-1'> <div className='flex flex-col self-center gap-1'>
<MiniButton <MiniButton
noHover
title='Определить граммемы' title='Определить граммемы'
noHover
icon={<IconMoveRight size='1.25rem' className='icon-primary' />} icon={<IconMoveRight size='1.25rem' className='icon-primary' />}
disabled={isProcessing || !inputText}
onClick={handleParse} onClick={handleParse}
disabled={isProcessing || !inputText}
/> />
<MiniButton <MiniButton
noHover
title='Генерировать словоформу' title='Генерировать словоформу'
noHover
icon={<IconMoveLeft size='1.25rem' className='icon-primary' />} icon={<IconMoveLeft size='1.25rem' className='icon-primary' />}
disabled={isProcessing || inputGrams.length == 0}
onClick={handleInflect} onClick={handleInflect}
disabled={isProcessing || inputGrams.length == 0}
/> />
</div> </div>
<SelectMultiGrammeme <SelectMultiGrammeme
@ -168,29 +168,29 @@ export function DlgEditWordForms() {
<div className='flex justify-between'> <div className='flex justify-between'>
<div className='cc-icons'> <div className='cc-icons'>
<MiniButton <MiniButton
noHover
title='Внести словоформу' title='Внести словоформу'
noHover
icon={<IconAccept size='1.5rem' className='icon-green' />} icon={<IconAccept size='1.5rem' className='icon-green' />}
disabled={isProcessing || !inputText || inputGrams.length == 0}
onClick={handleAddForm} onClick={handleAddForm}
disabled={isProcessing || !inputText || inputGrams.length == 0}
/> />
<MiniButton <MiniButton
noHover
title='Генерировать стандартные словоформы' title='Генерировать стандартные словоформы'
noHover
icon={<IconMoveDown size='1.5rem' className='icon-primary' />} icon={<IconMoveDown size='1.5rem' className='icon-primary' />}
disabled={isProcessing || !inputText}
onClick={handleGenerateLexeme} onClick={handleGenerateLexeme}
disabled={isProcessing || !inputText}
/> />
</div> </div>
<div className='mt-3 mb-2 mx-auto text-sm font-semibold'> <div className='mt-3 mb-2 mx-auto text-sm font-semibold'>
<span>Заданные вручную словоформы [{forms.length}]</span> <span>Заданные вручную словоформы [{forms.length}]</span>
<MiniButton <MiniButton
noHover
title='Сбросить все словоформы' title='Сбросить все словоформы'
noHover
className='py-0 align-middle' className='py-0 align-middle'
icon={<IconRemove size='1.5rem' className='icon-red' />} icon={<IconRemove size='1.5rem' className='icon-red' />}
disabled={isProcessing || forms.length === 0}
onClick={handleResetAll} onClick={handleResetAll}
disabled={isProcessing || forms.length === 0}
/> />
</div> </div>
</div> </div>

View File

@ -47,10 +47,10 @@ export function TableWordForms({ forms, setForms, onFormSelect }: TableWordForms
size: 0, size: 0,
cell: props => ( cell: props => (
<MiniButton <MiniButton
title='Удалить словоформу'
noHover noHover
noPadding noPadding
className='align-middle' className='align-middle'
title='Удалить словоформу'
icon={<IconRemove size='1.25rem' className='icon-red' />} icon={<IconRemove size='1.25rem' className='icon-red' />}
onClick={() => handleDeleteRow(props.row.index)} onClick={() => handleDeleteRow(props.row.index)}
/> />

View File

@ -87,20 +87,20 @@ export function EditorConstituenta() {
<ToolbarConstituenta <ToolbarConstituenta
className='cc-tab-tools right-1/2 translate-x-0 xs:right-4 xs:-translate-x-1/2 md:right-1/2 md:translate-x-0 cc-animate-position' className='cc-tab-tools right-1/2 translate-x-0 xs:right-4 xs:-translate-x-1/2 md:right-1/2 md:translate-x-0 cc-animate-position'
activeCst={activeCst} activeCst={activeCst}
disabled={disabled}
onSubmit={initiateSubmit} onSubmit={initiateSubmit}
onReset={() => setToggleReset(prev => !prev)} onReset={() => setToggleReset(prev => !prev)}
disabled={disabled}
/> />
<div className='mx-0 md:mx-auto pt-8 md:w-195 shrink-0 xs:pt-0'> <div className='mx-0 md:mx-auto pt-8 md:w-195 shrink-0 xs:pt-0'>
{activeCst ? ( {activeCst ? (
<FormConstituenta <FormConstituenta
id={globalIDs.constituenta_editor} id={globalIDs.constituenta_editor}
disabled={disabled}
toggleReset={toggleReset} toggleReset={toggleReset}
activeCst={activeCst} activeCst={activeCst}
schema={schema} schema={schema}
onOpenEdit={navigateCst} onOpenEdit={navigateCst}
disabled={disabled}
/> />
) : null} ) : null}
</div> </div>

View File

@ -136,7 +136,8 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
<form id={id} className='relative cc-column mt-1 px-6 py-1' onSubmit={event => void handleSubmit(onSubmit)(event)}> <form id={id} className='relative cc-column mt-1 px-6 py-1' onSubmit={event => void handleSubmit(onSubmit)(event)}>
{!disabled || isProcessing ? ( {!disabled || isProcessing ? (
<MiniButton <MiniButton
title={isModified ? tooltipText.unsaved : `Редактировать словоформы термина`} title={isModified ? tooltipText.unsaved : 'Редактировать словоформы термина'}
aria-label='Редактировать словоформы термина'
noHover noHover
onClick={handleEditTermForms} onClick={handleEditTermForms}
className='absolute z-pop top-0 left-[calc(7ch+4px)]' className='absolute z-pop top-0 left-[calc(7ch+4px)]'
@ -152,8 +153,9 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
</div> </div>
{!disabled || isProcessing ? ( {!disabled || isProcessing ? (
<MiniButton <MiniButton
noHover
title={isModified ? tooltipText.unsaved : 'Переименовать конституенту'} title={isModified ? tooltipText.unsaved : 'Переименовать конституенту'}
aria-label='Переименовать конституенту'
noHover
onClick={handleRenameCst} onClick={handleRenameCst}
icon={<IconEdit size='1rem' className='icon-primary' />} icon={<IconEdit size='1rem' className='icon-primary' />}
disabled={isModified} disabled={isModified}
@ -175,8 +177,8 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
value={field.value ?? ''} value={field.value ?? ''}
initialValue={activeCst.term_raw} initialValue={activeCst.term_raw}
resolved={activeCst.term_resolved} resolved={activeCst.term_resolved}
disabled={disabled}
onChange={newValue => field.onChange(newValue)} onChange={newValue => field.onChange(newValue)}
disabled={disabled}
/> />
)} )}
/> />
@ -214,12 +216,12 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
} }
value={field.value ?? ''} value={field.value ?? ''}
activeCst={activeCst} activeCst={activeCst}
disabled={disabled || activeCst.is_inherited}
toggleReset={toggleReset} toggleReset={toggleReset}
onChange={newValue => field.onChange(newValue)} onChange={newValue => field.onChange(newValue)}
onChangeLocalParse={setLocalParse} onChangeLocalParse={setLocalParse}
onOpenEdit={onOpenEdit} onOpenEdit={onOpenEdit}
onShowTypeGraph={handleTypeGraph} onShowTypeGraph={handleTypeGraph}
disabled={disabled || activeCst.is_inherited}
/> />
)} )}
/> />
@ -240,8 +242,8 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
value={field.value ?? ''} value={field.value ?? ''}
initialValue={activeCst.definition_raw} initialValue={activeCst.definition_raw}
resolved={activeCst.definition_resolved} resolved={activeCst.definition_resolved}
disabled={disabled}
onChange={newValue => field.onChange(newValue)} onChange={newValue => field.onChange(newValue)}
disabled={disabled}
/> />
)} )}
/> />
@ -275,8 +277,8 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
<div className='relative mx-auto flex'> <div className='relative mx-auto flex'>
<SubmitButton <SubmitButton
text='Сохранить изменения' text='Сохранить изменения'
disabled={disabled || !isModified}
icon={<IconSave size='1.25rem' />} icon={<IconSave size='1.25rem' />}
disabled={disabled || !isModified}
/> />
<div className='absolute z-pop top-1/2 -translate-y-1/2 left-full cc-icons'> <div className='absolute z-pop top-1/2 -translate-y-1/2 left-full cc-icons'>
{activeCst.has_inherited_children && !activeCst.is_inherited ? ( {activeCst.has_inherited_children && !activeCst.is_inherited ? (

View File

@ -98,33 +98,35 @@ export function ToolbarConstituenta({
<> <>
<MiniButton <MiniButton
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')} titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
aria-label='Сохранить изменения'
icon={<IconSave size='1.25rem' className='icon-primary' />} icon={<IconSave size='1.25rem' className='icon-primary' />}
disabled={disabled || !isModified}
onClick={onSubmit} onClick={onSubmit}
disabled={disabled || !isModified}
/> />
<MiniButton <MiniButton
title='Сбросить несохраненные изменения' title='Сбросить несохраненные изменения'
icon={<IconReset size='1.25rem' className='icon-primary' />} icon={<IconReset size='1.25rem' className='icon-primary' />}
disabled={disabled || !isModified}
onClick={onReset} onClick={onReset}
disabled={disabled || !isModified}
/> />
<MiniButton <MiniButton
title='Создать конституенту после данной' title='Создать конституенту после данной'
icon={<IconNewItem size='1.25rem' className='icon-green' />} icon={<IconNewItem size='1.25rem' className='icon-green' />}
disabled={!isContentEditable || isProcessing}
onClick={() => (activeCst ? createCst(activeCst.cst_type, false) : createCstDefault())} onClick={() => (activeCst ? createCst(activeCst.cst_type, false) : createCstDefault())}
disabled={!isContentEditable || isProcessing}
/> />
<MiniButton <MiniButton
titleHtml={isModified ? tooltipText.unsaved : prepareTooltip('Клонировать конституенту', 'Alt + V')} titleHtml={isModified ? tooltipText.unsaved : prepareTooltip('Клонировать конституенту', 'Alt + V')}
aria-label='Клонировать конституенту'
icon={<IconClone size='1.25rem' className='icon-green' />} icon={<IconClone size='1.25rem' className='icon-green' />}
disabled={disabled || isModified}
onClick={cloneCst} onClick={cloneCst}
disabled={disabled || isModified}
/> />
<MiniButton <MiniButton
title='Удалить редактируемую конституенту' title='Удалить редактируемую конституенту'
disabled={disabled || !canDeleteSelected}
onClick={promptDeleteCst} onClick={promptDeleteCst}
icon={<IconDestroy size='1.25rem' className='icon-red' />} icon={<IconDestroy size='1.25rem' className='icon-red' />}
disabled={disabled || !canDeleteSelected}
/> />
</> </>
) : null} ) : null}
@ -139,15 +141,17 @@ export function ToolbarConstituenta({
<> <>
<MiniButton <MiniButton
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')} titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
aria-label='Переместить вверх'
icon={<IconMoveUp size='1.25rem' className='icon-primary' />} icon={<IconMoveUp size='1.25rem' className='icon-primary' />}
disabled={disabled || isModified || schema.items.length < 2}
onClick={moveUp} onClick={moveUp}
disabled={disabled || isModified || schema.items.length < 2}
/> />
<MiniButton <MiniButton
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')} titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
aria-label='Переместить вниз'
icon={<IconMoveDown size='1.25rem' className='icon-primary' />} icon={<IconMoveDown size='1.25rem' className='icon-primary' />}
disabled={disabled || isModified || schema.items.length < 2}
onClick={moveDown} onClick={moveDown}
disabled={disabled || isModified || schema.items.length < 2}
/> />
</> </>
) : null} ) : null}

View File

@ -157,9 +157,9 @@ export function EditorRSExpression({
<div className='relative cc-fade-in'> <div className='relative cc-fade-in'>
<ToolbarRSExpression <ToolbarRSExpression
className='absolute -top-2 right-0' className='absolute -top-2 right-0'
disabled={disabled}
showAST={handleShowAST} showAST={handleShowAST}
showTypeGraph={onShowTypeGraph} showTypeGraph={onShowTypeGraph}
disabled={disabled}
/> />
<StatusBar <StatusBar
@ -174,27 +174,27 @@ export function EditorRSExpression({
<RSInput <RSInput
ref={rsInput} ref={rsInput}
value={value} value={value}
schema={schema}
minHeight='3.75rem' minHeight='3.75rem'
maxHeight='8rem' maxHeight='8rem'
disabled={disabled}
onChange={handleChange} onChange={handleChange}
onAnalyze={handleCheckExpression} onAnalyze={handleCheckExpression}
schema={schema}
onOpenEdit={onOpenEdit} onOpenEdit={onOpenEdit}
disabled={disabled}
{...restProps} {...restProps}
/> />
<RSEditorControls <RSEditorControls
isOpen={showControls && (!disabled || (isProcessing && !activeCst.is_inherited))} isOpen={showControls && (!disabled || (isProcessing && !activeCst.is_inherited))}
disabled={disabled}
onEdit={handleEdit} onEdit={handleEdit}
disabled={disabled}
/> />
<ParsingResult <ParsingResult
isOpen={!!parseData && parseData.errors.length > 0} isOpen={!!parseData && parseData.errors.length > 0}
data={parseData} data={parseData}
disabled={disabled}
onShowError={error => onShowError(error, parseData?.prefixLen ?? 0)} onShowError={error => onShowError(error, parseData?.prefixLen ?? 0)}
disabled={disabled}
/> />
</div> </div>
); );

View File

@ -108,9 +108,9 @@ export function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsP
key={`${prefixes.rsedit_btn}${hotkey}`} key={`${prefixes.rsedit_btn}${hotkey}`}
text={text} text={text}
titleHtml={`<kbd>[${hotkey}]</kbd>`} titleHtml={`<kbd>[${hotkey}]</kbd>`}
className='hidden xs:inline'
onInsert={onEdit} onInsert={onEdit}
disabled={disabled} disabled={disabled}
className='hidden xs:inline'
/> />
))} ))}

View File

@ -17,7 +17,6 @@ export function RSTokenButton({ token, disabled, onInsert }: RSTokenButtonProps)
<button <button
type='button' type='button'
tabIndex={-1} tabIndex={-1}
disabled={disabled}
onClick={() => onInsert(token)} onClick={() => onInsert(token)}
className={clsx( className={clsx(
'h-5 sm:h-6', 'h-5 sm:h-6',
@ -30,6 +29,7 @@ export function RSTokenButton({ token, disabled, onInsert }: RSTokenButtonProps)
)} )}
data-tooltip-id={globalIDs.tooltip} data-tooltip-id={globalIDs.tooltip}
data-tooltip-html={describeToken(token)} data-tooltip-html={describeToken(token)}
disabled={disabled}
> >
{label ? <span className='whitespace-nowrap'>{label}</span> : null} {label ? <span className='whitespace-nowrap'>{label}</span> : null}
</button> </button>

View File

@ -78,8 +78,8 @@ export function FormRSForm() {
{...register('title')} {...register('title')}
label='Полное название' label='Полное название'
className='mb-3' className='mb-3'
disabled={!isContentEditable}
error={errors.title} error={errors.title}
disabled={!isContentEditable}
/> />
<div className='flex justify-between gap-3 mb-3'> <div className='flex justify-between gap-3 mb-3'>
<TextInput <TextInput
@ -87,8 +87,8 @@ export function FormRSForm() {
{...register('alias')} {...register('alias')}
label='Сокращение' label='Сокращение'
className='w-64' className='w-64'
disabled={!isContentEditable}
error={errors.alias} error={errors.alias}
disabled={!isContentEditable}
/> />
<div className='relative flex flex-col gap-2'> <div className='relative flex flex-col gap-2'>
<ToolbarVersioning <ToolbarVersioning
@ -122,16 +122,16 @@ export function FormRSForm() {
{...register('comment')} {...register('comment')}
label='Описание' label='Описание'
rows={3} rows={3}
disabled={!isContentEditable || isProcessing}
error={errors.comment} error={errors.comment}
disabled={!isContentEditable || isProcessing}
/> />
{isContentEditable || isDirty ? ( {isContentEditable || isDirty ? (
<SubmitButton <SubmitButton
text='Сохранить изменения' text='Сохранить изменения'
className='self-center mt-4' className='self-center mt-4'
loading={isProcessing} loading={isProcessing}
disabled={!isDirty}
icon={<IconSave size='1.25rem' />} icon={<IconSave size='1.25rem' />}
disabled={!isDirty}
/> />
) : null} ) : null}
</form> </form>

View File

@ -69,21 +69,23 @@ export function ToolbarVersioning({ blockReload, className }: ToolbarVersioningP
? 'Откатить к версии' ? 'Откатить к версии'
: 'Переключитесь на <br/>неактуальную версию' : 'Переключитесь на <br/>неактуальную версию'
} }
disabled={isContentEditable || blockReload} aria-label='Откатить к выбранной версии'
onClick={handleRestoreVersion} onClick={handleRestoreVersion}
icon={<IconUpload size='1.25rem' className='icon-red' />} icon={<IconUpload size='1.25rem' className='icon-red' />}
disabled={isContentEditable || blockReload}
/> />
<MiniButton <MiniButton
titleHtml={isContentEditable ? 'Создать версию' : 'Переключитесь <br/>на актуальную версию'} titleHtml={isContentEditable ? 'Создать версию' : 'Переключитесь <br/>на актуальную версию'}
disabled={!isContentEditable} aria-label={isContentEditable ? 'Создать версию' : 'Переключить на актуальную версию'}
onClick={handleCreateVersion} onClick={handleCreateVersion}
icon={<IconNewVersion size='1.25rem' className='icon-green' />} icon={<IconNewVersion size='1.25rem' className='icon-green' />}
disabled={!isContentEditable}
/> />
<MiniButton <MiniButton
title={schema.versions.length === 0 ? 'Список версий пуст' : 'Редактировать версии'} title={schema.versions.length === 0 ? 'Список версий пуст' : 'Редактировать версии'}
disabled={schema.versions.length === 0}
onClick={handleEditVersions} onClick={handleEditVersions}
icon={<IconVersions size='1.25rem' className='icon-primary' />} icon={<IconVersions size='1.25rem' className='icon-primary' />}
disabled={schema.versions.length === 0}
/> />
</> </>
) : null} ) : null}

View File

@ -55,29 +55,32 @@ export function ToolbarRSList({ className }: ToolbarRSListProps) {
) : null} ) : null}
<MiniButton <MiniButton
titleHtml={prepareTooltip('Сбросить выделение', 'ESC')} titleHtml={prepareTooltip('Сбросить выделение', 'ESC')}
aria-label='Сбросить выделение'
icon={<IconReset size='1.25rem' className='icon-primary' />} icon={<IconReset size='1.25rem' className='icon-primary' />}
disabled={selected.length === 0}
onClick={deselectAll} onClick={deselectAll}
disabled={selected.length === 0}
/> />
<MiniButton <MiniButton
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')} titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
aria-label='Переместить вверх'
icon={<IconMoveUp size='1.25rem' className='icon-primary' />} icon={<IconMoveUp size='1.25rem' className='icon-primary' />}
disabled={isProcessing || selected.length === 0 || selected.length === schema.items.length}
onClick={moveUp} onClick={moveUp}
disabled={isProcessing || selected.length === 0 || selected.length === schema.items.length}
/> />
<MiniButton <MiniButton
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')} titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
aria-label='Переместить вниз'
icon={<IconMoveDown size='1.25rem' className='icon-primary' />} icon={<IconMoveDown size='1.25rem' className='icon-primary' />}
disabled={isProcessing || selected.length === 0 || selected.length === schema.items.length}
onClick={moveDown} onClick={moveDown}
disabled={isProcessing || selected.length === 0 || selected.length === schema.items.length}
/> />
<div ref={insertMenu.ref} className='relative'> <div ref={insertMenu.ref} className='relative'>
<MiniButton <MiniButton
title='Добавить пустую конституенту' title='Добавить пустую конституенту'
hideTitle={insertMenu.isOpen} hideTitle={insertMenu.isOpen}
icon={<IconOpenList size='1.25rem' className='icon-green' />} icon={<IconOpenList size='1.25rem' className='icon-green' />}
disabled={isProcessing}
onClick={insertMenu.toggle} onClick={insertMenu.toggle}
disabled={isProcessing}
/> />
<Dropdown isOpen={insertMenu.isOpen} className='-translate-x-1/2'> <Dropdown isOpen={insertMenu.isOpen} className='-translate-x-1/2'>
{Object.values(CstType).map(typeStr => ( {Object.values(CstType).map(typeStr => (
@ -93,21 +96,24 @@ export function ToolbarRSList({ className }: ToolbarRSListProps) {
</div> </div>
<MiniButton <MiniButton
titleHtml={prepareTooltip('Добавить новую конституенту...', 'Alt + `')} titleHtml={prepareTooltip('Добавить новую конституенту...', 'Alt + `')}
aria-label='Добавить новую конституенту'
icon={<IconNewItem size='1.25rem' className='icon-green' />} icon={<IconNewItem size='1.25rem' className='icon-green' />}
disabled={isProcessing}
onClick={createCstDefault} onClick={createCstDefault}
disabled={isProcessing}
/> />
<MiniButton <MiniButton
titleHtml={prepareTooltip('Клонировать конституенту', 'Alt + V')} titleHtml={prepareTooltip('Клонировать конституенту', 'Alt + V')}
aria-label='Клонировать конституенту'
icon={<IconClone size='1.25rem' className='icon-green' />} icon={<IconClone size='1.25rem' className='icon-green' />}
disabled={isProcessing || selected.length !== 1}
onClick={cloneCst} onClick={cloneCst}
disabled={isProcessing || selected.length !== 1}
/> />
<MiniButton <MiniButton
titleHtml={prepareTooltip('Удалить выбранные', 'Delete')} titleHtml={prepareTooltip('Удалить выбранные', 'Delete')}
aria-label='Удалить выбранные'
icon={<IconDestroy size='1.25rem' className='icon-red' />} icon={<IconDestroy size='1.25rem' className='icon-red' />}
disabled={isProcessing || !canDeleteSelected}
onClick={promptDeleteCst} onClick={promptDeleteCst}
disabled={isProcessing || !canDeleteSelected}
/> />
<BadgeHelp topic={HelpTopic.UI_RS_LIST} offset={5} /> <BadgeHelp topic={HelpTopic.UI_RS_LIST} offset={5} />
</div> </div>

View File

@ -44,7 +44,7 @@ export function ToolbarFocusedCst() {
</div> </div>
<MiniButton <MiniButton
titleHtml='Сбросить фокус' title='Сбросить фокус'
icon={<IconReset size='1.25rem' className='icon-primary' />} icon={<IconReset size='1.25rem' className='icon-primary' />}
onClick={resetSelection} onClick={resetSelection}
/> />

View File

@ -99,8 +99,8 @@ export function ToolbarTermGraph() {
onClick={showParams} onClick={showParams}
/> />
<MiniButton <MiniButton
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
title='Граф целиком' title='Граф целиком'
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
onClick={handleFitView} onClick={handleFitView}
/> />
<MiniButton <MiniButton
@ -129,16 +129,16 @@ export function ToolbarTermGraph() {
<MiniButton <MiniButton
title='Новая конституента' title='Новая конституента'
icon={<IconNewItem size='1.25rem' className='icon-green' />} icon={<IconNewItem size='1.25rem' className='icon-green' />}
disabled={isProcessing}
onClick={handleCreateCst} onClick={handleCreateCst}
disabled={isProcessing}
/> />
) : null} ) : null}
{isContentEditable ? ( {isContentEditable ? (
<MiniButton <MiniButton
title='Удалить выбранные' title='Удалить выбранные'
icon={<IconDestroy size='1.25rem' className='icon-red' />} icon={<IconDestroy size='1.25rem' className='icon-red' />}
disabled={!canDeleteSelected || isProcessing}
onClick={handleDeleteCst} onClick={handleDeleteCst}
disabled={!canDeleteSelected || isProcessing}
/> />
) : null} ) : null}
<MiniButton <MiniButton

View File

@ -135,15 +135,16 @@ export function MenuEditSchema() {
text='Шаблоны' text='Шаблоны'
title='Создать конституенту из шаблона' title='Создать конституенту из шаблона'
icon={<IconTemplates size='1rem' className='icon-green' />} icon={<IconTemplates size='1rem' className='icon-green' />}
disabled={!isContentEditable || isProcessing}
onClick={handleTemplates} onClick={handleTemplates}
disabled={!isContentEditable || isProcessing}
/> />
<DropdownButton <DropdownButton
text='Встраивание' text='Встраивание'
titleHtml='Импортировать совокупность <br/>конституент из другой схемы' titleHtml='Импортировать совокупность <br/>конституент из другой схемы'
aria-label='Импортировать совокупность конституент из другой схемы'
icon={<IconInlineSynthesis size='1rem' className='icon-green' />} icon={<IconInlineSynthesis size='1rem' className='icon-green' />}
disabled={!isContentEditable || isProcessing}
onClick={handleInlineSynthesis} onClick={handleInlineSynthesis}
disabled={!isContentEditable || isProcessing}
/> />
<Divider margins='mx-3 my-1' /> <Divider margins='mx-3 my-1' />
@ -151,27 +152,31 @@ export function MenuEditSchema() {
<DropdownButton <DropdownButton
text='Упорядочить список' text='Упорядочить список'
titleHtml='Упорядочить список, исходя из <br/>логики типов и связей конституент' titleHtml='Упорядочить список, исходя из <br/>логики типов и связей конституент'
aria-label='Упорядочить список, исходя из логики типов и связей конституент'
icon={<IconSortList size='1rem' className='icon-primary' />} icon={<IconSortList size='1rem' className='icon-primary' />}
disabled={!isContentEditable || isProcessing}
onClick={handleRestoreOrder} onClick={handleRestoreOrder}
disabled={!isContentEditable || isProcessing}
/> />
<DropdownButton <DropdownButton
text='Порядковые имена' text='Порядковые имена'
titleHtml='Присвоить порядковые имена <br/>и обновить выражения' titleHtml='Присвоить порядковые имена <br/>и обновить выражения'
aria-label='Присвоить порядковые имена и обновить выражения'
icon={<IconGenerateNames size='1rem' className='icon-primary' />} icon={<IconGenerateNames size='1rem' className='icon-primary' />}
disabled={!isContentEditable || isProcessing}
onClick={handleReindex} onClick={handleReindex}
disabled={!isContentEditable || isProcessing}
/> />
<DropdownButton <DropdownButton
text='Порождение структуры' text='Порождение структуры'
titleHtml='Раскрыть структуру типизации <br/>выделенной конституенты' titleHtml='Раскрыть структуру типизации <br/>выделенной конституенты'
aria-label='Раскрыть структуру типизации выделенной конституенты'
icon={<IconGenerateStructure size='1rem' className='icon-primary' />} icon={<IconGenerateStructure size='1rem' className='icon-primary' />}
disabled={!isContentEditable || isProcessing || !activeCst || !canProduceStructure(activeCst)}
onClick={() => handleProduceStructure(activeCst)} onClick={() => handleProduceStructure(activeCst)}
disabled={!isContentEditable || isProcessing || !activeCst || !canProduceStructure(activeCst)}
/> />
<DropdownButton <DropdownButton
text='Отождествление' text='Отождествление'
titleHtml='Заменить вхождения <br/>одной конституенты на другую' titleHtml='Заменить вхождения <br/>одной конституенты на другую'
aria-label='Заменить вхождения одной конституенты на другую'
icon={<IconReplace size='1rem' className='icon-red' />} icon={<IconReplace size='1rem' className='icon-red' />}
onClick={handleSubstituteCst} onClick={handleSubstituteCst}
disabled={!isContentEditable || isProcessing} disabled={!isContentEditable || isProcessing}

View File

@ -129,6 +129,7 @@ export function MenuMain() {
<DropdownButton <DropdownButton
text='Поделиться' text='Поделиться'
titleHtml={tooltipText.shareItem(schema.access_policy === AccessPolicy.PUBLIC)} titleHtml={tooltipText.shareItem(schema.access_policy === AccessPolicy.PUBLIC)}
aria-label='Скопировать ссылку в буфер обмена'
icon={<IconShare size='1rem' className='icon-primary' />} icon={<IconShare size='1rem' className='icon-primary' />}
onClick={handleShare} onClick={handleShare}
disabled={schema.access_policy !== AccessPolicy.PUBLIC} disabled={schema.access_policy !== AccessPolicy.PUBLIC}

View File

@ -41,6 +41,7 @@ export function ConstituentsSearch({ dense }: ConstituentsSearchProps) {
<MiniButton <MiniButton
noHover noHover
titleHtml={`Наследованные: <b>${includeInherited ? 'отображать' : 'скрывать'}</b>`} titleHtml={`Наследованные: <b>${includeInherited ? 'отображать' : 'скрывать'}</b>`}
aria-label={`Отображение наследованных: ${includeInherited ? 'отображать' : 'скрывать'}`}
icon={<IconChild size='1rem' className={includeInherited ? 'icon-primary' : 'clr-text-controls'} />} icon={<IconChild size='1rem' className={includeInherited ? 'icon-primary' : 'clr-text-controls'} />}
className='h-fit self-center' className='h-fit self-center'
onClick={toggleInherited} onClick={toggleInherited}

View File

@ -46,10 +46,10 @@ export function SelectGraphFilter({ value, dense, className, onChange, ...restPr
const source = value as DependencyMode; const source = value as DependencyMode;
return ( return (
<DropdownButton <DropdownButton
className={!dense ? 'w-72' : undefined}
key={`${prefixes.cst_source_list}${index}`} key={`${prefixes.cst_source_list}${index}`}
onClick={() => handleChange(source)} className={!dense ? 'w-72' : undefined}
icon={<IconDependencyMode value={source} size='1rem' />} icon={<IconDependencyMode value={source} size='1rem' />}
onClick={() => handleChange(source)}
> >
{!dense ? ( {!dense ? (
<span> <span>

View File

@ -45,10 +45,10 @@ export function SelectMatchMode({ value, dense, className, onChange, ...restProp
const matchMode = value as CstMatchMode; const matchMode = value as CstMatchMode;
return ( return (
<DropdownButton <DropdownButton
className={!dense ? 'w-80' : undefined}
key={`${prefixes.cst_source_list}${index}`} key={`${prefixes.cst_source_list}${index}`}
onClick={() => handleChange(matchMode)} className={!dense ? 'w-80' : undefined}
icon={<IconCstMatchMode value={matchMode} size='1rem' />} icon={<IconCstMatchMode value={matchMode} size='1rem' />}
onClick={() => handleChange(matchMode)}
> >
{!dense ? ( {!dense ? (
<span> <span>