F: Reworking colors and hovering pt1
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
Frontend CI / notify-failure (push) Blocked by required conditions

This commit is contained in:
Ivan 2025-06-18 16:14:41 +03:00
parent fb705249bb
commit 2013dca777
35 changed files with 149 additions and 173 deletions

View File

@ -107,7 +107,8 @@ export { BiFontFamily as IconText } from 'react-icons/bi';
export { BiFont as IconTextOff } from 'react-icons/bi'; export { BiFont as IconTextOff } from 'react-icons/bi';
export { TbCircleLetterM as IconTypeGraph } from 'react-icons/tb'; export { TbCircleLetterM as IconTypeGraph } from 'react-icons/tb';
export { RiTreeLine as IconTree } from 'react-icons/ri'; export { RiTreeLine as IconTree } from 'react-icons/ri';
export { FaRegKeyboard as IconControls } from 'react-icons/fa6'; export { LuKeyboard as IconKeyboard } from 'react-icons/lu';
export { LuKeyboardOff as IconKeyboardOff } from 'react-icons/lu';
export { RiLockLine as IconImmutable } from 'react-icons/ri'; export { RiLockLine as IconImmutable } from 'react-icons/ri';
export { RiLockUnlockLine as IconMutable } from 'react-icons/ri'; export { RiLockUnlockLine as IconMutable } from 'react-icons/ri';
export { RiOpenSourceLine as IconPublic } from 'react-icons/ri'; export { RiOpenSourceLine as IconPublic } from 'react-icons/ri';

View File

@ -8,7 +8,7 @@ export function ModalBackdrop({ onHide }: ModalBackdropProps) {
return ( return (
<> <>
<div className='z-bottom fixed inset-0 backdrop-blur-[3px] opacity-50' /> <div className='z-bottom fixed inset-0 backdrop-blur-[3px] opacity-50' />
<div className='z-bottom fixed inset-0 bg-popover opacity-25' onClick={onHide} /> <div className='z-bottom fixed inset-0 bg-foreground opacity-5' onClick={onHide} />
</> </>
); );
} }

View File

