Refactoring: extract incapsulate icon usage. Iconset rework

This commit is contained in:
IRBorisov 2024-04-03 21:05:53 +03:00
parent abd88ccdbf
commit b3a4b8e034
32 changed files with 212 additions and 174 deletions

View File

@ -3,7 +3,7 @@ import { motion } from 'framer-motion';
import { FaSquarePlus } from 'react-icons/fa6';
import { IoLibrary } from 'react-icons/io5';
import { EducationIcon } from '@/components/Icons';
import { IconManuals } from '@/components/Icons';
import { useConceptNavigation } from '@/context/NavigationContext';
import { useConceptOptions } from '@/context/OptionsContext';
import { animateNavigation } from '@/styling/animations';
@ -59,12 +59,7 @@ function Navigation() {
icon={<IoLibrary size='1.5rem' />}
onClick={navigateLibrary}
/>
<NavigationButton
text='Справка'
title='Справочные материалы'
icon={<EducationIcon />}
onClick={navigateHelp}
/>
<NavigationButton text='Справка' title='Справочные материалы' icon={<IconManuals />} onClick={navigateHelp} />
<UserMenu />
</div>
</motion.div>

View File

@ -1,7 +1,7 @@
import clsx from 'clsx';
import { motion } from 'framer-motion';
import { RiPushpinFill, RiUnpinLine } from 'react-icons/ri';
import { IconPin, IconUnpin } from '@/components/Icons';
import { useConceptOptions } from '@/context/OptionsContext';
import { animateNavigationToggle } from '@/styling/animations';
@ -22,8 +22,8 @@ function ToggleNavigationButton() {
animate={noNavigationAnimation ? 'off' : 'on'}
variants={animateNavigationToggle}
>
{!noNavigationAnimation ? <RiPushpinFill /> : null}
{noNavigationAnimation ? <RiUnpinLine /> : null}
{!noNavigationAnimation ? <IconPin /> : null}
{noNavigationAnimation ? <IconUnpin /> : null}
</motion.button>
);
}

View File

@ -1,5 +1,6 @@
import { LuLightbulb, LuLightbulbOff, LuLogOut, LuMoon, LuSun, LuUserCircle2 } from 'react-icons/lu';
import { LuLogOut, LuMoon, LuSun } from 'react-icons/lu';
import { IconHelp, IconHelpOff, IconUser } from '@/components/Icons';
import Dropdown from '@/components/ui/Dropdown';
import DropdownButton from '@/components/ui/DropdownButton';
import { useAuth } from '@/context/AuthContext';
@ -38,7 +39,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
<DropdownButton
text={user?.username}
title='Профиль пользователя'
icon={<LuUserCircle2 size='1rem' />}
icon={<IconUser size='1rem' />}
onClick={navigateProfile}
/>
<DropdownButton
@ -49,7 +50,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
/>
<DropdownButton
text={showHelp ? 'Помощь: Вкл' : 'Помощь: Выкл'}
icon={showHelp ? <LuLightbulb size='1rem' /> : <LuLightbulbOff size='1rem' />}
icon={showHelp ? <IconHelp size='1rem' /> : <IconHelpOff size='1rem' />}
title='Отображение иконок подсказок'
onClick={toggleShowHelp}
/>

View File