@ -89,7 +89,7 @@ export function ModalForm({
<div className='cc-modal-wrapper'> <div className='cc-modal-wrapper'>
<ModalBackdrop onHide={handleCancel} /> <ModalBackdrop onHide={handleCancel} />
<form <form
className='cc-animate-modal relative grid border rounded-xl bg-background' className='cc-animate-modal relative grid border-2 px-1 pb-1 rounded-xl bg-background'
role='dialog' role='dialog'
onSubmit={handleSubmit} onSubmit={handleSubmit}
aria-labelledby='modal-title' aria-labelledby='modal-title'

View File

@ -6,7 +6,7 @@ export function ModalLoader() {
return ( return (
<div className='cc-modal-wrapper'> <div className='cc-modal-wrapper'>
<ModalBackdrop /> <ModalBackdrop />
<div className='cc-animate-modal p-20 border rounded-xl bg-background'> <div className='cc-animate-modal p-20 border-2 rounded-xl bg-background'>
<Loader circular scale={6} /> <Loader circular scale={6} />
</div> </div>
</div> </div>

View File

@ -39,7 +39,7 @@ export function ModalView({
return ( return (
<div className='cc-modal-wrapper'> <div className='cc-modal-wrapper'>
<ModalBackdrop onHide={hideDialog} /> <ModalBackdrop onHide={hideDialog} />
<div className='cc-animate-modal relative grid border rounded-xl bg-background' role='dialog'> <div className='cc-animate-modal relative grid border-2 px-1 pb-1 rounded-xl bg-background' role='dialog'>
{helpTopic && !hideHelpWhen?.() ? ( {helpTopic && !hideHelpWhen?.() ? (
<BadgeHelp <BadgeHelp
topic={helpTopic} topic={helpTopic}

View File

@ -22,6 +22,7 @@ export function TabLabel({
className, className,
disabled, disabled,
role = 'tab', role = 'tab',
selectedClassName = 'text-foreground! cc-selected',
...otherProps ...otherProps
}: TabLabelProps) { }: TabLabelProps) {
return ( return (
@ -29,12 +30,12 @@ export function TabLabel({
className={clsx( className={clsx(
'min-w-20 h-full', 'min-w-20 h-full',
'px-2 py-1 flex justify-center', 'px-2 py-1 flex justify-center',
'cc-animate-color duration-select', 'cc-animate-color duration-select text-muted-foreground',
'text-sm whitespace-nowrap font-controls', 'text-sm whitespace-nowrap font-controls',
'select-none', 'select-none',
'outline-hidden', 'outline-hidden',
!disabled && 'hover:cursor-pointer cc-hover-bg', !disabled && 'hover:cursor-pointer cc-hover-text',
disabled && 'text-muted-foreground', disabled && 'bg-secondary',
className className
)} )}
tabIndex='-1' tabIndex='-1'
@ -44,6 +45,7 @@ export function TabLabel({
data-tooltip-hidden={hideTitle} data-tooltip-hidden={hideTitle}
role={role} role={role}
disabled={disabled} disabled={disabled}
selectedClassName={selectedClassName}
{...otherProps} {...otherProps}
> >
{label} {label}

View File

@ -62,7 +62,7 @@ export function HelpLibrary() {
<IconFilterReset size='1rem' className='inline-icon' /> сбросить фильтры <IconFilterReset size='1rem' className='inline-icon' /> сбросить фильтры
</li> </li>
<li> <li>
<IconFolderTree size='1rem' className='inline-icon' /> переключение между Проводник и Поиск <IconFolderTree size='1rem' className='inline-icon' /> переключение между Проводник и Таблица
</li> </li>
</ul> </ul>

View File

@ -1,10 +1,10 @@
import { import {
IconChild, IconChild,
IconClone, IconClone,
IconControls,
IconDestroy, IconDestroy,
IconEdit, IconEdit,
IconFilter, IconFilter,
IconKeyboard,
IconList, IconList,
IconMoveDown, IconMoveDown,
IconMoveUp, IconMoveUp,
@ -94,7 +94,7 @@ export function HelpRSEditor() {
<IconStatusOK className='inline-icon' /> индикатор статуса определения сверху <IconStatusOK className='inline-icon' /> индикатор статуса определения сверху
</li> </li>
<li> <li>
<IconControls className='inline-icon' /> специальная клавиатура и горячие клавиши <IconKeyboard className='inline-icon' /> специальная клавиатура и горячие клавиши
</li> </li>
<li> <li>
<IconTypeGraph className='inline-icon' /> отображение{' '} <IconTypeGraph className='inline-icon' /> отображение{' '}

View File

@ -137,14 +137,14 @@ export function EditorLibraryItem({ schema, isAttachedToOSS }: EditorLibraryItem
<ValueIcon <ValueIcon
title='Дата обновления' title='Дата обновления'
dense dense
icon={<IconDateUpdate size='1.25rem' className='text-constructive' />} icon={<IconDateUpdate size='1.25rem' />}
value={new Date(schema.time_update).toLocaleString(intl.locale)} value={new Date(schema.time_update).toLocaleString(intl.locale)}
/> />
<ValueIcon <ValueIcon
title='Дата создания' title='Дата создания'
dense dense
icon={<IconDateCreate size='1.25rem' className='text-constructive' />} icon={<IconDateCreate size='1.25rem' />}
value={new Date(schema.time_create).toLocaleString(intl.locale, { value={new Date(schema.time_create).toLocaleString(intl.locale, {
year: '2-digit', year: '2-digit',
month: '2-digit', month: '2-digit',

View File

@ -1,21 +1,16 @@
import { UserRole } from '@/features/users'; import { UserRole } from '@/features/users';
import { IconAdmin, IconEditor, IconOwner, IconReader } from '@/components/icons'; import { type DomIconProps, IconAdmin, IconEditor, IconOwner, IconReader } from '@/components/icons';
interface IconRoleProps { export function IconRole({ value, size = '1.25rem', className }: DomIconProps<UserRole>) {
role: UserRole; switch (value) {
size?: string;
}
export function IconRole({ role, size = '1.25rem' }: IconRoleProps) {
switch (role) {
case UserRole.ADMIN: case UserRole.ADMIN:
return <IconAdmin size={size} className='icon-primary' />; return <IconAdmin size={size} className={className ?? 'icon-primary'} />;
case UserRole.OWNER: case UserRole.OWNER:
return <IconOwner size={size} className='icon-primary' />; return <IconOwner size={size} className={className ?? 'icon-primary'} />;
case UserRole.EDITOR: case UserRole.EDITOR:
return <IconEditor size={size} className='icon-primary' />; return <IconEditor size={size} className={className ?? 'icon-primary'} />;
case UserRole.READER: case UserRole.READER:
return <IconReader size={size} className='icon-primary' />; return <IconReader size={size} className={className ?? 'icon-primary'} />;
} }
} }

View File

@ -3,7 +3,7 @@ import { useAuthSuspense } from '@/features/auth';
import { useRoleStore, UserRole } from '@/features/users'; import { useRoleStore, UserRole } from '@/features/users';
import { describeUserRole, labelUserRole } from '@/features/users/labels'; import { describeUserRole, labelUserRole } from '@/features/users/labels';
import { Button } from '@/components/control'; import { MiniButton } from '@/components/control';
import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown'; import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown';
import { IconAlert } from '@/components/icons'; import { IconAlert } from '@/components/icons';
@ -29,13 +29,12 @@ export function MenuRole({ isOwned, isEditor }: MenuRoleProps) {
if (isAnonymous) { if (isAnonymous) {
return ( return (
<Button <MiniButton
dense noHover
noBorder noPadding
noOutline
titleHtml='<b>Анонимный режим</b><br />Войти в Портал' titleHtml='<b>Анонимный режим</b><br />Войти в Портал'
hideTitle={accessMenu.isOpen} hideTitle={accessMenu.isOpen}
className='h-full pr-2' className='h-full pr-2 bg-transparent'
icon={<IconAlert size='1.25rem' className='icon-red' />} icon={<IconAlert size='1.25rem' className='icon-red' />}
onClick={() => router.push({ path: urls.login })} onClick={() => router.push({ path: urls.login })}
/> />
@ -44,44 +43,45 @@ export function MenuRole({ isOwned, isEditor }: MenuRoleProps) {
return ( return (
<div ref={accessMenu.ref} onBlur={accessMenu.handleBlur} className='relative'> <div ref={accessMenu.ref} onBlur={accessMenu.handleBlur} className='relative'>
<Button <MiniButton
dense noHover
noBorder noPadding
noOutline
title={`Режим ${labelUserRole(role)}`} title={`Режим ${labelUserRole(role)}`}
hideTitle={accessMenu.isOpen} hideTitle={accessMenu.isOpen}
className='h-full pr-2' className='h-full pr-2 bg-transparent text-muted-foreground hover:text-primary'
icon={<IconRole role={role} size='1.25rem' />} icon={<IconRole value={role} size='1.25rem' className='' />}
onClick={accessMenu.toggle} onClick={accessMenu.toggle}
/> />
<Dropdown isOpen={accessMenu.isOpen} margin='mt-3'> <Dropdown isOpen={accessMenu.isOpen} margin='mt-3'>
<DropdownButton <DropdownButton
text={labelUserRole(UserRole.READER)} text={labelUserRole(UserRole.READER)}
title={describeUserRole(UserRole.READER)} title={describeUserRole(UserRole.READER)}
icon={<IconRole role={UserRole.READER} size='1rem' />} icon={<IconRole value={UserRole.READER} size='1rem' />}
onClick={() => handleChangeMode(UserRole.READER)} onClick={() => handleChangeMode(UserRole.READER)}
/> />
<DropdownButton <DropdownButton
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 value={UserRole.EDITOR} size='1rem' />}
onClick={() => handleChangeMode(UserRole.EDITOR)} onClick={() => handleChangeMode(UserRole.EDITOR)}
disabled={!isOwned && !isEditor} 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 value={UserRole.OWNER} size='1rem' />}
onClick={() => handleChangeMode(UserRole.OWNER)} onClick={() => handleChangeMode(UserRole.OWNER)}
disabled={!isOwned} disabled={!isOwned}
/> />
<DropdownButton {user.is_staff ? (
text={labelUserRole(UserRole.ADMIN)} <DropdownButton
title={describeUserRole(UserRole.ADMIN)} text={labelUserRole(UserRole.ADMIN)}
icon={<IconRole role={UserRole.ADMIN} size='1rem' />} title={describeUserRole(UserRole.ADMIN)}
onClick={() => handleChangeMode(UserRole.ADMIN)} icon={<IconRole value={UserRole.ADMIN} size='1rem' />}
disabled={!user.is_staff} onClick={() => handleChangeMode(UserRole.ADMIN)}
/> disabled={!user.is_staff}
/>
) : null}
</Dropdown> </Dropdown>
</div> </div>
); );

View File

@ -56,9 +56,10 @@ 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 flex'> <div className='relative flex'>
<MiniButton <MiniButton
noHover
title='Выгрузить в формате CSV' title='Выгрузить в формате CSV'
className='absolute z-tooltip -top-8 right-6 hidden sm:block' className='absolute z-tooltip -top-8 right-6 hidden sm:block'
icon={<IconCSV size='1.25rem' className='icon-green' />} icon={<IconCSV size='1.25rem' className='text-muted-foreground hover:text-constructive' />}
onClick={handleDownloadCSV} onClick={handleDownloadCSV}
/> />

View File

@ -42,7 +42,7 @@ export function useLibraryColumns() {
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}
icon={<IconFolderTree size='1.25rem' className='cc-controls' />} icon={<IconFolderTree size='1.25rem' className='text-muted-foreground hover:text-constructive' />}
/> />
), ),
size: 50, size: 50,

View File

@ -86,8 +86,9 @@ export function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocati
/> />
) : null} ) : null}
<MiniButton <MiniButton
title='Переключение в режим Поиск' noHover
icon={<IconFolderTree size='1.25rem' className='icon-green' />} title='Переключение в режим Таблица'
icon={<IconFolderTree size='1.25rem' className='text-muted-foreground hover:text-constructive' />}
onClick={toggleFolderMode} onClick={toggleFolderMode}
/> />
</div> </div>

View File

@ -84,13 +84,8 @@ export function DlgCreateBlock() {
className='w-160 px-6 h-110' className='w-160 px-6 h-110'
helpTopic={HelpTopic.CC_OSS} helpTopic={HelpTopic.CC_OSS}
> >
<Tabs <Tabs className='grid' selectedIndex={activeTab} onSelect={index => setActiveTab(index as TabID)}>
selectedTabClassName='cc-selected' <TabList className='z-pop mx-auto -mb-5 flex border divide-x rounded-none'>
className='grid'
selectedIndex={activeTab}
onSelect={index => setActiveTab(index as TabID)}
>
<TabList className='z-pop mx-auto -mb-5 flex border divide-x rounded-none bg-secondary'>
<TabLabel title='Основные атрибуты блока' label='Карточка' /> <TabLabel title='Основные атрибуты блока' label='Карточка' />
<TabLabel <TabLabel
title={`Выбор вложенных узлов: [${children_operations.length + children_blocks.length}]`} title={`Выбор вложенных узлов: [${children_operations.length + children_blocks.length}]`}

View File

@ -100,12 +100,11 @@ export function DlgCreateOperation() {
helpTopic={HelpTopic.CC_OSS} helpTopic={HelpTopic.CC_OSS}
> >
<Tabs <Tabs
selectedTabClassName='cc-selected'
className='grid' className='grid'
selectedIndex={activeTab} selectedIndex={activeTab}
onSelect={(index, last) => handleSelectTab(index as TabID, last as TabID)} onSelect={(index, last) => handleSelectTab(index as TabID, last as TabID)}
> >
<TabList className='z-pop mx-auto -mb-5 flex border divide-x rounded-none bg-secondary'> <TabList className='z-pop mx-auto -mb-5 flex border divide-x rounded-none'>
<TabLabel <TabLabel
title={describeOperationType(OperationType.INPUT)} title={describeOperationType(OperationType.INPUT)}
label={labelOperationType(OperationType.INPUT)} label={labelOperationType(OperationType.INPUT)}

View File

@ -75,13 +75,8 @@ export function DlgEditOperation() {
helpTopic={HelpTopic.UI_SUBSTITUTIONS} helpTopic={HelpTopic.UI_SUBSTITUTIONS}
hideHelpWhen={() => activeTab !== TabID.SUBSTITUTION} hideHelpWhen={() => activeTab !== TabID.SUBSTITUTION}
> >
<Tabs <Tabs className='grid' selectedIndex={activeTab} onSelect={index => setActiveTab(index as TabID)}>
selectedTabClassName='cc-selected' <TabList className='mb-3 mx-auto w-fit flex border divide-x rounded-none'>
className='grid'
selectedIndex={activeTab}
onSelect={index => setActiveTab(index as TabID)}
>
<TabList className='mb-3 mx-auto w-fit flex border divide-x rounded-none bg-secondary'>
<TabLabel <TabLabel
title='Текстовые поля' // title='Текстовые поля' //
label='Карточка' label='Карточка'

View File

@ -23,41 +23,36 @@ export function OssStats({ className, stats }: OssStatsProps) {
<span>Всего</span> <span>Всего</span>
<span>{stats.count_all}</span> <span>{stats.count_all}</span>
</div> </div>
<ValueStats <ValueStats id='count_block' title='Блоки' icon={<IconConceptBlock size='1.25rem' />} value={stats.count_block} />
id='count_block'
title='Блоки'
icon={<IconConceptBlock size='1.25rem' className='text-primary' />}
value={stats.count_block}
/>
<ValueStats <ValueStats
id='count_inputs' id='count_inputs'
title='Загрузка' title='Загрузка'
icon={<IconDownload size='1.25rem' className='text-primary' />} icon={<IconDownload size='1.25rem' />}
value={stats.count_inputs} value={stats.count_inputs}
/> />
<ValueStats <ValueStats
id='count_synthesis' id='count_synthesis'
title='Синтез' title='Синтез'
icon={<IconSynthesis size='1.25rem' className='text-primary' />} icon={<IconSynthesis size='1.25rem' />}
value={stats.count_synthesis} value={stats.count_synthesis}
/> />
<ValueStats <ValueStats
id='count_schemas' id='count_schemas'
title='Прикрепленные схемы' title='Прикрепленные схемы'
icon={<IconRSForm size='1.25rem' className='text-primary' />} icon={<IconRSForm size='1.25rem' />}
value={stats.count_schemas} value={stats.count_schemas}
/> />
<ValueStats <ValueStats
id='count_owned' id='count_owned'
title='Собственные' title='Собственные'
icon={<IconRSFormOwned size='1.25rem' className='text-primary' />} icon={<IconRSFormOwned size='1.25rem' />}
value={stats.count_owned} value={stats.count_owned}
/> />
<ValueStats <ValueStats
id='count_imported' id='count_imported'
title='Внешние' title='Внешние'
icon={<IconRSFormImported size='1.25rem' className='text-primary' />} icon={<IconRSFormImported size='1.25rem' />}
value={stats.count_schemas - stats.count_owned} value={stats.count_schemas - stats.count_owned}
/> />
</div> </div>

View File

@ -1,6 +1,6 @@
import { useAuthSuspense } from '@/features/auth'; import { useAuthSuspense } from '@/features/auth';
import { Button } from '@/components/control'; import { MiniButton } from '@/components/control';
import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown'; import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown';
import { IconChild, IconEdit2 } from '@/components/icons'; import { IconChild, IconEdit2 } from '@/components/icons';
import { useDialogsStore } from '@/stores/dialogs'; import { useDialogsStore } from '@/stores/dialogs';
@ -31,14 +31,13 @@ export function MenuEditOss() {
return ( return (
<div ref={menu.ref} onBlur={menu.handleBlur} className='relative'> <div ref={menu.ref} onBlur={menu.handleBlur} className='relative'>
<Button <MiniButton
dense noHover
noBorder noPadding
noOutline
title='Редактирование' title='Редактирование'
hideTitle={menu.isOpen} hideTitle={menu.isOpen}
className='h-full px-2' className='h-full px-3 bg-transparent text-muted-foreground hover:text-primary'
icon={<IconEdit2 size='1.25rem' className={isMutable ? 'icon-green' : 'icon-red'} />} icon={<IconEdit2 size='1.25rem' />}
onClick={menu.toggle} onClick={menu.toggle}
/> />
<Dropdown isOpen={menu.isOpen} margin='mt-3'> <Dropdown isOpen={menu.isOpen} margin='mt-3'>
@ -47,7 +46,7 @@ export function MenuEditOss() {
titleHtml='Перенос конституент</br>между схемами' titleHtml='Перенос конституент</br>между схемами'
aria-label='Перенос конституент между схемами' aria-label='Перенос конституент между схемами'
icon={<IconChild size='1rem' className='icon-green' />} icon={<IconChild size='1rem' className='icon-green' />}
disabled={isProcessing} disabled={isProcessing || !isMutable}
onClick={handleRelocate} onClick={handleRelocate}
/> />
</Dropdown> </Dropdown>

View File

@ -3,7 +3,7 @@ import { useAuthSuspense } from '@/features/auth';
import { useRoleStore, UserRole } from '@/features/users'; import { useRoleStore, UserRole } from '@/features/users';
import { Divider } from '@/components/container'; import { Divider } from '@/components/container';
import { Button } from '@/components/control'; import { MiniButton } from '@/components/control';
import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown'; import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown';
import { IconDestroy, IconLibrary, IconMenu, IconNewItem, IconQR, IconShare } from '@/components/icons'; import { IconDestroy, IconLibrary, IconMenu, IconNewItem, IconQR, IconShare } from '@/components/icons';
import { useDialogsStore } from '@/stores/dialogs'; import { useDialogsStore } from '@/stores/dialogs';
@ -47,14 +47,13 @@ export function MenuMain() {
return ( return (
<div ref={menu.ref} onBlur={menu.handleBlur} className='relative'> <div ref={menu.ref} onBlur={menu.handleBlur} className='relative'>
<Button <MiniButton
dense noHover
noBorder noPadding
noOutline
title='Меню' title='Меню'
hideTitle={menu.isOpen} hideTitle={menu.isOpen}
icon={<IconMenu size='1.25rem' className='cc-controls' />} icon={<IconMenu size='1.25rem' />}
className='h-full pl-2' className='h-full pl-2 text-muted-foreground hover:text-primary bg-transparent'
onClick={menu.toggle} onClick={menu.toggle}
/> />
<Dropdown isOpen={menu.isOpen} margin='mt-3'> <Dropdown isOpen={menu.isOpen} margin='mt-3'>

View File

@ -58,10 +58,9 @@ export function OssTabs({ activeTab }: OssTabsProps) {
selectedIndex={activeTab} selectedIndex={activeTab}
onSelect={onSelectTab} onSelect={onSelectTab}
defaultFocus defaultFocus
selectedTabClassName='cc-selected'
className='relative flex flex-col mx-auto min-w-fit items-center' className='relative flex flex-col mx-auto min-w-fit items-center'
> >
<TabList className='absolute z-sticky flex border-b-2 border-x-2 divide-x-2 bg-secondary'> <TabList className='absolute z-sticky flex border-b-2 border-x-2 divide-x-2'>
<MenuOssTabs /> <MenuOssTabs />
<TabLabel label='Карточка' title={schema.title ?? ''} /> <TabLabel label='Карточка' title={schema.title ?? ''} />

View File

@ -0,0 +1,9 @@
import { type DomIconProps, IconKeyboard, IconKeyboardOff } from '@/components/icons';
export function IconShowKeyboard({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
if (value) {
return <IconKeyboard size={size} className={className} />;
} else {
return <IconKeyboardOff size={size} className={className} />;
}
}

View File

@ -76,13 +76,8 @@ export function DlgCstTemplate() {
onSubmit={event => void methods.handleSubmit(onSubmit)(event)} onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
helpTopic={HelpTopic.RSL_TEMPLATES} helpTopic={HelpTopic.RSL_TEMPLATES}
> >
<Tabs <Tabs className='grid' selectedIndex={activeTab} onSelect={index => setActiveTab(index as TabID)}>
selectedTabClassName='cc-selected' <TabList className='mb-3 mx-auto flex border divide-x rounded-none'>
className='grid'
selectedIndex={activeTab}
onSelect={index => setActiveTab(index as TabID)}
>
<TabList className='mb-3 mx-auto flex border divide-x rounded-none bg-secondary'>
<TabLabel label='Шаблон' title='Выбор шаблона выражения' className='w-32' /> <TabLabel label='Шаблон' title='Выбор шаблона выражения' className='w-32' />
<TabLabel label='Аргументы' title='Подстановка аргументов шаблона' className='w-32' /> <TabLabel label='Аргументы' title='Подстановка аргументов шаблона' className='w-32' />
<TabLabel label='Конституента' title='Редактирование конституенты' className='w-32' /> <TabLabel label='Конституента' title='Редактирование конституенты' className='w-32' />

View File

@ -106,8 +106,8 @@ export function DlgEditReference() {
className='w-160 px-6 h-128' className='w-160 px-6 h-128'
helpTopic={HelpTopic.TERM_CONTROL} helpTopic={HelpTopic.TERM_CONTROL}
> >
<Tabs selectedTabClassName='cc-selected' className='grid' selectedIndex={activeTab} onSelect={handleChangeTab}> <Tabs className='grid' selectedIndex={activeTab} onSelect={handleChangeTab}>
<TabList className='mb-3 mx-auto flex border divide-x rounded-none bg-secondary'> <TabList className='mb-3 mx-auto flex border divide-x rounded-none'>
<TabLabel title='Отсылка на термин в заданной словоформе' label={labelReferenceType(ReferenceType.ENTITY)} /> <TabLabel title='Отсылка на термин в заданной словоформе' label={labelReferenceType(ReferenceType.ENTITY)} />
<TabLabel <TabLabel
title='Установление синтаксической связи с отсылкой на термин' title='Установление синтаксической связи с отсылкой на термин'

View File

@ -58,13 +58,8 @@ export function DlgInlineSynthesis() {
canSubmit={methods.formState.isValid && sourceID !== null} canSubmit={methods.formState.isValid && sourceID !== null}
onSubmit={event => void methods.handleSubmit(onSubmit)(event)} onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
> >
<Tabs <Tabs className='grid' selectedIndex={activeTab} onSelect={index => setActiveTab(index as TabID)}>
selectedTabClassName='cc-selected' <TabList className='mb-3 mx-auto flex border divide-x rounded-none'>
className='grid'
selectedIndex={activeTab}
onSelect={index => setActiveTab(index as TabID)}
>
<TabList className='mb-3 mx-auto flex border divide-x rounded-none bg-secondary'>
<TabLabel <TabLabel
label='Схема' // label='Схема' //
title='Источник конституент' title='Источник конституент'

View File

@ -168,7 +168,7 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
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)]'
icon={<IconEdit size='1rem' className='icon-primary' />} icon={<IconEdit size='1rem' className='hover:icon-primary' />}
disabled={isModified} disabled={isModified}
/> />
) : null} ) : null}
@ -184,7 +184,7 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
aria-label='Переименовать конституенту' aria-label='Переименовать конституенту'
noHover noHover
onClick={handleRenameCst} onClick={handleRenameCst}
icon={<IconEdit size='1rem' className='icon-primary' />} icon={<IconEdit size='1rem' className='hover:icon-primary' />}
disabled={isModified} disabled={isModified}
/> />
) : null} ) : null}

View File

@ -1,7 +1,9 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { IconShowKeyboard } from '@/features/rsform/components/icon-show-keyboard';
import { MiniButton } from '@/components/control'; import { MiniButton } from '@/components/control';
import { IconControls, IconTree, IconTypeGraph } from '@/components/icons'; import { IconTree, IconTypeGraph } from '@/components/icons';
import { usePreferencesStore } from '@/stores/preferences'; import { usePreferencesStore } from '@/stores/preferences';
import { useMutatingRSForm } from '../../../backend/use-mutating-rsform'; import { useMutatingRSForm } from '../../../backend/use-mutating-rsform';
@ -22,20 +24,23 @@ export function ToolbarRSExpression({ className, disabled, showTypeGraph, showAS
<div className={clsx('cc-icons', className)}> <div className={clsx('cc-icons', className)}>
{!disabled || isProcessing ? ( {!disabled || isProcessing ? (
<MiniButton <MiniButton
noHover
title='Отображение специальной клавиатуры' title='Отображение специальной клавиатуры'
icon={<IconControls size='1.25rem' className={showControls ? 'icon-primary' : ''} />} icon={<IconShowKeyboard value={showControls} size='1.25rem' className='hover:text-primary' />}
onClick={toggleControls} onClick={toggleControls}
/> />
) : null} ) : null}
<MiniButton <MiniButton
noHover
title='Граф ступеней типизации' title='Граф ступеней типизации'
icon={<IconTypeGraph size='1.25rem' className='icon-primary' />} icon={<IconTypeGraph size='1.25rem' className='hover:text-primary' />}
onClick={showTypeGraph} onClick={showTypeGraph}
/> />
<MiniButton <MiniButton
noHover
title='Дерево разбора выражения' title='Дерево разбора выражения'
onClick={showAST} onClick={showAST}
icon={<IconTree size='1.25rem' className='icon-primary' />} icon={<IconTree size='1.25rem' className='hover:text-primary' />}
/> />
</div> </div>
); );

View File

@ -39,12 +39,12 @@ export function RSFormStats({ className, stats, isArchive }: RSFormStatsProps) {
<ValueStats <ValueStats
id='count_owned' id='count_owned'
title='Собственные' title='Собственные'
icon={<IconPredecessor size='1.25rem' className='text-primary' />} icon={<IconPredecessor size='1.25rem' />}
value={stats.count_all - stats.count_inherited} value={stats.count_all - stats.count_inherited}
/> />
<ValueStats <ValueStats
id='count_inherited' id='count_inherited'
icon={<IconChild size='1.25rem' className='text-primary' />} icon={<IconChild size='1.25rem' />}
value={stats.count_inherited} value={stats.count_inherited}
titleHtml={isArchive ? 'Архивные схемы не хранят<br/> информацию о наследовании' : 'Наследованные'} titleHtml={isArchive ? 'Архивные схемы не хранят<br/> информацию о наследовании' : 'Наследованные'}
/> />
@ -53,94 +53,91 @@ export function RSFormStats({ className, stats, isArchive }: RSFormStatsProps) {
id='count_ok' id='count_ok'
title='Корректные' title='Корректные'
className='col-start-1' className='col-start-1'
icon={<IconStatusOK size='1.25rem' className='text-constructive' />} icon={<IconStatusOK size='1.25rem' />}
value={stats.count_all - stats.count_errors - stats.count_property - stats.count_incalculable} value={stats.count_all - stats.count_errors - stats.count_property - stats.count_incalculable}
/> />
<ValueStats <ValueStats
id='count_property' id='count_property'
title='Неразмерные' title='Неразмерные'
icon={<IconStatusProperty size='1.25rem' className='text-primary' />} icon={<IconStatusProperty size='1.25rem' />}
value={stats.count_errors} value={stats.count_errors}
/> />
<ValueStats <ValueStats
id='count_incalculable' id='count_incalculable'
title='Невычислимые' title='Невычислимые'
icon={<IconStatusIncalculable size='1.25rem' className='text-destructive' />} icon={<IconStatusIncalculable size='1.25rem' />}
value={stats.count_incalculable} value={stats.count_incalculable}
/> />
<ValueStats <ValueStats
id='count_errors' id='count_errors'
title='Некорректные' title='Некорректные'
icon={<IconStatusError size='1.25rem' className='text-destructive' />} icon={<IconStatusError size='1.25rem' className={stats.count_errors > 0 ? 'text-destructive' : undefined} />}
value={stats.count_errors} value={stats.count_errors}
/> />
<ValueStats <ValueStats
id='count_base' id='count_base'
title='Базисные множества' title='Базисные множества'
icon={<IconCstBaseSet size='1.25rem' className='cc-controls' />} icon={<IconCstBaseSet size='1.25rem' />}
value={stats.count_base} value={stats.count_base}
/> />
<ValueStats <ValueStats
id='count_constant' id='count_constant'
title='Константные множества' title='Константные множества'
icon={<IconCstConstSet size='1.25rem' className='cc-controls' />} icon={<IconCstConstSet size='1.25rem' />}
value={stats.count_constant} value={stats.count_constant}
/> />
<ValueStats <ValueStats
id='count_structured' id='count_structured'
title='Родовые структуры' title='Родовые структуры'
icon={<IconCstStructured size='1.25rem' className='cc-controls' />} icon={<IconCstStructured size='1.25rem' />}
value={stats.count_structured} value={stats.count_structured}
/> />
<ValueStats <ValueStats id='count_axiom' title='Аксиомы' icon={<IconCstAxiom size='1.25rem' />} value={stats.count_axiom} />
id='count_axiom'
title='Аксиомы'
icon={<IconCstAxiom size='1.25rem' className='cc-controls' />}
value={stats.count_axiom}
/>
<ValueStats <ValueStats id='count_term' title='Термы' icon={<IconCstTerm size='1.25rem' />} value={stats.count_term} />
id='count_term'
title='Термы'
icon={<IconCstTerm size='1.25rem' className='cc-controls' />}
value={stats.count_term}
/>
<ValueStats <ValueStats
id='count_function' id='count_function'
title='Терм-функции' title='Терм-функции'
icon={<IconCstFunction size='1.25rem' className='cc-controls' />} icon={<IconCstFunction size='1.25rem' />}
value={stats.count_function} value={stats.count_function}
/> />
<ValueStats <ValueStats
id='count_predicate' id='count_predicate'
title='Предикат-функции' title='Предикат-функции'
icon={<IconCstPredicate size='1.25rem' className='cc-controls' />} icon={<IconCstPredicate size='1.25rem' />}
value={stats.count_predicate} value={stats.count_predicate}
/> />
<ValueStats <ValueStats
id='count_theorem' id='count_theorem'
title='Теоремы' title='Теоремы'
icon={<IconCstTheorem size='1.25rem' className='cc-controls' />} icon={<IconCstTheorem size='1.25rem' />}
value={stats.count_theorem} value={stats.count_theorem}
/> />
<ValueStats <ValueStats
id='count_text_term' id='count_text_term'
title='Термины' title='Термины'
icon={<IconTerminology size='1.25rem' className='text-primary' />} icon={<IconTerminology size='1.25rem' />}
value={stats.count_text_term} value={stats.count_text_term}
/> />
<ValueStats <ValueStats
id='count_definition' id='count_definition'
title='Определения' title='Определения'
icon={<IconDefinition size='1.25rem' className='text-primary' />} icon={<IconDefinition size='1.25rem' />}
value={stats.count_definition} value={stats.count_definition}
/> />
<ValueStats <ValueStats
id='count_convention' id='count_convention'
title='Конвенции' title='Конвенции'
icon={<IconConvention size='1.25rem' className='text-primary' />} icon={
<IconConvention
size='1.25rem'
className={
stats.count_convention < stats.count_structured + stats.count_base ? 'text-destructive' : undefined
}
/>
}
value={stats.count_convention} value={stats.count_convention}
/> />
</div> </div>

View File

@ -149,7 +149,7 @@ export function EditorRSList() {
<MiniButton <MiniButton
className='absolute z-pop right-4 hidden sm:block top-18' className='absolute z-pop right-4 hidden sm:block top-18'
title='Выгрузить в формате CSV' title='Выгрузить в формате CSV'
icon={<IconCSV size='1.25rem' className='icon-green' />} icon={<IconCSV size='1.25rem' className='text-muted-foreground hover:text-constructive' />}
onClick={handleDownloadCSV} onClick={handleDownloadCSV}
/> />

View File

@ -2,7 +2,7 @@ import { urls, useConceptNavigation } from '@/app';
import { useAuthSuspense } from '@/features/auth'; import { useAuthSuspense } from '@/features/auth';
import { Divider } from '@/components/container'; import { Divider } from '@/components/container';
import { Button } from '@/components/control'; import { MiniButton } from '@/components/control';
import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown'; import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown';
import { import {
IconArchive, IconArchive,
@ -103,13 +103,12 @@ export function MenuEditSchema() {
if (isArchive) { if (isArchive) {
return ( return (
<Button <MiniButton
dense noHover
noBorder noPadding
noOutline
titleHtml='<b>Архив</b>: Редактирование запрещено<br />Перейти к актуальной версии' titleHtml='<b>Архив</b>: Редактирование запрещено<br />Перейти к актуальной версии'
hideTitle={menu.isOpen} hideTitle={menu.isOpen}
className='h-full px-2' className='h-full px-3 bg-transparent'
icon={<IconArchive size='1.25rem' className='icon-primary' />} icon={<IconArchive size='1.25rem' className='icon-primary' />}
onClick={event => router.push({ path: urls.schema(schema.id), newTab: event.ctrlKey || event.metaKey })} onClick={event => router.push({ path: urls.schema(schema.id), newTab: event.ctrlKey || event.metaKey })}
/> />
@ -118,14 +117,13 @@ export function MenuEditSchema() {
return ( return (
<div ref={menu.ref} onBlur={menu.handleBlur} className='relative'> <div ref={menu.ref} onBlur={menu.handleBlur} className='relative'>
<Button <MiniButton
dense noHover
noBorder noPadding
noOutline
title='Редактирование' title='Редактирование'
hideTitle={menu.isOpen} hideTitle={menu.isOpen}
className='h-full px-2' className='h-full px-3 bg-transparent text-muted-foreground hover:text-primary'
icon={<IconEdit2 size='1.25rem' className={isContentEditable ? 'icon-green' : 'icon-red'} />} icon={<IconEdit2 size='1.25rem' />}
onClick={menu.toggle} onClick={menu.toggle}
/> />
<Dropdown isOpen={menu.isOpen} margin='mt-3'> <Dropdown isOpen={menu.isOpen} margin='mt-3'>

View File

@ -7,7 +7,7 @@ import { AccessPolicy, LocationHead } from '@/features/library';
import { useRoleStore, UserRole } from '@/features/users'; import { useRoleStore, UserRole } from '@/features/users';
import { Divider } from '@/components/container'; import { Divider } from '@/components/container';
import { Button } from '@/components/control'; import { MiniButton } from '@/components/control';
import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown'; import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown';
import { import {
IconClone, IconClone,
@ -128,14 +128,13 @@ export function MenuMain() {
return ( return (
<div ref={menu.ref} onBlur={menu.handleBlur} className='relative'> <div ref={menu.ref} onBlur={menu.handleBlur} className='relative'>
<Button <MiniButton
dense noHover
noBorder noPadding
noOutline
title='Меню' title='Меню'
hideTitle={menu.isOpen} hideTitle={menu.isOpen}
icon={<IconMenu size='1.25rem' className='cc-controls' />} icon={<IconMenu size='1.25rem' />}
className='h-full pl-2' className='h-full pl-2 text-muted-foreground hover:text-primary bg-transparent'
onClick={menu.toggle} onClick={menu.toggle}
/> />
<Dropdown isOpen={menu.isOpen} margin='mt-3'> <Dropdown isOpen={menu.isOpen} margin='mt-3'>

View File

@ -76,10 +76,9 @@ export function RSTabs({ activeID, activeTab }: RSTabsProps) {
selectedIndex={activeTab} selectedIndex={activeTab}
onSelect={onSelectTab} onSelect={onSelectTab}
defaultFocus defaultFocus
selectedTabClassName='cc-selected'
className='relative flex flex-col min-w-fit items-center' className='relative flex flex-col min-w-fit items-center'
> >
<TabList className='absolute z-sticky flex border-b-2 border-x-2 divide-x-2 bg-secondary'> <TabList className='absolute z-sticky flex border-b-2 border-x-2 divide-x-2'>
<MenuRSTabs /> <MenuRSTabs />
<TabLabel <TabLabel

View File

@ -3,7 +3,7 @@
*/ */
@utility cc-btn-nav { @utility cc-btn-nav {
color: color-mix(in oklab, var(--color-foreground) 50%, transparent); color: var(--color-muted-foreground);
border-radius: 0.75rem; border-radius: 0.75rem;
cursor: pointer; cursor: pointer;
@ -19,8 +19,6 @@
} }
.dark & { .dark & {
color: color-mix(in oklab, var(--color-foreground) 70%, transparent);
&:hover { &:hover {
color: var(--color-foreground); color: var(--color-foreground);
} }

View File

@ -24,7 +24,7 @@
--clr-prim-100: oklch(098% 0 0deg); --clr-prim-100: oklch(098% 0 0deg);
--clr-prim-200: oklch(095% 0 0deg); --clr-prim-200: oklch(095% 0 0deg);
--clr-prim-400: oklch(085% 0 0deg); --clr-prim-400: oklch(085% 0 0deg);
--clr-prim-600: oklch(070% 0 0deg); --clr-prim-600: oklch(065% 0 0deg);
--clr-prim-999: oklch(000% 0 0deg); --clr-prim-999: oklch(000% 0 0deg);
--clr-sec-0: oklch(100% 0 0deg); --clr-sec-0: oklch(100% 0 0deg);
@ -46,7 +46,7 @@
--acc-bg-teal: oklch(082% 0.180 210deg); --acc-bg-teal: oklch(082% 0.180 210deg);
--acc-bg-orange: oklch(085% 0.130 62deg); --acc-bg-orange: oklch(085% 0.130 62deg);
--acc-bg-green25: oklch(097% 0.080 138deg); --acc-bg-green25: oklch(096% 0.070 138deg);
--acc-bg-green50: oklch(092% 0.150 138deg); --acc-bg-green50: oklch(092% 0.150 138deg);
--acc-bg-orange50: oklch(090% 0.044 62deg); --acc-bg-orange50: oklch(090% 0.044 62deg);
@ -65,7 +65,7 @@
--clr-prim-100: oklch(022% 0 0deg); --clr-prim-100: oklch(022% 0 0deg);
--clr-prim-200: oklch(030% 0 0deg); --clr-prim-200: oklch(030% 0 0deg);
--clr-prim-400: oklch(050% 0 0deg); --clr-prim-400: oklch(050% 0 0deg);
--clr-prim-600: oklch(065% 0 0deg); --clr-prim-600: oklch(070% 0 0deg);
--clr-prim-999: oklch(095% 0 0deg); --clr-prim-999: oklch(095% 0 0deg);
--clr-sec-0: oklch(100% 0 0deg); --clr-sec-0: oklch(100% 0 0deg);

View File

@ -11,7 +11,7 @@
--toastify-color-dark: var(--color-secondary); --toastify-color-dark: var(--color-secondary);
--toastify-toast-width: 20rem; --toastify-toast-width: 20rem;
--toastify-toast-padding: 0.75rem; --toastify-toast-padding: 1rem;
--toastify-toast-min-height: 0; --toastify-toast-min-height: 0;
--toastify-toast-max-height: 40rem; --toastify-toast-max-height: 40rem;