@ -1,6 +1,6 @@
import { FaCircleUser } from 'react-icons/fa6';
import { InDoorIcon } from '@/components/Icons';
import { IconLogin } from '@/components/Icons';
import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NavigationContext';
import useDropdown from '@/hooks/useDropdown';
@ -20,7 +20,7 @@ function UserMenu() {
{!user ? (
<NavigationButton
title='Перейти на страницу логина'
icon={<InDoorIcon size='1.5rem' className='icon-primary' />}
icon={<IconLogin size='1.5rem' className='icon-primary' />}
onClick={navigateLogin}
/>
) : null}

View File

@ -1,5 +1,50 @@
// Search new icons at https://reactsvgicons.com/
export { FiSave as IconSave } from 'react-icons/fi';
export { BiCheck as IconAccept } from 'react-icons/bi';
export { BiX as IconClose } from 'react-icons/bi';
export { BiX as IconRemove } from 'react-icons/bi';
export { BiTrash as IconDestroy } from 'react-icons/bi';
export { BiReset as IconReset } from 'react-icons/bi';
export { BiPlusCircle as IconNewItem } from 'react-icons/bi';
export { BiDuplicate as IconClone } from 'react-icons/bi';
export { LuReplace as IconReplace } from 'react-icons/lu';
export { BiDownload as IconDownload } from 'react-icons/bi';
export { BiUpload as IconUpload } from 'react-icons/bi';
export { LiaEdit as IconEdit } from 'react-icons/lia';
export { RiPushpinFill as IconPin } from 'react-icons/ri';
export { RiUnpinLine as IconUnpin } from 'react-icons/ri';
export { BiCog as IconSettings } from 'react-icons/bi';
export { LuUserCircle2 as IconUser } from 'react-icons/lu';
export { LuCrown as IconOwner } from 'react-icons/lu';
export { BiMeteor as IconAdmin } from 'react-icons/bi';
export { LuGlasses as IconReader } from 'react-icons/lu';
export { FiBell as IconFollow } from 'react-icons/fi';
export { FiBellOff as IconFollowOff } from 'react-icons/fi';
export { BiListUl as IconList } from 'react-icons/bi';
export { BiFontFamily as IconText } from 'react-icons/bi';
export { BiFont as IconTextOff } from 'react-icons/bi';
export { RiTreeLine as IconTree } from 'react-icons/ri';
export { LuMinimize as IconGraphClosure } from 'react-icons/lu';
export { LuMaximize as IconGraphMaximize } from 'react-icons/lu';
export { LuExpand as IconGraphExpand } from 'react-icons/lu';
export { BiGitBranch as IconGraphInputs } from 'react-icons/bi';
export { BiGitMerge as IconGraphOutputs } from 'react-icons/bi';
export { BiCheckShield as IconImmutable } from 'react-icons/bi';
export { RiOpenSourceLine as IconPublic } from 'react-icons/ri';
export { BiShareAlt as IconShare } from 'react-icons/bi';
export { LuLightbulb as IconHelp } from 'react-icons/lu';
export { LuLightbulbOff as IconHelpOff } from 'react-icons/lu';
export { BiFilterAlt as IconFilter } from 'react-icons/bi';
export { BiUpvote as IconMoveUp } from 'react-icons/bi';
export { BiDownvote as IconMoveDown } from 'react-icons/bi';
export { LuRotate3D as IconRotate3D } from 'react-icons/lu';
export { MdOutlineFitScreen as IconFitImage } from 'react-icons/md';
interface IconSVGProps {
viewBox: string;
size?: string;
@ -28,7 +73,7 @@ function IconSVG({ viewBox, size = '1.5rem', className, props, children }: IconS
);
}
export function EducationIcon(props: IconProps) {
export function IconManuals(props: IconProps) {
return (
<IconSVG viewBox='0 0 20 20' {...props}>
<path d='M3.33 8L10 12l10-6-10-6L0 6h10v2H3.33zM0 8v8l2-2.22V9.2L0 8zm10 12l-5-3-2-1.2v-6l7 4.2 7-4.2v6L10 20z' />
@ -36,7 +81,7 @@ export function EducationIcon(props: IconProps) {
);
}
export function InDoorIcon(props: IconProps) {
export function IconLogin(props: IconProps) {
return (
<IconSVG viewBox='0 0 24 24' {...props}>
<path fill='none' d='M0 0h24v24H0z' />
@ -45,7 +90,7 @@ export function InDoorIcon(props: IconProps) {
);
}
export function CheckboxCheckedIcon() {
export function CheckboxChecked() {
return (
<svg className='w-3 h-3' viewBox='0 0 512 512' fill='#ffffff'>
<path d='M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7l233.4-233.3c12.5-12.5 32.8-12.5 45.3 0z' />
@ -53,7 +98,7 @@ export function CheckboxCheckedIcon() {
);
}
export function CheckboxNullIcon() {
export function CheckboxNull() {
return (
<svg className='w-3 h-3' viewBox='0 0 16 16' fill='#ffffff'>
<path d='M2 7.75A.75.75 0 012.75 7h10a.75.75 0 010 1.5h-10A.75.75 0 012 7.75z' />

View File

@ -1,10 +1,9 @@
import { LuLightbulb } from 'react-icons/lu';
import TextURL from '@/components/ui/TextURL';
import Tooltip, { PlacesType } from '@/components/ui/Tooltip';
import { useConceptOptions } from '@/context/OptionsContext';
import { HelpTopic } from '@/models/miscellaneous';
import { IconHelp } from '../Icons';
import InfoTopic from '../info/InfoTopic';
import { CProps } from '../props';
@ -22,7 +21,7 @@ function BadgeHelp({ topic, ...restProps }: BadgeHelpProps) {
}
return (
<div id={`help-${topic}`} className='p-1'>
<LuLightbulb size='1.25rem' className='icon-primary' />
<IconHelp size='1.25rem' className='icon-primary' />
<Tooltip clickable anchorSelect={`#help-${topic}`} layer='z-modal-tooltip' {...restProps}>
<div className='relative' onClick={event => event.stopPropagation()}>
<div className='absolute right-0 text-sm top-[0.4rem] clr-input'>

View File

@ -1,5 +1,4 @@
import { BiCheckShield, BiShareAlt } from 'react-icons/bi';
import { FiBell } from 'react-icons/fi';
import { IconFollow, IconImmutable, IconPublic } from '../Icons';
function HelpLibrary() {
// prettier-ignore
@ -11,15 +10,15 @@ function HelpLibrary() {
<p>Сортировка по клику на заголовок таблицы</p>
<h2>Отображение статусов</h2>
<div className='flex items-center gap-2'>
<FiBell size='1rem'/>
<IconFollow size='1rem'/>
<p><b>отслеживаемая</b> обозначает отслеживание схемы</p>
</div>
<div className='flex items-center gap-2'>
<BiShareAlt size='1rem'/>
<IconPublic size='1rem'/>
<p><b>общедоступная</b> отображает схему всем пользователям</p>
</div>
<div className='flex items-center gap-2'>
<BiCheckShield size='1rem'/>
<IconImmutable size='1rem'/>
<p><b>неизменная</b> выделяет стандартные схемы</p>
</div>
</div>);

View File

@ -1,8 +1,8 @@
'use client';
import { useCallback, useMemo, useState } from 'react';
import { BiChevronLeft, BiChevronRight, BiFirstPage, BiLastPage, BiX } from 'react-icons/bi';
import { LuFlag, LuFlagOff, LuPower, LuPowerOff, LuReplace } from 'react-icons/lu';
import { BiChevronLeft, BiChevronRight, BiFirstPage, BiLastPage } from 'react-icons/bi';
import { LuFlag, LuFlagOff, LuPower, LuPowerOff } from 'react-icons/lu';
import ConstituentaBadge from '@/components/info/ConstituentaBadge';
import ConstituentaSelector from '@/components/select/ConstituentaSelector';
@ -13,6 +13,8 @@ import { useConceptOptions } from '@/context/OptionsContext';
import { IConstituenta, IRSForm, ISubstitution } from '@/models/rsform';
import { describeConstituenta } from '@/utils/labels';
import { IconRemove, IconReplace } from '../Icons';
interface SubstitutionsPickerProps {
prefixID: string;
rows?: number;
@ -141,7 +143,7 @@ function SubstitutionsPicker({
<MiniButton
noHover
title='Удалить'
icon={<BiX size='1rem' className='icon-red' />}
icon={<IconRemove size='1rem' className='icon-red' />}
onClick={() => handleDeleteRow(props.row.index)}
/>
)
@ -194,7 +196,7 @@ function SubstitutionsPicker({
noHover
title='Добавить в таблицу отождествлений'
className='mb-[0.375rem] grow-0'
icon={<LuReplace size='1.5rem' className='icon-primary' />}
icon={<IconReplace size='1.5rem' className='icon-primary' />}
disabled={!leftCst || !rightCst || leftCst === rightCst}
onClick={addSubstitution}
/>

View File

@ -3,7 +3,7 @@ import { useMemo } from 'react';
import { globals } from '@/utils/constants';
import { CheckboxCheckedIcon } from '../Icons';
import { CheckboxChecked } from '../Icons';
import { CProps } from '../props';
export interface CheckboxProps extends Omit<CProps.Button, 'value' | 'onClick'> {
@ -72,7 +72,7 @@ function Checkbox({
>
{value ? (
<div className='mt-[1px] ml-[1px]'>
<CheckboxCheckedIcon />
<CheckboxChecked />
</div>
) : null}
</div>

View File

@ -3,7 +3,7 @@ import { useMemo } from 'react';
import { globals } from '@/utils/constants';
import { CheckboxCheckedIcon, CheckboxNullIcon } from '../Icons';
import { CheckboxChecked, CheckboxNull } from '../Icons';
import { CheckboxProps } from './Checkbox';
export interface CheckboxTristateProps extends Omit<CheckboxProps, 'value' | 'setValue'> {
@ -75,12 +75,12 @@ function CheckboxTristate({
>
{value ? (
<div className='mt-[1px] ml-[1px]'>
<CheckboxCheckedIcon />
<CheckboxChecked />
</div>
) : null}
{value == null ? (
<div className='mt-[1px] ml-[1px]'>
<CheckboxNullIcon />
<CheckboxNull />
</div>
) : null}
</div>

View File

@ -2,8 +2,8 @@
import clsx from 'clsx';
import { useRef, useState } from 'react';
import { BiUpload } from 'react-icons/bi';
import { IconUpload } from '../Icons';
import { CProps } from '../props';
import Button from './Button';
import Label from './Label';
@ -45,7 +45,7 @@ function FileInput({ id, label, acceptType, title, className, style, onChange, .
onChange={handleFileChange}
{...restProps}
/>
<Button text={label} icon={<BiUpload size='1.25rem' />} onClick={handleUploadClick} title={title} />
<Button text={label} icon={<IconUpload size='1.25rem' />} onClick={handleUploadClick} title={title} />
<Label text={fileName} htmlFor={id} />
</div>
);

View File

@ -3,12 +3,12 @@
import clsx from 'clsx';
import { motion } from 'framer-motion';
import { useRef } from 'react';
import { BiX } from 'react-icons/bi';
import useEscapeKey from '@/hooks/useEscapeKey';
import { animateModal } from '@/styling/animations';
import { prepareTooltip } from '@/utils/labels';
import { IconClose } from '../Icons';
import { CProps } from '../props';
import Button from './Button';
import MiniButton from './MiniButton';
@ -75,7 +75,7 @@ function Modal({
<MiniButton
noPadding
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
icon={<BiX size='1.25rem' />}
icon={<IconClose size='1.25rem' />}
onClick={handleCancel}
/>
</Overlay>

View File

@ -3,8 +3,8 @@
import { createColumnHelper } from '@tanstack/react-table';
import clsx from 'clsx';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { BiCheck, BiRefresh, BiX } from 'react-icons/bi';
import { IconAccept, IconRemove, IconReset } from '@/components/Icons';
import RSInput from '@/components/RSInput';
import ConstituentaPicker from '@/components/select/ConstituentaPicker';
import DataTable, { IConditionalStyle } from '@/components/ui/DataTable';
@ -122,7 +122,7 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
title='Очистить значение'
noPadding
noHover
icon={<BiX size='1.25rem' className='icon-red' />}
icon={<IconRemove size='1.25rem' className='icon-red' />}
onClick={() => handleClearArgument(props.row.original)}
/>
) : null}
@ -188,7 +188,7 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
title='Подставить значение аргумента'
noHover
className='py-0'
icon={<BiCheck size='2rem' className='icon-green' />}
icon={<IconAccept size='2rem' className='icon-green' />}
disabled={!argumentValue || !selectedArgument}
onClick={() => handleAssignArgument(selectedArgument!, argumentValue)}
/>
@ -198,7 +198,7 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
className='py-0'
disabled={!isModified}
onClick={handleReset}
icon={<BiRefresh size='2rem' className='icon-primary' />}
icon={<IconReset size='2rem' className='icon-primary' />}
/>
</div>
</div>

View File

@ -1,9 +1,8 @@
'use client';
import { useLayoutEffect, useMemo, useState } from 'react';
import { BiReset } from 'react-icons/bi';
import { FiSave } from 'react-icons/fi';
import { IconReset, IconSave } from '@/components/Icons';
import MiniButton from '@/components/ui/MiniButton';
import Modal from '@/components/ui/Modal';
import TextArea from '@/components/ui/TextArea';
@ -92,14 +91,14 @@ function DlgEditVersions({ hideWindow, versions, onDelete, onUpdate }: DlgEditVe
<MiniButton
title='Сохранить изменения'
disabled={!isModified || !isValid || processing}
icon={<FiSave size='1.25rem' className='icon-primary' />}
icon={<IconSave size='1.25rem' className='icon-primary' />}
onClick={handleUpdate}
/>
<MiniButton
title='Сбросить несохраненные изменения'
disabled={!isModified}
onClick={handleReset}
icon={<BiReset size='1.25rem' className='icon-primary' />}
icon={<IconReset size='1.25rem' className='icon-primary' />}
/>
</div>
</div>

View File

@ -2,9 +2,9 @@
import clsx from 'clsx';
import { useMemo } from 'react';
import { BiX } from 'react-icons/bi';
import { useIntl } from 'react-intl';
import { IconRemove } from '@/components/Icons';
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable';
import MiniButton from '@/components/ui/MiniButton';
import { useConceptOptions } from '@/context/OptionsContext';
@ -66,7 +66,7 @@ function VersionsTable({ processing, items, onDelete, selected, onSelect }: Vers
noHover
noPadding
disabled={processing}
icon={<BiX size='1.25rem' className='icon-red' />}
icon={<IconRemove size='1.25rem' className='icon-red' />}
onClick={() => onDelete(props.row.original.id)}
/>
</div>

View File

@ -2,8 +2,9 @@
import clsx from 'clsx';
import { useLayoutEffect, useState } from 'react';
import { BiCheck, BiChevronsDown, BiLeftArrow, BiRightArrow, BiX } from 'react-icons/bi';
import { BiChevronsDown, BiLeftArrow, BiRightArrow } from 'react-icons/bi';
import { IconAccept, IconRemove } from '@/components/Icons';
import BadgeHelp from '@/components/man/BadgeHelp';
import SelectGrammeme from '@/components/select/SelectGrammeme';
import Label from '@/components/ui/Label';
@ -182,7 +183,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
<MiniButton
noHover
title='Внести словоформу'
icon={<BiCheck size='1.5rem' className='icon-green' />}
icon={<IconAccept size='1.5rem' className='icon-green' />}
disabled={textProcessor.loading || !inputText || inputGrams.length == 0}
onClick={handleAddForm}
/>
@ -201,7 +202,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
noHover
title='Сбросить все словоформы'
className='py-0'
icon={<BiX size='1.5rem' className='icon-red' />}
icon={<IconRemove size='1.5rem' className='icon-red' />}
disabled={textProcessor.loading || forms.length === 0}
onClick={handleResetAll}
/>

View File

@ -2,8 +2,8 @@
import clsx from 'clsx';
import { useCallback, useMemo } from 'react';
import { BiX } from 'react-icons/bi';
import { IconRemove } from '@/components/Icons';
import WordFormBadge from '@/components/info/WordFormBadge';
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
import MiniButton from '@/components/ui/MiniButton';
@ -60,7 +60,7 @@ function WordFormsTable({ forms, setForms, onFormSelect }: WordFormsTableProps)
noHover
noPadding
title='Удалить словоформу'
icon={<BiX size='1.25rem' className='icon-red' />}
icon={<IconRemove size='1.25rem' className='icon-red' />}
onClick={() => handleDeleteRow(props.row.index)}
/>
</div>

View File

@ -2,10 +2,10 @@
import clsx from 'clsx';
import { useEffect, useRef, useState } from 'react';
import { BiDownload } from 'react-icons/bi';
import { toast } from 'react-toastify';
import { urls } from '@/app/urls';
import { IconDownload } from '@/components/Icons';
import InfoError from '@/components/info/InfoError';
import Button from '@/components/ui/Button';
import Checkbox from '@/components/ui/Checkbox';
@ -95,7 +95,7 @@ function CreateRSFormPage() {
/>
<MiniButton
title='Загрузить из Экстеор'
icon={<BiDownload size='1.25rem' className='icon-primary' />}
icon={<IconDownload size='1.25rem' className='icon-primary' />}
onClick={() => inputRef.current?.click()}
/>
</Overlay>

View File

@ -1,7 +1,6 @@
import clsx from 'clsx';
import { BiCheckShield, BiShareAlt } from 'react-icons/bi';
import { FiBell } from 'react-icons/fi';
import { IconFollow, IconImmutable, IconPublic } from '@/components/Icons';
import { ICurrentUser, ILibraryItem } from '@/models/library';
import { prefixes } from '@/utils/constants';
@ -15,17 +14,17 @@ function ItemIcons({ user, item }: ItemIconsProps) {
<div className={clsx('min-w-[3.3rem]', 'inline-flex gap-1 align-middle')} id={`${prefixes.library_list}${item.id}`}>
{user && user.subscriptions.includes(item.id) ? (
<span title='Отслеживаемая'>
<FiBell size='1rem' />
<IconFollow size='1rem' />
</span>
) : null}
{item.is_common ? (
<span title='Общедоступная'>
<BiShareAlt size='1em' />
<IconPublic size='1rem' />
</span>
) : null}
{item.is_canonical ? (
<span title='Неизменная'>
<BiCheckShield size='1rem' />
<IconImmutable size='1rem' />
</span>
) : null}
</div>

View File

@ -1,8 +1,8 @@
'use client';
import { useCallback } from 'react';
import { BiFilterAlt } from 'react-icons/bi';
import { IconFilter } from '@/components/Icons';
import Dropdown from '@/components/ui/Dropdown';
import DropdownCheckbox from '@/components/ui/DropdownCheckbox';
import SelectorButton from '@/components/ui/SelectorButton';
@ -49,7 +49,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
title='Список фильтров'
hideTitle={strategyMenu.isOpen}
className='h-full'
icon={<BiFilterAlt size='1.25rem' />}
icon={<IconFilter size='1.25rem' />}
text={labelLibraryFilter(value)}
onClick={strategyMenu.toggle}
/>

View File

@ -1,6 +1,4 @@
import { BiDownvote, BiDuplicate, BiPlusCircle, BiReset, BiTrash, BiUpvote } from 'react-icons/bi';
import { FiSave } from 'react-icons/fi';
import { IconClone, IconDestroy, IconMoveDown, IconMoveUp, IconNewItem, IconReset, IconSave } from '@/components/Icons';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import { messages, prepareTooltip } from '@/utils/labels';
@ -34,25 +32,25 @@ function ConstituentaToolbar({
<Overlay position='top-1 right-4 sm:right-1/2 sm:translate-x-1/2' className='cc-icons'>
<MiniButton
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
icon={<FiSave size='1.25rem' className='icon-primary' />}
icon={<IconSave size='1.25rem' className='icon-primary' />}
disabled={disabled || !modified}
onClick={onSubmit}
/>
<MiniButton
title='Сбросить несохраненные изменения'
icon={<BiReset size='1.25rem' className='icon-primary' />}
icon={<IconReset size='1.25rem' className='icon-primary' />}
disabled={disabled || !modified}
onClick={onReset}
/>
<MiniButton
title='Создать конституенту после данной'
icon={<BiPlusCircle size={'1.25rem'} className='icon-green' />}
icon={<IconNewItem size={'1.25rem'} className='icon-green' />}
disabled={disabled}
onClick={onCreate}
/>
<MiniButton
titleHtml={modified ? messages.unsaved : prepareTooltip('Клонировать конституенту', 'Alt + V')}
icon={<BiDuplicate size='1.25rem' className='icon-green' />}
icon={<IconClone size='1.25rem' className='icon-green' />}
disabled={disabled || modified}
onClick={onClone}
/>
@ -60,17 +58,17 @@ function ConstituentaToolbar({
title='Удалить редактируемую конституенту'
disabled={disabled}
onClick={onDelete}
icon={<BiTrash size='1.25rem' className='icon-red' />}
icon={<IconDestroy size='1.25rem' className='icon-red' />}
/>
<MiniButton
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
icon={<BiUpvote size='1.25rem' className='icon-primary' />}
icon={<IconMoveUp size='1.25rem' className='icon-primary' />}
disabled={disabled || modified}
onClick={onMoveUp}
/>
<MiniButton
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
icon={<BiDownvote size='1.25rem' className='icon-primary' />}
icon={<IconMoveDown size='1.25rem' className='icon-primary' />}
disabled={disabled || modified}
onClick={onMoveDown}
/>

View File

@ -1,6 +1,6 @@
import clsx from 'clsx';
import { LiaEdit } from 'react-icons/lia';
import { IconEdit } from '@/components/Icons';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import { IConstituenta } from '@/models/rsform';
@ -26,7 +26,7 @@ function ControlsOverlay({ constituenta, disabled, modified, processing, onRenam
}
noHover
onClick={onEditTerm}
icon={<LiaEdit size='1rem' className='icon-primary' />}
icon={<IconEdit size='1rem' className='icon-primary' />}
disabled={modified}
/>
) : null}
@ -46,7 +46,7 @@ function ControlsOverlay({ constituenta, disabled, modified, processing, onRenam
noHover
title={modified ? messages.unsaved : 'Переименовать конституенту'}
onClick={onRename}
icon={<LiaEdit size='1rem' className='icon-primary' />}
icon={<IconEdit size='1rem' className='icon-primary' />}
disabled={modified}
/>
) : null}

View File

@ -3,9 +3,9 @@
import clsx from 'clsx';
import { AnimatePresence } from 'framer-motion';
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
import { FiSave } from 'react-icons/fi';
import { toast } from 'react-toastify';
import { IconSave } from '@/components/Icons';
import RefsInput from '@/components/RefsInput';
import SubmitButton from '@/components/ui/SubmitButton';
import TextArea from '@/components/ui/TextArea';
@ -237,7 +237,7 @@ function FormConstituenta({
text='Сохранить изменения'
className='self-center'
disabled={disabled || !isModified}
icon={<FiSave size='1.25rem' />}
icon={<IconSave size='1.25rem' />}
/>
) : null}
</AnimatePresence>

View File

@ -3,11 +3,10 @@
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
import { AnimatePresence } from 'framer-motion';
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
import { BiFontFamily, BiListUl } from 'react-icons/bi';
import { FaRegKeyboard } from 'react-icons/fa6';
import { RiNodeTree } from 'react-icons/ri';
import { toast } from 'react-toastify';
import { IconList, IconText, IconTree } from '@/components/Icons';
import BadgeHelp from '@/components/man/BadgeHelp';
import RSInput from '@/components/RSInput';
import { RSTextWrapper } from '@/components/RSInput/textEditing';
@ -165,7 +164,7 @@ function EditorRSExpression({
<MiniButton
title='Изменить шрифт'
onClick={toggleFont}
icon={<BiFontFamily size='1.25rem' className={mathFont === 'math' ? 'icon-primary' : ''} />}
icon={<IconText size='1.25rem' className={mathFont === 'math' ? 'icon-primary' : ''} />}
/>
{!disabled || model.processing ? (
<MiniButton
@ -177,12 +176,12 @@ function EditorRSExpression({
<MiniButton
title='Отображение списка конституент'
onClick={onToggleList}
icon={<BiListUl size='1.25rem' className={showList ? 'icon-primary' : ''} />}
icon={<IconList size='1.25rem' className={showList ? 'icon-primary' : ''} />}
/>
<MiniButton
title='Дерево разбора выражения'
onClick={handleShowAST}
icon={<RiNodeTree size='1.25rem' className='icon-primary' />}
icon={<IconTree size='1.25rem' className='icon-primary' />}
/>
</Overlay>

View File

@ -2,10 +2,9 @@
import clsx from 'clsx';
import { useEffect, useLayoutEffect, useState } from 'react';
import { FiSave } from 'react-icons/fi';
import { LuGitBranchPlus, LuPencilLine } from 'react-icons/lu';
import { toast } from 'react-toastify';
import { IconList, IconNewItem, IconSave } from '@/components/Icons';
import BadgeHelp from '@/components/man/BadgeHelp';
import VersionSelector from '@/components/select/VersionSelector';
import Checkbox from '@/components/ui/Checkbox';
@ -124,13 +123,13 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
title={controller.isContentEditable ? 'Создать версию' : 'Переключитесь на актуальную версию'}
disabled={!controller.isContentEditable}
onClick={controller.createVersion}
icon={<LuGitBranchPlus size='1.25rem' className='icon-green' />}
icon={<IconNewItem size='1.25rem' className='icon-green' />}
/>
<MiniButton
title={schema?.versions.length === 0 ? 'Список версий пуст' : 'Редактировать версии'}
disabled={!schema || schema?.versions.length === 0}
onClick={controller.editVersions}
icon={<LuPencilLine size='1.25rem' className='icon-primary' />}
icon={<IconList size='1.25rem' className='icon-primary' />}
/>
</>
) : null}
@ -177,7 +176,7 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
className='self-center'
loading={processing}
disabled={!isModified}
icon={<FiSave size='1.25rem' />}
icon={<IconSave size='1.25rem' />}
/>
) : null}
</form>

View File

@ -1,10 +1,16 @@
'use client';
import { useMemo } from 'react';
import { BiDownload, BiShareAlt, BiTrash } from 'react-icons/bi';
import { FiBell, FiBellOff, FiSave } from 'react-icons/fi';
import { LuCrown } from 'react-icons/lu';
import {
IconDestroy,
IconDownload,
IconFollow,
IconFollowOff,
IconOwner,
IconSave,
IconShare
} from '@/components/Icons';
import BadgeHelp from '@/components/man/BadgeHelp';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
@ -31,18 +37,18 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
<MiniButton
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
disabled={!canSave}
icon={<FiSave size='1.25rem' className='icon-primary' />}
icon={<IconSave size='1.25rem' className='icon-primary' />}
onClick={onSubmit}
/>
) : null}
<MiniButton
title='Поделиться схемой'
icon={<BiShareAlt size='1.25rem' className='icon-primary' />}
icon={<IconShare size='1.25rem' className='icon-primary' />}
onClick={controller.share}
/>
<MiniButton
title='Скачать TRS файл'
icon={<BiDownload size='1.25rem' className='icon-primary' />}
icon={<IconDownload size='1.25rem' className='icon-primary' />}
onClick={controller.download}
/>
{!anonymous ? (
@ -50,9 +56,9 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
titleHtml={`Отслеживание <b>${subscribed ? 'включено' : 'выключено'}</b>`}
icon={
subscribed ? (
<FiBell size='1.25rem' className='icon-primary' />
<IconFollow size='1.25rem' className='icon-primary' />
) : (
<FiBellOff size='1.25rem' className='clr-text-controls' />
<IconFollowOff size='1.25rem' className='clr-text-controls' />
)
}
disabled={controller.isProcessing}
@ -62,7 +68,7 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
{!anonymous && claimable ? (
<MiniButton
title='Стать владельцем'
icon={<LuCrown size='1.25rem' className='icon-green' />}
icon={<IconOwner size='1.25rem' className='icon-green' />}
disabled={controller.isProcessing}
onClick={controller.claim}
/>
@ -70,7 +76,7 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
{controller.isMutable ? (
<MiniButton
title='Удалить схему'
icon={<BiTrash size='1.25rem' className='icon-red' />}
icon={<IconDestroy size='1.25rem' className='icon-red' />}
disabled={!controller.isContentEditable || controller.isProcessing}
onClick={onDestroy}
/>

View File

@ -1,5 +1,6 @@
import { BiDownArrowCircle, BiDownvote, BiDuplicate, BiPlusCircle, BiTrash, BiUpvote } from 'react-icons/bi';
import { BiDownArrowCircle } from 'react-icons/bi';
import { IconClone, IconDestroy, IconMoveDown, IconMoveUp, IconNewItem } from '@/components/Icons';
import BadgeHelp from '@/components/man/BadgeHelp';
import Dropdown from '@/components/ui/Dropdown';
import DropdownButton from '@/components/ui/DropdownButton';
@ -22,25 +23,25 @@ function RSListToolbar() {
<Overlay position='top-1 right-1/2 translate-x-1/2' className='items-start cc-icons'>
<MiniButton
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
icon={<BiUpvote size='1.25rem' className='icon-primary' />}
icon={<IconMoveUp size='1.25rem' className='icon-primary' />}
disabled={controller.isProcessing || controller.nothingSelected}
onClick={controller.moveUp}
/>
<MiniButton
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
icon={<BiDownvote size='1.25rem' className='icon-primary' />}
icon={<IconMoveDown size='1.25rem' className='icon-primary' />}
disabled={controller.isProcessing || controller.nothingSelected}
onClick={controller.moveDown}
/>
<MiniButton
titleHtml={prepareTooltip('Клонировать конституенту', 'Alt + V')}
icon={<BiDuplicate size='1.25rem' className='icon-green' />}
icon={<IconClone size='1.25rem' className='icon-green' />}
disabled={controller.isProcessing || controller.selected.length !== 1}
onClick={controller.cloneCst}
/>
<MiniButton
titleHtml={prepareTooltip('Добавить новую конституенту...', 'Alt + `')}
icon={<BiPlusCircle size='1.25rem' className='icon-green' />}
icon={<IconNewItem size='1.25rem' className='icon-green' />}
disabled={controller.isProcessing}
onClick={() => controller.createCst(undefined, false)}
/>
@ -65,7 +66,7 @@ function RSListToolbar() {
</div>
<MiniButton
titleHtml={prepareTooltip('Удалить выбранные', 'Delete')}
icon={<BiTrash size='1.25rem' className='icon-red' />}
icon={<IconDestroy size='1.25rem' className='icon-red' />}
disabled={controller.isProcessing || controller.nothingSelected}
onClick={controller.deleteCst}
/>

View File

@ -1,18 +1,20 @@
'use client';
import {
BiFilterAlt,
BiFont,
BiFontFamily,
BiGitBranch,
BiGitMerge,
BiPlanet,
BiPlusCircle,
BiReset,
BiTrash
} from 'react-icons/bi';
import { LuExpand, LuImage, LuMaximize, LuMinimize } from 'react-icons/lu';
IconDestroy,
IconFilter,
IconFitImage,
IconGraphClosure,
IconGraphExpand,
IconGraphInputs,
IconGraphMaximize,
IconGraphOutputs,
IconNewItem,
IconReset,
IconRotate3D,
IconText,
IconTextOff
} from '@/components/Icons';
import BadgeHelp from '@/components/man/BadgeHelp';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
@ -56,27 +58,27 @@ function GraphToolbar({
<div className='cc-icons'>
<MiniButton
title='Настройки фильтрации узлов и связей'
icon={<BiFilterAlt size='1.25rem' className='icon-primary' />}
icon={<IconFilter size='1.25rem' className='icon-primary' />}
onClick={showParamsDialog}
/>
<MiniButton
title={!noText ? 'Скрыть текст' : 'Отобразить текст'}
icon={
!noText ? (
<BiFontFamily size='1.25rem' className='icon-green' />
<IconText size='1.25rem' className='icon-green' />
) : (
<BiFont size='1.25rem' className='icon-primary' />
<IconTextOff size='1.25rem' className='icon-primary' />
)
}
onClick={toggleNoText}
/>
<MiniButton
icon={<LuImage size='1.25rem' className='icon-primary' />}
title='Восстановить камеру'
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
title='Граф целиком'
onClick={onResetViewpoint}
/>
<MiniButton
icon={<BiPlanet size='1.25rem' className={orbit ? 'icon-green' : 'icon-primary'} />}
icon={<IconRotate3D size='1.25rem' className={orbit ? 'icon-green' : 'icon-primary'} />}
title='Анимация вращения'
disabled={!is3D}
onClick={toggleOrbit}
@ -84,7 +86,7 @@ function GraphToolbar({
{controller.isContentEditable ? (
<MiniButton
title='Новая конституента'
icon={<BiPlusCircle size='1.25rem' className='icon-green' />}
icon={<IconNewItem size='1.25rem' className='icon-green' />}
disabled={controller.isProcessing}
onClick={onCreate}
/>
@ -92,7 +94,7 @@ function GraphToolbar({
{controller.isContentEditable ? (
<MiniButton
title='Удалить выбранные'
icon={<BiTrash size='1.25rem' className='icon-red' />}
icon={<IconDestroy size='1.25rem' className='icon-red' />}
disabled={controller.nothingSelected || controller.isProcessing}
onClick={onDelete}
/>
@ -102,36 +104,36 @@ function GraphToolbar({
<div className='cc-icons'>
<MiniButton
titleHtml='<b>[ESC]</b><br/>Сбросить выделение'
icon={<BiReset size='1.25rem' className='icon-primary' />}
icon={<IconReset size='1.25rem' className='icon-primary' />}
onClick={controller.deselectAll}
/>
<MiniButton
titleHtml='<b>Замыкание</b> - дополнение выделения влияющими конституентами'
icon={<LuMinimize size='1.25rem' className='icon-primary' />}
icon={<IconGraphClosure size='1.25rem' className='icon-primary' />}
disabled={controller.nothingSelected}
onClick={controller.selectAllInputs}
/>
<MiniButton
titleHtml='<b>Максимизация</b> - дополнение выделения конституентами, зависимыми только от выделенных'
icon={<LuMaximize size='1.25rem' className='icon-primary' />}
icon={<IconGraphMaximize size='1.25rem' className='icon-primary' />}
disabled={controller.nothingSelected}
onClick={controller.selectMax}
/>
<MiniButton
titleHtml='Выделить все зависимые'
icon={<LuExpand size='1.25rem' className='icon-primary' />}
icon={<IconGraphExpand size='1.25rem' className='icon-primary' />}
disabled={controller.nothingSelected}
onClick={controller.selectAllOutputs}
/>
<MiniButton
titleHtml='Выделить поставщиков'
icon={<BiGitBranch size='1.25rem' className='icon-primary' />}
icon={<IconGraphInputs size='1.25rem' className='icon-primary' />}
disabled={controller.nothingSelected}
onClick={controller.selectInputs}
/>
<MiniButton
titleHtml='Выделить потребителей'
icon={<BiGitMerge size='1.25rem' className='icon-primary' />}
icon={<IconGraphOutputs size='1.25rem' className='icon-primary' />}
disabled={controller.nothingSelected}
onClick={controller.selectOutputs}
/>

View File

@ -1,30 +1,23 @@
'use client';
import {
BiDiamond,
BiDownload,
BiDuplicate,
BiMenu,
BiMeteor,
BiPlusCircle,
BiShareAlt,
BiTrash,
BiUpload
} from 'react-icons/bi';
import { BiDiamond, BiMenu } from 'react-icons/bi';
import { FiEdit } from 'react-icons/fi';
import {
LuAlertTriangle,
LuArchive,
LuBookCopy,
LuCrown,
LuGlasses,
LuNetwork,
LuReplace,
LuWand2
} from 'react-icons/lu';
import { LuAlertTriangle, LuArchive, LuBookCopy, LuNetwork, LuWand2 } from 'react-icons/lu';
import { VscLibrary } from 'react-icons/vsc';
import { urls } from '@/app/urls';
import {
IconAdmin,
IconClone,
IconDestroy,
IconDownload,
IconNewItem,
IconOwner,
IconReader,
IconReplace,
IconShare,
IconUpload
} from '@/components/Icons';
import Button from '@/components/ui/Button';
import Dropdown from '@/components/ui/Dropdown';
import DropdownButton from '@/components/ui/DropdownButton';
@ -140,33 +133,33 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
{user ? (
<DropdownButton
text={model.isOwned ? 'Вы — владелец' : 'Стать владельцем'}
icon={<LuCrown size='1rem' className='icon-green' />}
icon={<IconOwner size='1rem' className='icon-green' />}
disabled={!model.isClaimable && !model.isOwned}
onClick={!model.isOwned && model.isClaimable ? handleClaimOwner : undefined}
/>
) : null}
<DropdownButton
text='Поделиться'
icon={<BiShareAlt size='1rem' className='icon-primary' />}
icon={<IconShare size='1rem' className='icon-primary' />}
onClick={handleShare}
/>
{user ? (
<DropdownButton
text='Клонировать'
icon={<BiDuplicate size='1rem' className='icon-primary' />}
icon={<IconClone size='1rem' className='icon-primary' />}
disabled={model.isArchive}
onClick={handleClone}
/>
) : null}
<DropdownButton
text='Выгрузить в Экстеор'
icon={<BiDownload size='1rem' className='icon-primary' />}
icon={<IconDownload size='1rem' className='icon-primary' />}
onClick={handleDownload}
/>
{controller.isContentEditable ? (
<DropdownButton
text='Загрузить из Экстеора'
icon={<BiUpload size='1rem' className='icon-red' />}
icon={<IconUpload size='1rem' className='icon-red' />}
disabled={controller.isProcessing}
onClick={handleUpload}
/>
@ -174,7 +167,7 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
{controller.isMutable ? (
<DropdownButton
text='Удалить схему'
icon={<BiTrash size='1rem' className='icon-red' />}
icon={<IconDestroy size='1rem' className='icon-red' />}
disabled={controller.isProcessing}
onClick={handleDelete}
/>
@ -183,7 +176,7 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
<DropdownButton
className='border-t-2'
text='Создать новую схему'
icon={<BiPlusCircle size='1rem' className='icon-primary' />}
icon={<IconNewItem size='1rem' className='icon-primary' />}
onClick={handleCreateNew}
/>
) : null}
@ -241,7 +234,7 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
<DropdownButton
text='Отождествление'
title='Заменить вхождения одной конституенты на другую'
icon={<LuReplace size='1rem' className='icon-red' />}
icon={<IconReplace size='1rem' className='icon-red' />}
onClick={handleSubstituteCst}
disabled={!controller.isContentEditable || controller.isProcessing}
/>
@ -274,11 +267,11 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
className='h-full pr-2'
icon={
mode === UserAccessMode.ADMIN ? (
<BiMeteor size='1.25rem' className='icon-primary' />
<IconAdmin size='1.25rem' className='icon-primary' />
) : mode === UserAccessMode.OWNER ? (
<LuCrown size='1.25rem' className='icon-primary' />
<IconOwner size='1.25rem' className='icon-primary' />
) : (
<LuGlasses size='1.25rem' className='icon-primary' />
<IconReader size='1.25rem' className='icon-primary' />
)
}
onClick={accessMenu.toggle}
@ -287,20 +280,20 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
<DropdownButton
text={labelAccessMode(UserAccessMode.READER)}
title={describeAccessMode(UserAccessMode.READER)}
icon={<LuGlasses size='1rem' className='icon-primary' />}
icon={<IconReader size='1rem' className='icon-primary' />}
onClick={() => handleChangeMode(UserAccessMode.READER)}
/>
<DropdownButton
text={labelAccessMode(UserAccessMode.OWNER)}
title={describeAccessMode(UserAccessMode.OWNER)}
icon={<LuCrown size='1rem' className='icon-primary' />}
icon={<IconOwner size='1rem' className='icon-primary' />}
disabled={!model.isOwned}
onClick={() => handleChangeMode(UserAccessMode.OWNER)}
/>
<DropdownButton
text={labelAccessMode(UserAccessMode.ADMIN)}
title={describeAccessMode(UserAccessMode.ADMIN)}
icon={<BiMeteor size='1rem' className='icon-primary' />}
icon={<IconAdmin size='1rem' className='icon-primary' />}
disabled={!user?.is_staff}
onClick={() => handleChangeMode(UserAccessMode.ADMIN)}
/>

View File

@ -1,8 +1,8 @@
'use client';
import { useCallback, useLayoutEffect, useState } from 'react';
import { BiCog, BiFilterAlt } from 'react-icons/bi';
import { IconFilter, IconSettings } from '@/components/Icons';
import Dropdown from '@/components/ui/Dropdown';
import DropdownButton from '@/components/ui/DropdownButton';
import SearchBar from '@/components/ui/SearchBar';
@ -90,7 +90,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
title='Настройка атрибутов для фильтрации'
hideTitle={matchModeMenu.isOpen}
className='h-full'
icon={<BiFilterAlt size='1.25rem' />}
icon={<IconFilter size='1.25rem' />}
text={labelCstMatchMode(filterMatch)}
onClick={matchModeMenu.toggle}
/>
@ -121,7 +121,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
title='Настройка фильтрации по графу термов'
hideTitle={sourceMenu.isOpen}
className='h-full pr-2'
icon={<BiCog size='1.25rem' />}
icon={<IconSettings size='1.25rem' />}
text={labelCstSource(filterSource)}
onClick={sourceMenu.toggle}
/>

View File

@ -2,10 +2,10 @@
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { BiInfoCircle } from 'react-icons/bi';
import { toast } from 'react-toastify';
import { urls } from '@/app/urls';
import { IconHelp } from '@/components/Icons';
import InfoError from '@/components/info/InfoError';
import Button from '@/components/ui/Button';
import Checkbox from '@/components/ui/Checkbox';
@ -80,7 +80,7 @@ function RegisterPage() {
<FlexColumn>
<div className='absolute'>
<Overlay id={globals.password_tooltip} position='top-[4.8rem] left-[3.4rem] absolute'>
<BiInfoCircle size='1.25rem' className='icon-primary' />
<IconHelp size='1.25rem' className='icon-primary' />
</Overlay>
<Tooltip anchorSelect={`#${globals.password_tooltip}`} offset={6}>
<p>- используйте уникальный пароль</p>

View File

@ -2,8 +2,8 @@
import { AnimatePresence } from 'framer-motion';
import { useMemo, useState } from 'react';
import { FiBell, FiBellOff } from 'react-icons/fi';
import { IconFollow, IconFollowOff } from '@/components/Icons';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import AnimateFade from '@/components/wrap/AnimateFade';
@ -41,9 +41,9 @@ function UserTabs() {
title='Отслеживаемые схемы'
icon={
showSubs ? (
<FiBell size='1.25rem' className='icon-primary' />
<IconFollow size='1.25rem' className='icon-primary' />
) : (
<FiBellOff size='1.25rem' className='icon-primary' />
<IconFollowOff size='1.25rem' className='icon-primary' />
)
}
onClick={() => setShowSubs(prev => !prev)}